/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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 General Public License for more details.
*
*
* Copyright 2006 - 2016 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.platform.web.http.api.resources.services;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.channels.IllegalSelectorException;
import java.security.GeneralSecurityException;
import java.security.InvalidParameterException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import javax.ws.rs.core.StreamingOutput;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Level;
import org.pentaho.platform.api.engine.IAuthorizationPolicy;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.PentahoAccessControlException;
import org.pentaho.platform.api.repository2.unified.IRepositoryFileData;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.api.repository2.unified.RepositoryFileAcl;
import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission;
import org.pentaho.platform.api.repository2.unified.RepositoryRequest;
import org.pentaho.platform.api.repository2.unified.UnifiedRepositoryAccessDeniedException;
import org.pentaho.platform.api.repository2.unified.data.simple.SimpleRepositoryFileData;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.plugin.services.exporter.PentahoPlatformExporter;
import org.pentaho.platform.plugin.services.importer.IPlatformImporter;
import org.pentaho.platform.plugin.services.importer.PlatformImportException;
import org.pentaho.platform.plugin.services.importer.RepositoryFileImportBundle;
import org.pentaho.platform.plugin.services.importexport.BaseExportProcessor;
import org.pentaho.platform.plugin.services.importexport.DefaultExportHandler;
import org.pentaho.platform.plugin.services.importexport.ExportException;
import org.pentaho.platform.plugin.services.importexport.ExportHandler;
import org.pentaho.platform.plugin.services.importexport.IRepositoryImportLogger;
import org.pentaho.platform.plugin.services.importexport.SimpleExportProcessor;
import org.pentaho.platform.plugin.services.importexport.ZipExportProcessor;
import org.pentaho.platform.repository.RepositoryDownloadWhitelist;
import org.pentaho.platform.repository2.ClientRepositoryPaths;
import org.pentaho.platform.repository2.locale.PentahoLocale;
import org.pentaho.platform.repository2.unified.ServerRepositoryPaths;
import org.pentaho.platform.repository2.unified.fileio.RepositoryFileInputStream;
import org.pentaho.platform.repository2.unified.fileio.RepositoryFileOutputStream;
import org.pentaho.platform.repository2.unified.jcr.JcrTenantUtils;
import org.pentaho.platform.repository2.unified.jcr.PentahoJcrConstants;
import org.pentaho.platform.repository2.unified.webservices.DefaultUnifiedRepositoryWebService;
import org.pentaho.platform.repository2.unified.webservices.LocaleMapDto;
import org.pentaho.platform.repository2.unified.webservices.PropertiesWrapper;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileAclAceDto;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileAclDto;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileAdapter;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileDto;
import org.pentaho.platform.repository2.unified.webservices.RepositoryFileTreeDto;
import org.pentaho.platform.repository2.unified.webservices.StringKeyStringValueDto;
import org.pentaho.platform.security.policy.rolebased.actions.AdministerSecurityAction;
import org.pentaho.platform.security.policy.rolebased.actions.PublishAction;
import org.pentaho.platform.security.policy.rolebased.actions.RepositoryCreateAction;
import org.pentaho.platform.security.policy.rolebased.actions.RepositoryReadAction;
import org.pentaho.platform.web.http.api.resources.SessionResource;
import org.pentaho.platform.web.http.api.resources.Setting;
import org.pentaho.platform.web.http.api.resources.StringListWrapper;
import org.pentaho.platform.web.http.api.resources.operations.CopyFilesOperation;
import org.pentaho.platform.web.http.api.resources.utils.FileUtils;
import org.pentaho.platform.web.http.api.resources.utils.RepositoryFileHelper;
import org.pentaho.platform.web.http.messages.Messages;
public class FileService {
public static final Integer MODE_OVERWRITE = 1;
public static final Integer MODE_RENAME = 2;
public static final Integer MODE_NO_OVERWRITE = 3;
private static final Log logger = LogFactory.getLog( FileService.class );
protected IAuthorizationPolicy policy;
protected DefaultUnifiedRepositoryWebService defaultUnifiedRepositoryWebService;
protected IUnifiedRepository repository;
protected RepositoryDownloadWhitelist whitelist;
protected SessionResource sessionResource;
private PentahoPlatformExporter backupExporter;
public DownloadFileWrapper systemBackup( String userAgent ) throws IOException, ExportException {
if ( doCanAdminister() ) {
String originalFileName, quotedFileName, encodedFileName;
originalFileName = "SystemBackup.zip";
encodedFileName = makeEncodedFileName( originalFileName );
quotedFileName = makeQuotedFileName( originalFileName );
StreamingOutput streamingOutput = getBackupStream();
final String attachment = makeAttachment( userAgent, encodedFileName, quotedFileName );
return new DownloadFileWrapper( streamingOutput, attachment, encodedFileName );
} else {
throw new SecurityException();
}
}
public void systemRestore( final InputStream fileUpload, String overwriteFile ) throws PlatformImportException, SecurityException {
if ( doCanAdminister() ) {
boolean overwriteFileFlag = ( "false".equals( overwriteFile ) ? false : true );
IRepositoryImportLogger importLogger = null;
Level level = Level.ERROR;
boolean logJobStarted = false;
ByteArrayOutputStream importLoggerStream = new ByteArrayOutputStream();
String importDirectory = "/";
RepositoryFileImportBundle.Builder bundleBuilder = new RepositoryFileImportBundle.Builder();
bundleBuilder.input( fileUpload );
bundleBuilder.charSet( "UTF-8" );
bundleBuilder.hidden( RepositoryFile.HIDDEN_BY_DEFAULT );
bundleBuilder.schedulable( RepositoryFile.SCHEDULABLE_BY_DEFAULT );
bundleBuilder.path( importDirectory );
bundleBuilder.overwriteFile( overwriteFileFlag );
bundleBuilder.name( "SystemBackup.zip" );
bundleBuilder.applyAclSettings( true );
bundleBuilder.overwriteAclSettings( false );
bundleBuilder.retainOwnership( true );
bundleBuilder.preserveDsw( true );
IPlatformImporter importer = PentahoSystem.get( IPlatformImporter.class );
importLogger = importer.getRepositoryImportLogger();
logJobStarted = true;
importLogger.startJob( importLoggerStream, importDirectory, level );
try {
importer.importFile( bundleBuilder.build() );
} finally {
if ( logJobStarted == true ) {
importLogger.endJob();
}
}
} else {
throw new SecurityException();
}
}
private StreamingOutput getBackupStream() throws IOException, ExportException {
File zipFile = getBackupExporter().performExport();
final FileInputStream inputStream = new FileInputStream( zipFile );
return new StreamingOutput() {
@Override
public void write( OutputStream output ) throws IOException {
IOUtils.copy( inputStream, output );
}
};
}
/**
* Moves the list of files to the user's trash folder
* <p/>
* Move a list of files to the user's trash folder, the list should be comma separated.
*
* @param params Comma separated list of the files to be deleted
* @throws Exception containing the string, "SystemResource.GENERAL_ERROR"
*/
public void doDeleteFiles( String params ) throws Exception {
String[] sourceFileIds = FileUtils.convertCommaSeparatedStringToArray( params );
try {
for ( int i = 0; i < sourceFileIds.length; i++ ) {
getRepoWs().deleteFile( sourceFileIds[i], null );
}
} catch ( Exception e ) {
throw e;
}
}
/**
* Permanently deletes the selected list of files from the repository
* <p/>
* Permanently deletes a comma separated list of files without sending them to the trash folder
*
* @param params Comma separated list of the files to be deleted
* @return Exception containing the string, "SystemResource.GENERAL_ERROR"
*/
public void doDeleteFilesPermanent( String params ) throws Exception {
String[] sourceFileIds = FileUtils.convertCommaSeparatedStringToArray( params ); //$NON-NLS-1$
try {
for ( int i = 0; i < sourceFileIds.length; i++ ) {
getRepoWs().deleteFileWithPermanentFlag( sourceFileIds[ i ], true, null );
}
} catch ( Exception e ) {
logger.error( Messages.getInstance().getString( "SystemResource.GENERAL_ERROR" ), e );
throw e;
}
}
/**
* Delete the locale for the selected file and locale
*
* @param pathId Colon separated path for the repository file
* @param locale The locale to be deleted
* @throws Exception containing the string, "SystemResource.GENERAL_ERROR"
*/
public void doDeleteLocale( String pathId, String locale ) throws Exception {
try {
RepositoryFileDto file = getRepoWs().getFile( idToPath( pathId ) );
if ( file != null ) {
getRepoWs().deleteLocalePropertiesForFile( file.getId(), locale );
}
} catch ( Exception e ) {
logger.error( Messages.getInstance().getString( "SystemResource.GENERAL_ERROR" ), e );
throw e;
}
}
public List<Setting> doGetCanAccessList( String pathId, String permissions ) {
StringTokenizer tokenizer = new StringTokenizer( permissions, "|" );
ArrayList<Setting> permMap = new ArrayList<Setting>();
while ( tokenizer.hasMoreTokens() ) {
Integer perm = Integer.valueOf( tokenizer.nextToken() );
EnumSet<RepositoryFilePermission> permission = EnumSet.of( RepositoryFilePermission.values()[perm] );
permMap.add( new Setting( perm.toString(), new Boolean( getRepository()
.hasAccess( idToPath( pathId ), permission ) ).toString() ) );
}
return permMap;
}
public List<Setting> doGetPathsAccessList( StringListWrapper pathsWrapper ) {
List<Setting> pathsPermissonsSettings = new ArrayList<Setting>();
String permissions =
RepositoryFilePermission.READ.ordinal() + "|" + RepositoryFilePermission.WRITE.ordinal() + "|"
+ RepositoryFilePermission.DELETE.ordinal() + "|" + RepositoryFilePermission.ACL_MANAGEMENT.ordinal() + "|"
+ RepositoryFilePermission.ALL.ordinal();
List<String> paths = pathsWrapper.getStrings();
for ( String path : paths ) {
List<Setting> permList = doGetCanAccessList( path, permissions );
for ( Setting perm : permList ) {
if ( Boolean.parseBoolean( perm.getValue() ) ) {
Setting setting = new Setting();
setting.setName( path );
setting.setValue( perm.getName() );
pathsPermissonsSettings.add( setting );
}
}
}
return pathsPermissonsSettings;
}
/**
* Creates a new file with the provided contents at a given path
*
* @param pathId (colon separated path for the repository file)
* @param fileContents (content of the file)
* @return
* @throws IOException
*/
public void createFile( String charsetName, String pathId, InputStream fileContents )
throws Exception {
try {
String idToPath = idToPath( pathId );
RepositoryFileOutputStream rfos = getRepositoryFileOutputStream( idToPath );
rfos.setCharsetName( charsetName );
rfos.setAutoCreateDirStructure( true );
copy( fileContents, rfos );
rfos.close();
fileContents.close();
} catch ( Exception e ) {
logger.error( Messages.getInstance().getString( "SystemResource.GENERAL_ERROR" ), e );
throw e;
}
}
/**
* Moves a list of files from its current location to another.
* <p/>
* Moves a list of files from its current location to another, the list should be comma separated.
*
* @param destPathId colon separated path for the repository file
* <pre function="syntax.xml">
* :path:to:file:id
* </pre>
* @param params comma separated list of files to be moved
* <pre function="syntax.xml">
* path1,path2,...
* </pre>
*
* @return boolean <code>true</code> if all files were moved correctly or <code>false</code> if the destiny path is
* not available
* @throws FileNotFoundException
*/
public void doMoveFiles( String destPathId, String params ) throws FileNotFoundException {
String idToPath = idToPath( destPathId );
RepositoryFileDto repositoryFileDto = getRepoWs().getFile( idToPath );
if ( repositoryFileDto == null ) {
throw new FileNotFoundException( idToPath );
}
String[] sourceFileIds = FileUtils.convertCommaSeparatedStringToArray( params );
int i = 0;
try {
for ( ; i < sourceFileIds.length; i++ ) {
getRepoWs().moveFile( sourceFileIds[ i ], repositoryFileDto.getPath(), null );
}
} catch ( IllegalArgumentException | UnifiedRepositoryAccessDeniedException e ) {
throw e;
} catch ( Exception e ) {
throw new InternalError();
}
}
/**
* Restores a list of files from the user's trash folder
* <p/>
* Restores a list of files from the user's trash folder to their previous locations. The list should be comma
* separated.
*
* @param params Comma separated list of files to be restored
* @throws Exception containing the string, "SystemResource.GENERAL_ERROR"
*/
public void doRestoreFiles( String params ) throws InternalError {
String[] sourceFileIds = FileUtils.convertCommaSeparatedStringToArray( params );
try {
for ( int i = 0; i < sourceFileIds.length; i++ ) {
getRepoWs().undeleteFile( sourceFileIds[ i ], null );
}
} catch ( Exception e ) {
if ( e instanceof UnifiedRepositoryAccessDeniedException ) {
throw (UnifiedRepositoryAccessDeniedException) e;
}
logger.error( Messages.getInstance().getString( "SystemResource.FILE_RESTORE_FAILED" ), e );
throw new InternalError();
}
}
/**
*
* Restores a list of files from the trash folder to user's home folder,
* ignoring files previous locations (with no change of file owner)
* @param params Comma separated list of files to be restored
* @param overwriteMode Default is RENAME (2) which adds a number to the end of the file name. MODE_OVERWRITE (1)
* will just replace existing or MODE_NO_OVERWRITE (3) will not copy if file exist.
*
*/
public boolean doRestoreFilesInHomeDir( String params, int overwriteMode ) {
if ( overwriteMode < 1 || overwriteMode > 3 ) {
overwriteMode = MODE_RENAME;
}
String userHomeFolderPath =
ClientRepositoryPaths.getUserHomeFolderPath( getSession().getName() );
String filesToDeletePermanent = null;
if ( overwriteMode == MODE_RENAME ) {
doCopyFiles( userHomeFolderPath, overwriteMode, params );
filesToDeletePermanent = params;
} else if ( overwriteMode == MODE_NO_OVERWRITE ) {
// we can delete from trash only non-conflict files,
// because conflict files won't be restored
String nonConflictFileIds = getSourceFileIdsThatNotConflictWithFolderFiles( userHomeFolderPath, params );
if ( nonConflictFileIds.isEmpty() ) {
// all files are conflict, so nothing to restore in this mode
return true;
}
doCopyFiles( userHomeFolderPath, overwriteMode, nonConflictFileIds );
filesToDeletePermanent = nonConflictFileIds;
} else if ( overwriteMode == MODE_OVERWRITE ) {
String conflictFileIdsInHomeDir = getFolderFileIdsThatConflictWithSource( userHomeFolderPath, params );
if ( !conflictFileIdsInHomeDir.isEmpty() ) {
try {
doDeleteFilesPermanent( conflictFileIdsInHomeDir );
doMoveFiles( userHomeFolderPath, params );
} catch ( FileNotFoundException e ) {
logger.error( "File with id: " + e.getMessage() + " is not found!" );
return false;
} catch ( Exception e ) {
logger.warn( "Files with ids: " + params + " were restored, but not deleted" );
return false;
}
} else {
try {
doMoveFiles( userHomeFolderPath, params );
} catch ( FileNotFoundException e ) {
logger.error( "File with id: " + e.getMessage() + " is not found!" );
return false;
}
}
}
if ( filesToDeletePermanent != null && !params.isEmpty() ) {
try {
doDeleteFilesPermanent( filesToDeletePermanent );
} catch ( Exception e ) {
logger.warn( "Files with ids: " + filesToDeletePermanent + " were restored, but not deleted" );
}
}
return true;
}
public String getFolderFileIdsThatConflictWithSource( String pathToFolder, String params ) {
if ( params == null ) {
throw new IllegalArgumentException( "parameters cannot be null" );
}
String[] sourceFileIds = FileUtils.convertCommaSeparatedStringToArray( params );
List<String> conflictFileIdsList = new ArrayList<>();
List<RepositoryFileDto> homeFolderFiles = doGetChildren( pathToFolder, null, false, true );
for ( RepositoryFileDto fileInHomeFolder : homeFolderFiles ) {
for ( String sourceFileId : sourceFileIds ) {
RepositoryFile fileToRestore = getRepository().getFileById( sourceFileId );
if ( fileToRestore.getName().equals( fileInHomeFolder.getName() ) ) {
conflictFileIdsList.add( fileInHomeFolder.getId() );
}
}
}
return getCommaSeparatedFileIds( conflictFileIdsList );
}
/**
* Conflict occurs if one of source files has the same
* name with any of folder files.
*
* @param params
* String with file ids, separated by comma
* @param pathToFolder
* path to folder
*
* @return String
* with file ids of not conflict files, separated by comma
*
*/
protected String getSourceFileIdsThatNotConflictWithFolderFiles( String pathToFolder, String params ) {
String[] sourceFileIds = FileUtils.convertCommaSeparatedStringToArray( params );
List<String> nonConflictFileIdsList = new ArrayList<>();
List<RepositoryFileDto> homeFolderFiles = doGetChildren( pathToFolder, null, true, true );
for ( String sourceFileId : sourceFileIds ) {
boolean isConflict = false;
RepositoryFile fileToRestore = getRepository().getFileById( sourceFileId );
if ( fileToRestore == null ) {
logger.error( "Could not get file with id: " + sourceFileId );
continue;
}
for ( RepositoryFileDto fileInHomeFolder : homeFolderFiles ) {
if ( fileToRestore.getName().equals( fileInHomeFolder.getName() ) ) {
isConflict = true;
break;
}
}
if ( !isConflict ) {
nonConflictFileIdsList.add( sourceFileId );
}
}
return getCommaSeparatedFileIds( nonConflictFileIdsList );
}
/**
*
* @param fileIdsList
* List with file ids.
* @return
* - String of file ids, separated by comma
* - Empty String if {@code fileIdList} is null or empty
*
*/
protected String getCommaSeparatedFileIds( List<String> fileIdsList ) {
if ( fileIdsList == null || fileIdsList.size() == 0 ) {
return StringUtils.EMPTY;
}
StringBuilder stringBuilder = new StringBuilder();
for ( String fileId : fileIdsList ) {
stringBuilder.append( fileId ).append( "," );
}
String fileIds = stringBuilder.toString();
// delete last ','
fileIds = fileIds.substring( 0, fileIds.length() - 1 );
return fileIds;
}
public class DownloadFileWrapper {
private StreamingOutput outputStream;
private String attachment;
private String encodedFileName;
public DownloadFileWrapper( StreamingOutput outputStream, String attachment, String encodedFileName ) {
super();
this.outputStream = outputStream;
this.attachment = attachment;
this.encodedFileName = encodedFileName;
}
public StreamingOutput getOutputStream() {
return outputStream;
}
public String getAttachment() {
return attachment;
}
public String getEncodedFileName() {
return encodedFileName;
}
}
public DownloadFileWrapper doGetFileOrDirAsDownload( String userAgent, String pathId, String strWithManifest )
throws Throwable {
// change file id to path
String path = idToPath( pathId );
validateAccess( path );
IAuthorizationPolicy policy = getPolicy();
String originalFileName, encodedFileName = null;
// if no path is sent, return bad request
if ( StringUtils.isEmpty( pathId ) ) {
throw new InvalidParameterException( pathId );
}
// check if path is valid
if ( !isPathValid( path ) ) {
throw new IllegalSelectorException();
}
// check if entity exists in repo
RepositoryFile repositoryFile = getRepository().getFile( path );
if ( repositoryFile == null ) {
// file does not exist or is not readable but we can't tell at this point
throw new FileNotFoundException( path );
}
// send zip with manifest by default
boolean withManifest = "false".equals( strWithManifest ) ? false : true;
boolean requiresZip = repositoryFile.isFolder() || withManifest;
BaseExportProcessor exportProcessor = getDownloadExportProcessor( path, requiresZip, withManifest );
originalFileName = requiresZip ? repositoryFile.getName() + ".zip" : repositoryFile.getName(); //$NON-NLS-1$//$NON-NLS-2$
encodedFileName = makeEncodedFileName( originalFileName );
String quotedFileName = makeQuotedFileName( originalFileName );
// add export handlers for each expected file type
exportProcessor.addExportHandler( getDownloadExportHandler() );
// copy streaming output
StreamingOutput streamingOutput = getDownloadStream( repositoryFile, exportProcessor );
final String attachment = makeAttachment( userAgent, encodedFileName, quotedFileName );
return new DownloadFileWrapper( streamingOutput, attachment, encodedFileName );
}
private String makeEncodedFileName( String originalFile ) throws UnsupportedEncodingException {
return URLEncoder.encode( originalFile, "UTF-8" ).replaceAll( "\\+", "%20" );
}
private String makeQuotedFileName( String OriginalFile ) {
return "\"" + OriginalFile + "\"";
}
private String makeAttachment( String userAgent, String encodedFileName, String quotedFileName ) {
final String attachment;
if ( userAgent.contains( "Firefox" ) ) {
// special content-disposition for firefox browser to support utf8-encoded symbols in filename
attachment = "attachment; filename*=UTF-8\'\'" + encodedFileName;
} else {
attachment = "attachment; filename=" + quotedFileName;
}
return attachment;
}
/**
* Retrieves the file from the repository as inline. This is mainly used for css or and dependent files for the html
* document
*
* @param pathId (colon separated path for the repository file)
* @return RepositoryFileToStreamWrapper
* @throws FileNotFoundException
*/
// have to accept anything for browsers to work
public RepositoryFileToStreamWrapper doGetFileAsInline( String pathId ) throws FileNotFoundException {
String path = null;
RepositoryFile repositoryFile = null;
// Check if the path is actually and ID
path = idToPath( pathId );
if ( isPath( path ) ) {
if ( !isPathValid( path ) ) {
throw new IllegalArgumentException();
}
repositoryFile = getRepository().getFile( path );
} else {
// Yes path provided is an ID
repositoryFile = getRepository().getFileById( pathId );
}
if ( repositoryFile == null ) {
// file does not exist or is not readable but we can't tell at this point
throw new FileNotFoundException();
}
// check whitelist acceptance of file (based on extension)
if ( !getWhitelist().accept( repositoryFile.getName() ) ) {
// if whitelist check fails, we can still inline if you have PublishAction, otherwise we're FORBIDDEN
if ( !getPolicy().isAllowed( PublishAction.NAME ) ) {
throw new IllegalArgumentException();
}
}
try {
SimpleRepositoryFileData fileData =
getRepository().getDataForRead( repositoryFile.getId(), SimpleRepositoryFileData.class );
final InputStream is = fileData.getInputStream();
// copy streaming output
StreamingOutput streamingOutput = getStreamingOutput( is );
RepositoryFileToStreamWrapper wrapper = new RepositoryFileToStreamWrapper();
wrapper.setOutputStream( streamingOutput );
wrapper.setRepositoryFile( repositoryFile );
return wrapper;
} catch ( Exception e ) {
logger.error( Messages.getInstance().getString(
"FileResource.EXPORT_FAILED", repositoryFile.getName() + " " + e.getMessage() ), e );
throw new InternalError();
}
}
/**
* Retrieve the list of locale properties for a given locale
*
* @param pathId (colon separated path for the repository file)
* @param locale
* @return
*/
public List<StringKeyStringValueDto> doGetLocaleProperties( String pathId, String locale ) {
RepositoryFileDto file = getRepoWs().getFile( idToPath( pathId ) );
List<StringKeyStringValueDto> keyValueList = new ArrayList<StringKeyStringValueDto>();
if ( file != null ) {
PropertiesWrapper propertiesWrapper = getRepoWs().getLocalePropertiesForFileById( file.getId(), locale );
if ( propertiesWrapper != null ) {
Properties properties = propertiesWrapper.getProperties();
if ( properties != null && !properties.isEmpty() ) {
for ( String key : properties.stringPropertyNames() ) {
keyValueList.add( getStringKeyStringValueDto( key, properties.getProperty( key ) ) );
}
}
}
}
return keyValueList;
}
/**
* Set the list of locale properties for a given locale
*
* @param pathId
* @param locale
* @param properties
*/
public void doSetLocaleProperties( String pathId, String locale, List<StringKeyStringValueDto> properties )
throws Exception {
RepositoryFileDto file = getRepoWs().getFile( idToPath( pathId ) );
Properties fileProperties = new Properties();
if ( properties != null && !properties.isEmpty() ) {
for ( StringKeyStringValueDto dto : properties ) {
fileProperties.put( dto.getKey(), dto.getValue() );
}
}
getRepoWs().setLocalePropertiesForFileByFileId( file.getId(), locale, fileProperties );
}
/**
* Copy files to a new location
*
* @param pathId
* @param mode
* @param params
*/
public void doCopyFiles( String pathId, Integer mode, String params ) {
if ( !getPolicy().isAllowed( RepositoryCreateAction.NAME ) ) {
throw new IllegalArgumentException();
}
if ( mode == null ) {
mode = MODE_RENAME;
}
String path = idToPath( pathId );
String[] sourceFileIds = FileUtils.convertCommaSeparatedStringToArray( params ); //$NON-NLS-1$
CopyFilesOperation copyFilesOperation =
new CopyFilesOperation( getRepository(), getRepoWs(), Arrays.asList( sourceFileIds ), path, mode );
copyFilesOperation.execute();
}
/**
* Takes a pathId and returns a response object with the output stream based on the file located at the pathID
*
* @param pathId pathId to the file
* @return Response object containing the file stream for the file located at the pathId, along with the mimetype,
* and file name.
* @throws FileNotFoundException, IllegalArgumentException
*/
public RepositoryFileToStreamWrapper doGetFileOrDir( String pathId ) throws FileNotFoundException {
String path = idToPath( pathId );
if ( !isPathValid( path ) ) {
throw new IllegalArgumentException();
}
RepositoryFile repoFile = getRepository().getFile( path );
if ( repoFile == null ) {
// file does not exist or is not readable but we can't tell at this point
throw new FileNotFoundException();
}
// check whitelist acceptance of file (based on extension)
if ( !getWhitelist().accept( repoFile.getName() ) ) {
// if whitelist check fails, we can still inline if you have PublishAction, otherwise we're FORBIDDEN
if ( !getPolicy().isAllowed( PublishAction.NAME ) ) {
throw new IllegalArgumentException();
}
}
final RepositoryFileInputStream is = getRepositoryFileInputStream( repoFile );
StreamingOutput streamingOutput = getStreamingOutput( is );
RepositoryFileToStreamWrapper wrapper = new RepositoryFileToStreamWrapper();
wrapper.setOutputStream( streamingOutput );
wrapper.setRepositoryFile( repoFile );
wrapper.setMimetype( is.getMimeType() );
return wrapper;
}
/**
* Save the acls of the selected file to the repository
*
* This method is used to update and save the acls of the selected file to the repository
*
* @param pathId @param pathId colon separated path for the repository file
* <pre function="syntax.xml">
* :path:to:file:id
* </pre>
* @param acl Acl of the repository file <code> RepositoryFileAclDto </code>
* @throws FileNotFoundException
*/
public void setFileAcls( String pathId, RepositoryFileAclDto acl ) throws FileNotFoundException {
RepositoryFileDto file = getRepoWs().getFile( idToPath( pathId ) );
if ( file == null ) {
// file does not exist or is not readable but we can't tell at this point
throw new FileNotFoundException();
}
acl.setId( file.getId() );
// here we remove fake admin role added for display purpose only
List<RepositoryFileAclAceDto> aces = acl.getAces();
if ( aces != null ) {
Iterator<RepositoryFileAclAceDto> it = aces.iterator();
while ( it.hasNext() ) {
RepositoryFileAclAceDto ace = it.next();
if ( !ace.isModifiable() ) {
it.remove();
}
}
}
getRepoWs().updateAcl( acl );
}
/**
* Check whether the current user has specific permission on the selected repository file
*
* @param pathId
* @param permissions
* @return
*/
public String doGetCanAccess( String pathId, String permissions ) {
StringTokenizer tokenizer = new StringTokenizer( permissions, "|" );
List<Integer> permissionList = new ArrayList<Integer>();
while ( tokenizer.hasMoreTokens() ) {
Integer perm = Integer.valueOf( tokenizer.nextToken() );
switch ( perm ) {
case 0: {
permissionList.add( RepositoryFilePermission.READ.ordinal() );
break;
}
case 1: {
permissionList.add( RepositoryFilePermission.WRITE.ordinal() );
break;
}
case 2: {
permissionList.add( RepositoryFilePermission.DELETE.ordinal() );
break;
}
case 3: {
permissionList.add( RepositoryFilePermission.ACL_MANAGEMENT.ordinal() );
break;
}
case 4: {
permissionList.add( RepositoryFilePermission.ALL.ordinal() );
break;
}
}
}
return getRepoWs().hasAccess( idToPath( pathId ), permissionList ) ? "true" : "false";
}
public StringBuffer doGetReservedChars() {
List<Character> reservedCharacters = getRepoWs().getReservedChars();
StringBuffer buffer = new StringBuffer();
for ( int i = 0; i < reservedCharacters.size(); i++ ) {
buffer.append( reservedCharacters.get( i ) );
}
return buffer;
}
public StringBuffer doGetReservedCharactersDisplay() {
List<Character> reservedCharacters = getRepoWs().getReservedChars();
StringBuffer buffer = new StringBuffer();
for ( int i = 0; i < reservedCharacters.size(); i++ ) {
if ( reservedCharacters.get( i ) >= 0x07 && reservedCharacters.get( i ) <= 0x0d ) {
buffer.append( escapeJava( "" + reservedCharacters.get( i ) ) );
} else {
buffer.append( reservedCharacters.get( i ) );
}
if ( i + 1 < reservedCharacters.size() ) {
buffer.append( ',' );
}
}
return buffer;
}
/**
* Retrieves the properties of the root directory
*
* @return file properties object <code> RepositoryFileDto </code> for the root directory
*/
public RepositoryFileDto doGetRootProperties() {
return getRepoWs().getFile( FileUtils.PATH_SEPARATOR );
}
/**
* Retrieves the properties of a selected repository file
*
* @param pathId @param pathId colon separated path for the repository file
* <pre function="syntax.xml">
* :path:to:file:id
* </pre>
* @return file properties object <code> RepositoryFileDto </code>
* @throws FileNotFoundException
*/
public RepositoryFileDto doGetProperties( String pathId ) throws FileNotFoundException {
RepositoryFileDto file = getRepoWs().getFile( FileUtils.idToPath( pathId ) );
if ( file == null ) {
throw new FileNotFoundException();
}
return file;
}
/**
* Gets the permission for whether or not a user can create files
*
* @return Boolean representing whether or not user can create files
*/
public String doGetCanCreate() {
return getPolicy().isAllowed( RepositoryCreateAction.NAME ) ? "true" : "false"; //$NON-NLS-1$//$NON-NLS-2$
}
/**
* Gets the content creator of the specified file
*
* @param pathId
* @return
*/
public RepositoryFileDto doGetContentCreator( String pathId ) throws FileNotFoundException {
RepositoryFileDto file = getRepoWs().getFile( idToPath( pathId ) );
if ( file == null ) {
throw new FileNotFoundException();
}
Map<String, Serializable> fileMetadata = getRepository().getFileMetadata( file.getId() );
String creatorId = (String) fileMetadata.get( PentahoJcrConstants.PHO_CONTENTCREATOR );
if ( creatorId != null && creatorId.length() > 0 ) {
return getRepoWs().getFileById( creatorId );
}
return null;
}
/**
* Get deleted files
*
* @return
*/
public List<RepositoryFileDto> doGetDeletedFiles() {
return getRepoWs().getDeletedFiles();
}
/**
* Get metadata for a file by path id
*
* @param pathId
* @return
*/
public List<StringKeyStringValueDto> doGetMetadata( String pathId ) throws FileNotFoundException {
List<StringKeyStringValueDto> list = null;
String path = null;
if ( pathId == null || pathId.equals( FileUtils.PATH_SEPARATOR ) ) {
path = FileUtils.PATH_SEPARATOR;
} else {
if ( !pathId.startsWith( FileUtils.PATH_SEPARATOR ) ) {
path = idToPath( pathId );
}
}
final RepositoryFileDto file = getRepoWs().getFile( path );
if ( file == null ) {
throw new FileNotFoundException();
}
list = getRepoWs().getFileMetadata( file.getId() );
if ( list != null ) {
boolean hasSchedulable = false;
for ( StringKeyStringValueDto value : list ) {
if ( value.getKey().equals( RepositoryFile.SCHEDULABLE_KEY ) ) {
hasSchedulable = true;
break;
}
}
if ( !hasSchedulable ) {
StringKeyStringValueDto schedPerm = new StringKeyStringValueDto( RepositoryFile.SCHEDULABLE_KEY, "true" );
list.add( schedPerm );
}
// check file object for hidden value and add it to the list
list.add( new StringKeyStringValueDto( RepositoryFile.HIDDEN_KEY, String.valueOf( file.isHidden() ) ) );
}
return list;
}
/**
* Set the metadata on a file
*
* @param pathId
* @param metadata
* @throws GeneralSecurityException
*/
public void doSetMetadata( String pathId, List<StringKeyStringValueDto> metadata ) throws GeneralSecurityException {
RepositoryFileDto file = getRepoWs().getFile( idToPath( pathId ) );
RepositoryFileAclDto fileAcl = getRepoWs().getAcl( file.getId() );
boolean canManage =
getSession().getName().equals( fileAcl.getOwner() )
|| ( getPolicy().isAllowed( RepositoryReadAction.NAME )
&& getPolicy().isAllowed( RepositoryCreateAction.NAME ) && getPolicy().isAllowed(
AdministerSecurityAction.NAME ) );
if ( !canManage ) {
if ( fileAcl.isEntriesInheriting() ) {
List<RepositoryFileAclAceDto> aces = getRepoWs().getEffectiveAces( file.getId() );
fileAcl.setAces( aces, fileAcl.isEntriesInheriting() );
}
for ( int i = 0; i < fileAcl.getAces().size(); i++ ) {
RepositoryFileAclAceDto acl = fileAcl.getAces().get( i );
if ( acl.getRecipient().equals( getSession().getName() ) ) {
if ( acl.getPermissions().contains( RepositoryFilePermission.ACL_MANAGEMENT.ordinal() )
|| acl.getPermissions().contains( RepositoryFilePermission.ALL.ordinal() ) ) {
canManage = true;
break;
}
}
}
}
if ( canManage ) {
Map<String, Serializable> fileMetadata = getRepository().getFileMetadata( file.getId() );
boolean isHidden = RepositoryFile.HIDDEN_BY_DEFAULT;
boolean isSchedulable = RepositoryFile.SCHEDULABLE_BY_DEFAULT;
fileMetadata.remove( RepositoryFile.HIDDEN_KEY );
for ( StringKeyStringValueDto nv : metadata ) {
// don't add hidden to the list because it is not actually part of the metadata node
String key = nv.getKey();
if ( RepositoryFile.HIDDEN_KEY.equalsIgnoreCase( key ) ) {
isHidden = BooleanUtils.toBoolean( nv.getValue() );
continue;
}
if ( RepositoryFile.SCHEDULABLE_KEY.equalsIgnoreCase( key ) ) {
isSchedulable = BooleanUtils.toBoolean( nv.getValue() );
}
fileMetadata.put( key, nv.getValue() );
}
// now update the rest of the metadata
if ( !file.isFolder() ) {
getRepository().setFileMetadata( file.getId(), fileMetadata );
}
// handle hidden flag if it is different
if ( file.isHidden() != isHidden ) {
file.setHidden( isHidden );
file.setNotSchedulable( !isSchedulable );
/*
* Since we cannot simply set the new value, use the RepositoryFileAdapter to create a new instance and then
* update the original.
*/
RepositoryFile sourceFile = getRepository().getFileById( file.getId() );
RepositoryFileDto destFileDto = toFileDto( sourceFile, null, false );
destFileDto.setHidden( isHidden );
destFileDto.setNotSchedulable( !isSchedulable );
RepositoryFile destFile = toFile( destFileDto );
// add the existing acls and file data
RepositoryFileAcl acl = getRepository().getAcl( sourceFile.getId() );
if ( !file.isFolder() ) {
IRepositoryFileData data = RepositoryFileHelper.getFileData( sourceFile );
getRepository().updateFile( destFile, data, null );
getRepository().updateAcl( acl );
} else {
getRepository().updateFolder( destFile, null );
}
}
} else {
throw new GeneralSecurityException();
}
}
protected RepositoryFileDto toFileDto( RepositoryFile repositoryFile, Set<String> memberSet, boolean exclude ) {
return RepositoryFileAdapter.toFileDto( repositoryFile, memberSet, exclude );
}
public RepositoryFile toFile( final RepositoryFileDto repositoryFileDto ) {
return RepositoryFileAdapter.toFile( repositoryFileDto );
}
public RepositoryDownloadWhitelist getWhitelist() {
if ( whitelist == null ) {
whitelist = new RepositoryDownloadWhitelist();
}
return whitelist;
}
public boolean isPathValid( String path ) {
if ( path.startsWith( "/etc" ) || path.startsWith( "/system" ) ) {
return false;
}
return true;
}
public boolean isPath( String pathId ) {
return pathId != null && pathId.contains( "/" );
}
/**
*
* @param params
* id of files, separated by ','
*
* @return false if homeFolder has files
* with names and extension equal to passed files
* true otherwise
*
* @throws IllegalArgumentException
* if {@code params} is null
*/
public boolean canRestoreToFolderWithNoConflicts( String pathToFolder, String params ) {
if ( params == null ) {
throw new IllegalArgumentException( "parameters cannot be null" );
}
List<RepositoryFileDto> filesInFolder = doGetChildren( pathToFolder, null, false, true );
String[] sourceFileIds = FileUtils.convertCommaSeparatedStringToArray( params );
for ( RepositoryFileDto fileInFolder : filesInFolder ) {
for ( String sourceFileId : sourceFileIds ) {
RepositoryFile fileToRestore = getRepository().getFileById( sourceFileId );
if ( fileToRestore.getName().equals( fileInFolder.getName() ) ) {
return false;
}
}
}
return true;
}
public IAuthorizationPolicy getPolicy() {
if ( policy == null ) {
policy = PentahoSystem.get( IAuthorizationPolicy.class );
}
return policy;
}
/**
* Store content creator of the selected repository file
*
* @param pathId colon separated path for the repository file
* <pre function="syntax.xml">
* :path:to:file:id
* </pre>
* @param contentCreator repository file
* <pre function="syntax.xml">
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
* <repositoryFileDto>
* <createdDate>1402911997019</createdDate>
* <fileSize>3461</fileSize>
* <folder>false</folder>
* <hidden>false</hidden>
* <id>ff11ac89-7eda-4c03-aab1-e27f9048fd38</id>
* <lastModifiedDate>1406647160536</lastModifiedDate>
* <locale>en</locale>
* <localePropertiesMapEntries>
* <localeMapDto>
* <locale>default</locale>
* <properties>
* <stringKeyStringValueDto>
* <key>file.title</key>
* <value>myFile</value>
* </stringKeyStringValueDto>
* <stringKeyStringValueDto>
* <key>jcr:primaryType</key>
* <value>nt:unstructured</value>
* </stringKeyStringValueDto>
* <stringKeyStringValueDto>
* <key>title</key>
* <value>myFile</value>
* </stringKeyStringValueDto>
* <stringKeyStringValueDto>
* <key>file.description</key>
* <value>myFile Description</value>
* </stringKeyStringValueDto>
* </properties>
* </localeMapDto>
* </localePropertiesMapEntries>
* <locked>false</locked>
* <name>myFile.prpt</name></name>
* <originalParentFolderPath>/public/admin</originalParentFolderPath>
* <ownerType>-1</ownerType>
* <path>/public/admin/ff11ac89-7eda-4c03-aab1-e27f9048fd38</path>
* <title>myFile</title>
* <versionId>1.9</versionId>
* <versioned>true</versioned>
* </repositoryFileAclDto>
* </pre>
* @throws FileNotFoundException
*/
public void doSetContentCreator( String pathId, RepositoryFileDto contentCreator ) throws FileNotFoundException {
RepositoryFileDto file = getRepoWs().getFile( idToPath( pathId ) );
if ( file == null ) {
throw new FileNotFoundException();
}
try {
Map<String, Serializable> fileMetadata = getRepository().getFileMetadata( file.getId() );
fileMetadata.put( PentahoJcrConstants.PHO_CONTENTCREATOR, contentCreator.getId() );
getRepository().setFileMetadata( file.getId(), fileMetadata );
} catch ( Exception e ) {
throw new InternalError();
}
}
/**
* Retrieves the list of locale map for the selected repository file. The list will be empty if a problem occurs.
*
* @param pathId colon separated path for the repository file
* <pre function="syntax.xml">
* :path:to:file:id
* </pre>
* @return <code>List<LocaleMapDto></code> the list of locales
* <pre function="syntax.xml">
* <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
* <localePropertiesMapEntries>
* <localeMapDto>
* <locale>default</locale>
* <properties>
* <stringKeyStringValueDto>
* <key>file.title</key>
* <value>myFile</value>
* </stringKeyStringValueDto>
* <stringKeyStringValueDto>
* <key>jcr:primaryType</key>
* <value>nt:unstructured</value>
* </stringKeyStringValueDto>
* <stringKeyStringValueDto>
* <key>title</key>
* <value>myFile</value>
* </stringKeyStringValueDto>
* <stringKeyStringValueDto>
* <key>file.description</key>
* <value>myFile Description</value>
* </stringKeyStringValueDto>
* </properties>
* </localeMapDto>
* </localePropertiesMapEntries>
* </pre>
* @throws FileNotFoundException
*/
public List<LocaleMapDto> doGetFileLocales( String pathId ) throws FileNotFoundException {
List<LocaleMapDto> availableLocales = new ArrayList<LocaleMapDto>();
RepositoryFileDto file = getRepoWs().getFile( idToPath( pathId ) );
if ( file == null ) {
throw new FileNotFoundException();
}
try {
List<PentahoLocale> locales = getRepoWs().getAvailableLocalesForFileById( file.getId() );
if ( locales != null && !locales.isEmpty() ) {
for ( PentahoLocale locale : locales ) {
availableLocales.add( new LocaleMapDto( locale.toString(), null ) );
}
}
} catch ( Exception e ) {
throw new InternalError();
}
return availableLocales;
}
/**
* Checks whether the current user can administer the platform.
* The conditions are <code>RepositoryReadAction</code>, <code>RepositoryCreateAction</code> and
* <code>AdministerSecurityAction</code>
*
* @return <code>boolean</code>
*/
public boolean doCanAdminister() {
boolean status = false;
try {
status = getPolicy().isAllowed( RepositoryReadAction.NAME )
&& getPolicy().isAllowed( RepositoryCreateAction.NAME )
&& getPolicy().isAllowed( AdministerSecurityAction.NAME );
} catch ( Exception e ) {
logger.error( Messages.getInstance().getString( "SystemResource.CAN_ADMINISTER" ), e );
}
return status;
}
/**
* Retrieves the acls of the selected repository file
*
* @param pathId (colon separated path for the repository file)
* @return <code> RepositoryFileAclDto </code>
*/
public RepositoryFileAclDto doGetFileAcl( String pathId ) {
RepositoryFileDto file = getRepoWs().getFile( FileUtils.idToPath( pathId ) );
RepositoryFileAclDto fileAcl = getRepoWs().getAcl( file.getId() );
if ( fileAcl.isEntriesInheriting() ) {
List<RepositoryFileAclAceDto> aces =
getRepoWs().getEffectiveAcesWithForceFlag( file.getId(), fileAcl.isEntriesInheriting() );
fileAcl.setAces( aces, fileAcl.isEntriesInheriting() );
}
addAdminRole( fileAcl );
return fileAcl;
}
protected void addAdminRole( RepositoryFileAclDto fileAcl ) {
String adminRoleName =
PentahoSystem.get( String.class, "singleTenantAdminAuthorityName", PentahoSessionHolder.getSession() );
if ( fileAcl.getAces() == null ) {
fileAcl.setAces( new LinkedList<RepositoryFileAclAceDto>() );
}
for ( RepositoryFileAclAceDto facl : fileAcl.getAces() ) {
if ( facl.getRecipient().equals( adminRoleName ) && facl.getRecipientType() == 1 ) {
return;
}
}
RepositoryFileAclAceDto adminGroup = new RepositoryFileAclAceDto();
adminGroup.setRecipient( adminRoleName );
adminGroup.setRecipientType( 1 );
adminGroup.setModifiable( false );
List<Integer> perms = new LinkedList<Integer>();
perms.add( 4 );
adminGroup.setPermissions( perms );
fileAcl.getAces().add( adminGroup );
}
public RepositoryFileTreeDto doGetTree( String pathId, Integer depth, String filter, Boolean showHidden,
Boolean includeAcls ) {
String path = null;
if ( pathId == null || pathId.equals( FileUtils.PATH_SEPARATOR ) ) {
path = FileUtils.PATH_SEPARATOR;
} else if ( !pathId.startsWith( FileUtils.PATH_SEPARATOR ) ) {
path = idToPath( pathId );
}
RepositoryRequest repositoryRequest = getRepositoryRequest( path, showHidden, depth, filter );
repositoryRequest.setIncludeAcls( includeAcls );
RepositoryFileTreeDto tree = getRepoWs().getTreeFromRequest( repositoryRequest );
List<RepositoryFileTreeDto> filteredChildren = new ArrayList<RepositoryFileTreeDto>();
// BISERVER-9599 - Use special sort order
if ( isShowingTitle( repositoryRequest ) ) {
Collator collator = getCollatorInstance();
collator.setStrength( Collator.PRIMARY ); // ignore case
sortByLocaleTitle( collator, tree );
}
for ( RepositoryFileTreeDto child : tree.getChildren() ) {
RepositoryFileDto file = child.getFile();
Map<String, Serializable> fileMeta = getRepository().getFileMetadata( file.getId() );
boolean isSystemFolder =
fileMeta.containsKey( IUnifiedRepository.SYSTEM_FOLDER ) ? (Boolean) fileMeta
.get( IUnifiedRepository.SYSTEM_FOLDER ) : false;
if ( !isSystemFolder ) {
filteredChildren.add( child );
}
}
tree.setChildren( filteredChildren );
return tree;
}
public void sortByLocaleTitle( final Collator collator, final RepositoryFileTreeDto tree ) {
if ( tree == null || tree.getChildren() == null || tree.getChildren().size() <= 0 ) {
return;
}
for ( RepositoryFileTreeDto rft : tree.getChildren() ) {
sortByLocaleTitle( collator, rft );
}
Collections.sort( tree.getChildren(), new Comparator<RepositoryFileTreeDto>() {
@Override
public int compare( RepositoryFileTreeDto repositoryFileTree, RepositoryFileTreeDto repositoryFileTree2 ) {
String title1 = repositoryFileTree.getFile().getTitle();
String title2 = repositoryFileTree2.getFile().getTitle();
if ( collator.compare( title1, title2 ) == 0 ) {
return title1.compareTo( title2 ); // use lexical order if equals ignore case
}
return collator.compare( title1, title2 );
}
} );
}
/**
* Retrieve the executed contents for a selected repository file
*
* @param pathId the path for the file
* @return list of <code> repositoryFileDto </code>
* @throws FileNotFoundException if the file is not found
*/
public List<RepositoryFileDto> doGetGeneratedContent( String pathId ) throws FileNotFoundException {
SessionResource sessionResource = getSessionResource();
return doGetGeneratedContentForUser( pathId, sessionResource.doGetCurrentUserDir() );
}
/**
* Retrieve the executed contents for a selected repository file and a given user
*
* @param pathId the path for the file
* @param user the username for the generated content folder
* @return list of <code> repositoryFileDto </code>
* @throws FileNotFoundException
*/
public List<RepositoryFileDto> doGetGeneratedContent( String pathId, String user ) throws FileNotFoundException {
SessionResource sessionResource = getSessionResource();
return doGetGeneratedContentForUser( pathId, sessionResource.doGetUserDir( user ) );
}
/**
* Retrieve the executed contents for a selected repository file and a given user
*
* @param pathId the path for the file
* @param userDir the user home directory
* @return list of <code> repositoryFileDto </code>
* @throws FileNotFoundException
* @private
*/
private List<RepositoryFileDto> doGetGeneratedContentForUser( String pathId, String userDir )
throws FileNotFoundException {
RepositoryFileDto targetFile = doGetProperties( pathId );
if ( targetFile != null ) {
String targetFileId = targetFile.getId();
return searchGeneratedContent( userDir, targetFileId, PentahoJcrConstants.PHO_CONTENTCREATOR );
} else {
logger.error( Messages.getInstance().getString( "FileResource.FILE_NOT_FOUND", pathId ) );
throw new FileNotFoundException( pathId );
}
}
/**
* @param userDir the user home directory
* @param targetComparator the comparator to filter
* @param metadataConstant the property used to get the file property to compare
* @return list of <code> repositoryFileDto </code>
* @throws FileNotFoundException
* @private
*/
protected List<RepositoryFileDto> searchGeneratedContent( String userDir, String targetComparator,
String metadataConstant )
throws FileNotFoundException {
List<RepositoryFileDto> content = new ArrayList<RepositoryFileDto>();
RepositoryFile workspaceFolder = getRepository().getFile( userDir );
if ( workspaceFolder != null ) {
List<RepositoryFile> children = getRepository().getChildren( workspaceFolder.getId() );
for ( RepositoryFile child : children ) {
if ( !child.isFolder() ) {
Map<String, Serializable> fileMetadata = getRepository().getFileMetadata( child.getId() );
String creatorId = (String) fileMetadata.get( metadataConstant );
if ( creatorId != null && creatorId.equals( targetComparator ) ) {
content.add( toFileDto( child, null, false ) );
}
}
}
} else {
logger.error( Messages.getInstance().getString( "FileResource.WORKSPACE_FOLDER_NOT_FOUND", userDir ) );
throw new FileNotFoundException( userDir );
}
return content;
}
/**
* Gets an instance of SessionResource
*
* @return <code>SessionResource</code>
*/
protected SessionResource getSessionResource() {
if ( sessionResource == null ) {
sessionResource = new SessionResource();
}
return sessionResource;
}
/**
* Rename the name of the selected file
*
* @param pathId (colon separated path for the repository file)
* @param newName (New name of the file)
* @return
*/
public boolean doRename( String pathId, String newName ) throws Exception {
IUnifiedRepository repository = getRepository();
RepositoryFile fileToBeRenamed = repository.getFile( FileUtils.idToPath( pathId ) );
StringBuilder buf = new StringBuilder( fileToBeRenamed.getPath().length() );
buf.append( getParentPath( fileToBeRenamed.getPath() ) );
buf.append( RepositoryFile.SEPARATOR );
buf.append( newName );
if ( !fileToBeRenamed.isFolder() ) {
String extension = getExtension( fileToBeRenamed.getName() );
if ( extension != null ) {
buf.append( extension );
}
}
repository.moveFile( fileToBeRenamed.getId(), buf.toString(), "Renaming the file" );
RepositoryFile movedFile = repository.getFileById( fileToBeRenamed.getId() );
if ( movedFile != null ) {
if ( !movedFile.isFolder() ) {
Map<String, Properties> localePropertiesMap = movedFile.getLocalePropertiesMap();
if ( localePropertiesMap == null ) {
localePropertiesMap = new HashMap<String, Properties>();
Properties properties = new Properties();
properties.setProperty( "file.title", newName );
properties.setProperty( "title", newName );
localePropertiesMap.put( "default", properties );
} else {
for ( Map.Entry<String, Properties> entry : localePropertiesMap.entrySet() ) {
Properties properties = entry.getValue();
if ( properties.containsKey( "file.title" ) ) {
properties.setProperty( "file.title", newName );
}
if ( properties.containsKey( "title" ) ) {
properties.setProperty( "title", newName );
}
}
}
RepositoryFile updatedFile =
new RepositoryFile.Builder( movedFile ).localePropertiesMap( localePropertiesMap ).name( newName ).title(
newName ).build();
repository.updateFile( updatedFile, RepositoryFileHelper.getFileData( movedFile ), "Updating the file" );
}
return true;
} else {
return false;
//return Response.ok( "File to be renamed does not exist" ).build();
}
}
/**
* Creates a new folder with the specified name
*
* @param pathId The path from the root folder to the root node of the tree to return using colon characters in
* place of / or \ characters. To clarify /path/to/file, the encoded pathId would be :path:to:file
* <pre function="syntax.xml">
* :path:to:file
* </pre>
* @return A jax-rs Response object with the appropriate status code, header, and body.
* @deprecated use {@link #doCreateDirSafe(String)} instead
*/
@Deprecated
public boolean doCreateDir( String pathId ) {
String path = idToPath( pathId );
return doCreateDirFor( path );
}
private boolean doCreateDirFor( String pathWithSlashes ) {
String[] folders = pathWithSlashes.split( "[" + FileUtils.PATH_SEPARATOR + "]" ); //$NON-NLS-1$//$NON-NLS-2$
RepositoryFileDto parentDir = getRepoWs().getFile( FileUtils.PATH_SEPARATOR );
boolean dirCreated = false;
for ( String folder : folders ) {
String currentFolderPath = ( parentDir.getPath() + FileUtils.PATH_SEPARATOR + folder ).substring( 1 );
if ( !currentFolderPath.startsWith( FileUtils.PATH_SEPARATOR ) ) {
currentFolderPath = FileUtils.PATH_SEPARATOR + currentFolderPath;
}
RepositoryFileDto currentFolder = getRepoWs().getFile( currentFolderPath );
if ( currentFolder == null ) {
currentFolder = new RepositoryFileDto();
currentFolder.setFolder( true );
currentFolder.setName( decode( folder ) );
currentFolder.setPath( parentDir.getPath() + FileUtils.PATH_SEPARATOR + folder );
currentFolder = getRepoWs().createFolder( parentDir.getId(), currentFolder, currentFolderPath );
dirCreated = true;
}
parentDir = currentFolder;
}
return dirCreated;
}
/**
* Creates a new folder with {@code pathId} as name if it does not contain reserved characters. To obtain them, the
* method calls {@link #doGetReservedChars()}. Additionally, it is checked that folder name is not '.' or '..' and
* does not contain '/'.
*
* @param pathId the desired path
* @return {@code true} if the folder has been created
* @throws InvalidNameException if {@code pathId} contains prohibited characters.
*/
public boolean doCreateDirSafe( String pathId ) throws InvalidNameException {
if ( pathId.indexOf( '/' ) != -1 ) {
// after converting id to path '/' will be interpreted as path separator,
// hence check it here
throw new InvalidNameException();
}
String path = idToPath( pathId );
if ( path.indexOf( '\\' ) != -1 ) {
// '\' is prohibited as well
throw new InvalidNameException();
}
if ( !isValidFolderName( path ) ) {
throw new InvalidNameException();
}
return doCreateDirFor( path );
}
private boolean isValidFolderName( String path ) {
if ( FileUtils.containsReservedCharacter( path, doGetReservedChars().toString().toCharArray() ) ) {
return false;
}
String folderName = FilenameUtils.getName( path );
return !".".equals( folderName ) && !"..".equals( folderName );
}
private String getParentPath( final String path ) {
return FileUtils.getParentPath( path );
}
private String getExtension( final String name ) {
int startIndex = name.lastIndexOf( '.' );
if ( startIndex >= 0 ) {
return name.substring( startIndex, name.length() );
}
return null;
}
protected DefaultUnifiedRepositoryWebService getRepoWs() {
if ( defaultUnifiedRepositoryWebService == null ) {
defaultUnifiedRepositoryWebService = new DefaultUnifiedRepositoryWebService();
}
return defaultUnifiedRepositoryWebService;
}
public int copy( InputStream input, OutputStream output ) throws IOException {
return IOUtils.copy( input, output );
}
public RepositoryFileOutputStream getRepositoryFileOutputStream( String path ) {
return new RepositoryFileOutputStream( path );
}
public RepositoryFileInputStream getRepositoryFileInputStream( RepositoryFile repositoryFile )
throws FileNotFoundException {
return new RepositoryFileInputStream( repositoryFile );
}
public StreamingOutput getStreamingOutput( final InputStream is ) {
return new StreamingOutput() {
@Override
public void write( OutputStream output ) throws IOException {
copy( is, output );
}
};
}
public StringKeyStringValueDto getStringKeyStringValueDto( String key, String value ) {
return new StringKeyStringValueDto( key, value );
}
public IUnifiedRepository getRepository() {
if ( repository == null ) {
repository = PentahoSystem.get( IUnifiedRepository.class );
}
return repository;
}
public String idToPath( String pathId ) {
return FileUtils.idToPath( pathId );
}
protected IPentahoSession getSession() {
return PentahoSessionHolder.getSession();
}
protected String escapeJava( String value ) {
return StringEscapeUtils.escapeJava( value );
}
protected BaseExportProcessor getDownloadExportProcessor( String path, boolean requiresZip, boolean withManifest ) {
return requiresZip ? new ZipExportProcessor( path, getRepository(), withManifest ) : new SimpleExportProcessor( path, getRepository() );
}
protected ExportHandler getDownloadExportHandler() {
return PentahoSystem.get( DefaultExportHandler.class );
}
protected StreamingOutput getDownloadStream( RepositoryFile repositoryFile, BaseExportProcessor exportProcessor )
throws ExportException, IOException {
File zipFile = exportProcessor.performExport( repositoryFile );
final FileInputStream is = new FileInputStream( zipFile );
// copy streaming output
return new StreamingOutput() {
@Override
public void write( OutputStream output ) throws IOException {
IOUtils.copy( is, output );
}
};
}
protected RepositoryRequest getRepositoryRequest( String path, Boolean showHidden, Integer depth, String filter ) {
return new RepositoryRequest( path, showHidden, depth, filter );
}
protected Collator getCollatorInstance() {
return Collator.getInstance( PentahoSessionHolder.getSession().getLocale() );
}
public class RepositoryFileToStreamWrapper {
private StreamingOutput outputStream;
private RepositoryFile repositoryFile;
private String mimetype;
public void setOutputStream( StreamingOutput outputStream ) {
this.outputStream = outputStream;
}
public void setRepositoryFile( RepositoryFile repositoryFile ) {
this.repositoryFile = repositoryFile;
}
public void setMimetype( String mimetype ) {
this.mimetype = mimetype;
}
public StreamingOutput getOutputStream() {
return outputStream;
}
public String getMimetype() {
return mimetype;
}
public RepositoryFile getRepositoryFile() {
return repositoryFile;
}
}
public List<RepositoryFileDto> doGetChildren( String pathId, String filter, Boolean showHidden,
Boolean includeAcls ) {
List<RepositoryFileDto> repositoryFileDtoList = new ArrayList<RepositoryFileDto>();
RepositoryFileDto repositoryFileDto = getRepoWs().getFile( FileUtils.idToPath( pathId ) );
if ( repositoryFileDto != null && isPathValid( repositoryFileDto.getPath() ) ) {
RepositoryRequest repositoryRequest = getRepositoryRequest( repositoryFileDto, showHidden, filter, includeAcls );
repositoryFileDtoList = getRepoWs().getChildrenFromRequest( repositoryRequest );
// BISERVER-9599 - Use special sort order
if ( isShowingTitle( repositoryRequest ) ) {
Collator collator = getCollator( Collator.PRIMARY );
sortByLocaleTitle( collator, repositoryFileDtoList );
}
}
return repositoryFileDtoList;
}
public boolean isShowingTitle( RepositoryRequest repositoryRequest ) {
if ( repositoryRequest.getExcludeMemberSet() != null && !repositoryRequest.getExcludeMemberSet().isEmpty() ) {
if ( repositoryRequest.getExcludeMemberSet().contains( "title" ) ) {
return false;
}
} else if ( repositoryRequest.getIncludeMemberSet() != null
&& !repositoryRequest.getIncludeMemberSet().contains( "title" ) ) {
return false;
}
return true;
}
public void sortByLocaleTitle( final Collator collator, final List<RepositoryFileDto> repositoryFileDtoList ) {
if ( repositoryFileDtoList == null || repositoryFileDtoList.size() <= 0 ) {
return;
}
Collections.sort( repositoryFileDtoList, new Comparator<RepositoryFileDto>() {
@Override
public int compare( RepositoryFileDto repositoryFile, RepositoryFileDto repositoryFile2 ) {
String title1 = repositoryFile.getTitle();
String title2 = repositoryFile2.getTitle();
if ( collator.compare( title1, title2 ) == 0 ) {
return title1.compareTo( title2 ); // use lexical order if equals ignore case
}
return collator.compare( title1, title2 );
}
} );
}
protected RepositoryRequest getRepositoryRequest( RepositoryFileDto repositoryFileDto, Boolean showHidden,
String filter, Boolean includeAcls ) {
RepositoryRequest repositoryRequest = new RepositoryRequest( repositoryFileDto.getId(), showHidden, 0, filter );
repositoryRequest.setIncludeAcls( includeAcls );
return repositoryRequest;
}
protected Collator getCollator( int strength ) {
Collator collator = Collator.getInstance( PentahoSessionHolder.getSession().getLocale() );
collator.setStrength( strength ); // ignore case
return collator;
}
private PentahoPlatformExporter getBackupExporter() {
if ( backupExporter == null ) {
backupExporter = new PentahoPlatformExporter( getRepository() );
}
return backupExporter;
}
protected String decode( String folder ) {
String decodeName = folder;
try {
decodeName = URLDecoder.decode( folder, "UTF-8" );
} catch ( Exception ex ) {
logger.error( ex );
}
return decodeName;
}
public static class InvalidNameException extends Exception {
private static final long serialVersionUID = 5394548505099358146L;
}
protected void validateAccess( String importDir ) throws PentahoAccessControlException {
IAuthorizationPolicy policy = getPolicy();
//check if we are admin or have publish permission
boolean isAdminOrPublish = policy.isAllowed( RepositoryReadAction.NAME )
&& policy.isAllowed( RepositoryCreateAction.NAME )
&& ( policy.isAllowed( AdministerSecurityAction.NAME )
|| policy.isAllowed( PublishAction.NAME ) );
if ( !isAdminOrPublish ) {
//the user does not have admin or publish permission, so we will check if the user imports to their home folder
boolean usingHomeFolder = false;
String tenatedUserName = PentahoSessionHolder.getSession().getName();
//get user home home folder path
String userHomeFolderPath = ServerRepositoryPaths
.getUserHomeFolderPath( JcrTenantUtils.getUserNameUtils().getTenant( tenatedUserName ),
JcrTenantUtils.getUserNameUtils().getPrincipleName( tenatedUserName ) );
if ( userHomeFolderPath != null && userHomeFolderPath.length() > 0 ) {
//we pass the relative path so add serverside root folder for every home folder
usingHomeFolder = ( ServerRepositoryPaths.getTenantRootFolderPath() + importDir )
.contains( userHomeFolderPath );
}
if ( !( usingHomeFolder && policy.isAllowed( RepositoryCreateAction.NAME )
&& policy.isAllowed( RepositoryReadAction.NAME ) ) ) {
throw new PentahoAccessControlException( "User is not authorized to perform this operation" );
}
}
}
}