Integrating FCKEditor into Turbine
FCKEditor (
http://www.fckeditor.net ) is a great Javascript based editor. It can manage server repositories to store files for documents. Let us make a server side interface for this. Planning considerations:
there should be a lot of subrepository to store its data in server, for example, user´s introductions, news parts, etc.
All of entity can make subdirectories into own repository. It´s optional controlling by TR.properties
All of entity´s subdirectory can has a special directory named ""private"" to serve data for logged in users.
All of subrepository has quotas to reduce size of entity store.
File serving is made by Turbine to controlling access to files in repositories.
Making TurbineDirectory Service
there are 3 class of this package
DirectoryService.java
It is an interface for constants and method declarations.
package com.zamek.portal_zamek.services.directory;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import org.apache.commons.fileupload.FileItem;
import org.apache.turbine.services.Service;
import com.zamek.portal_zamek.PortalException;
import com.zamek.portal_zamek.util.FileInfo;
public interface DirectoryService extends Service {
/**
* The key under which this service is stored in TurbineServices.
*/
String SERVICE_NAME = "DirectoryService";
/**
* repository part in url
*/
String CTX_REPO = "repo";
/**
* id part in url
*/
String CTX_ID = "id";
/**
* file part in url
*/
String CTX_FILE = "file";
/**
* FileServ action part of link
*/
String CTX_FILE_SERV = "FileServ";
/**
* repository path key in config
*/
String REPOSITORY_KEY = "repository";
/**
* default base path
*/
String DEFAULT_REPOSITORY = "resources";
/**
* default enable private
*/
boolean DEFAULT_PRIVATE = false;
/**
* quota key in config
*/
String QUOTA_KEY = "quota";
/**
* Private directory enable key in config
*/
String KEY_PRIVATE_ENABLED = "private.enabled";
/**
* create directory enabled key in config
*/
String KEY_CREATE_DIRECTORY_ENABLED = "create-directory.enabled";
/**
* quota key in config
*/
String KEY_QUOTA = "quota";
/**
* default quota of each directory size
*/
int DEFAULT_QUOTA = 1024000;
/**
* index of resourcebundle to quota exceeded
*/
String ERR_QUOTA_EXCEEDED = "quota_exceede";
String ERR_CANNOT_OPEN_FILE = "cannot_open_file";
String MASK_PRIVATE = "/private";
/**
* Folder already exists error for FCKEditor
*/
public final static int ERR_FOLDER_EXISTS = 101;
/**
* Invalid folder name error for FCKEditor
*/
public final static int ERR_INVALID_FOLDER = 102;
/**
* You have no permissions to create the folder error for FCKEDitor
*/
public final static int ERR_NO_PERMISSION = 103;
/**
* Unknown error creating folder for FCKEditor
*/
public final static int ERR_UNKNOWN = 110;
/**
* getting list of folders in given folder for FCKEditor.
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param includeMask default mask is *, if it's null
* @param excludeMask default mask is null
* @return list of directories in url directoy
* @throws FileNotFoundException
*/
public List<String> getFolders (final String subRepo, final String id,
final String url, final String includeMask, final String excludeMask)
throws FileNotFoundException;
/**
* getting list of files in given folder
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param url path url in own directory
* @param includeMask includeMask default mask is *, if it's null
* @param excludeMask excludeMask default mask is null
* @return list files in url directory
* @throws FileNotFoundException
*/
public List<FileInfo> getFiles(final String subRepo, final String id,
final String url, final String includeMask, final String excludeMask)
throws FileNotFoundException;
/**
* upload file to own directory using its original name
* You can set quota in TR.properties in
* services.directoryservice.<subrepo>.quota=nn
* quota is unlimited if it's 0
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param fileItem file to be upload
* @return true if success
* @throws PortalException
* @throws Exception
*/
public boolean uploadItemFile (final String subRepo, final String id,
final String path, final FileItem fileItem)
throws PortalException, Exception ;
/**
* upload file to own directory using newName
* You can set quota in TR.properties in
* services.directoryservice.<subrepo>.quota=nn
* quota is unlimited if it's 0
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param fileItem file to be upload
* @param newName rename to this new name
* @return true if success
* @throws PortalException
* @throws Exception
*/
public boolean uploadItemFile (final String subRepo, final String id,
final String path, final FileItem fileItem, final String newName)
throws PortalException, Exception;
/**
* checking to has private directory
* in TR.properties you can enable private directory:
* services.directoryservice.<subrepo>.private.enabled=true
* private directory is readable only for users, not to anonymous
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @return if private.enabled is true
*/
public boolean hasPrivateDir (final String subRepo, final String id);
/**
* get a file by url from repository
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param fileName name of file
* @return bytes of file
* @throws FileNotFoundException
* @throws IOException
* @throws PortalException
*/
public ByteArrayOutputStream getFile (final String subRepo, final String id, final String fileName)
throws FileNotFoundException, IOException, PortalException;
/**
* create a directory in repository if it's enabled by TR.properties
* services.directoryservice.<subrepo>.create-directory.enabled
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param folder folder to create
* @return
* 0: OK
* 101: Folder already exists error for FCKEditor
* 102: Invalid folder name error for FCKEditor
* 103: You have no permissions to create the folder error for FCKEDitor
* 110: Unknown error creating folder for FCKEditor
* @throws FileNotFoundException
*/
public int createDirectory (final String subRepo, final String id, final String folder)
throws FileNotFoundException;
/**
* save string to repository to given name
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id id entity id, root of entity
* @param path url in own directory
* @param fileName name of file to save
* @param data data to save
* @throws IOException
*/
public void saveToFile(final String subRepo, final String id,
final String path, final String fileName, final String data)
throws IOException, PortalException;
/**
* checking given file to exists
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id id entity id, root of entity
* @param path url in own directory
* @param fileName name of file to check
* @return true if exists
* @throws FileNotFoundException
*/
public boolean isFileExists (final String subRepo, final String id,
final String path, final String fileName)
throws FileNotFoundException;
/**
* create directory of identity in subRepo
*/
public boolean createIdDirectory (final String subRepo, final String id)
throws FileNotFoundException;
/**
* getting quota of subRepo
* @param subRepo
* @return 0 if no quota
*/
public long getQuota (final String subRepo);
/**
* getting real path of subrepo
* @param subRepo
* @return
*/
public String getBasePath(final String subRepo);
/**
* getting enable create directory property
* @param subRepo
* @return
*/
public boolean getCreateDirEnabled(final String subRepo);
/**
* getting enable private directory property
* @param subRepo
* @return
*/
public boolean getPrivateDirEnabled (final String subRepo);
/**
* checking subrepo exists in TR.properties
* @param subRepo
* @return
*/
public boolean isSubRepoEnabled(final String subRepo);
/**
* create a link for a file
* @param subRepo
* @param id
* @param fileName
* @return
*/
public String getFileUrl(final String subRepo, final String id, final String fileName) ;
}
TurbineDirectoryService.java
This is the core of TurbineDirectory service.
package com.zamek.portal_zamek.services.directory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.turbine.Turbine;
import org.apache.turbine.services.InitializationException;
import org.apache.turbine.services.TurbineBaseService;
import org.apache.turbine.services.localization.Localization;
import org.apache.turbine.util.uri.TemplateURI;
import com.zamek.portal_zamek.PortalException;
import com.zamek.portal_zamek.util.FileInfo;
import com.zamek.portal_zamek.util.FileUtils;
public class TurbineDirectoryService extends TurbineBaseService implements
DirectoryService {
private static Log log = LogFactory.getLog(TurbineDirectoryService.class);
private final static int BUFFER_SIZE = 0x4000;
private String repoPath = null;
/*
* list of disabled directory names
*/
enum DisabledFolderNames
{
ROOT ("^/");
private final String item;
DisabledFolderNames(String item) { this.item = item; }
String getItem () { return this.item; }
}
public TurbineDirectoryService() {
}
/**
* initialisation. Get repository path from TR.prop
*/
public void init () throws InitializationException
{
repoPath = getConfiguration().getString(
DirectoryService.REPOSITORY_KEY,
DirectoryService.DEFAULT_REPOSITORY);
String testPath = Turbine.getRealPath(repoPath);
File testDir = new File(testPath);
if (!testDir.exists())
{
if (!testDir.mkdirs())
throw new InitializationException("Couldn't create target directory!");
}
repoPath = testPath;
getConfiguration().setProperty(DirectoryService.REPOSITORY_KEY, repoPath);
log.debug("DirectoryService path is:" + repoPath);
setInit(true);
}
/**
* checking subrepo exists in TR.properties
* @param subRepo
* @return
*/
public boolean isSubRepoEnabled(final String subRepo)
{
return StringUtils.isNumeric(getConfiguration().getString(subRepo + '.' + DirectoryService.KEY_QUOTA));
}
/**
* getting list of folders in given folder for FCKEditor.
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param includeMask default mask is *, if it's null
* @param excludeMask default mask is null
* @return list of directories in url directoy
* @throws FileNotFoundException
*/
public List<String> getFolders (final String subRepo, final String id, final String url,
final String includeMask, final String excludeMask)
throws FileNotFoundException
{
String basePath = getBasePath(subRepo, id);
return basePath != null
? FileUtils.getDirectoryNames(basePath+File.separatorChar +
securityCheck (url), includeMask, excludeMask)
: null;
}
/**
* getting list of files in given folder for FCKEditor
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param url path url in own directory
* @param includeMask includeMask default mask is *, if it's null
* @param excludeMask excludeMask default mask is null
* @return list files in url directory
* @throws FileNotFoundException
*/
public List<FileInfo> getFiles(final String subRepo, final String id, final String url,
final String includeMask, final String excludeMask)
throws FileNotFoundException
{
String basePath = getBasePath(subRepo, id);
return basePath != null
? FileUtils.getFileNames(basePath+File.separatorChar +
securityCheck (url), includeMask, excludeMask)
: null;
}
/**
* upload file to own directory using its original name
* You can set quota in TR.properties in
* services.directoryservice.<subrepo>.quota=nn
* quota is unlimited if it's 0
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param fileItem file to be upload
* @return true if success
* @throws PortalException
* @throws Exception
*/
public boolean uploadItemFile (final String subRepo, final String id,
final String path, final FileItem fileItem)
throws PortalException, Exception
{
return uploadItemFile(subRepo, id, path, fileItem, fileItem.getName());
}
/**
* upload file to own directory using newName
* You can set quota in TR.properties in
* services.directoryservice.<subrepo>.quota=nn
* quota is unlimited if it's 0
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param fileItem file to be upload
* @param newName rename to this new name
* @return true if success
* @throws PortalException
* @throws Exception
*/
public boolean uploadItemFile (final String subRepo, final String id,
final String path, final FileItem fileItem, final String newName)
throws PortalException, Exception
{
String basePath = getBasePath(subRepo, id);
if (basePath == null)
return false;
Configuration conf = getConfiguration();
int quota = conf.getInt(subRepo + '.' + DirectoryService.KEY_QUOTA, 0);
// checking quotas
if (quota > 0 &&
((getSize(basePath) + fileItem.getSize()) > quota))
throw new PortalException (Localization.getString(ERR_QUOTA_EXCEEDED));
try
{
FileUtils.uploadFile(fileItem, basePath+securityCheck (path), securityCheck(newName));
return true;
}
catch (Exception e) {
return false;
}
}
/**
* checking to has private directory
* in TR.properties you can enable private directory:
* services.directoryservice.<subrepo>.private.enabled=true
* private directory is readable only for users, not to anonymous
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @return if private.enabled is true
*/
public boolean hasPrivateDir (final String subRepo, final String id)
{
return getConfiguration().getBoolean(subRepo + '.' + DirectoryService.KEY_PRIVATE_ENABLED);
}
/**
* get a file by url from repository
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param fileName name of file
* @return bytes of file
* @throws FileNotFoundException
* @throws IOException
* @throws PortalException
*/
public ByteArrayOutputStream getFile (final String subRepo, final String id,
final String fileName)
throws FileNotFoundException, IOException, PortalException
{
//getting basePath
String basePath = getBasePath(subRepo, id);
if (basePath == null)
return null;
File file = new File (basePath + File.separatorChar + securityCheck(fileName));
if (file.isFile() && file.exists() && file.canRead())
{
FileInputStream fis = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[BUFFER_SIZE];
int len = 0;
for(len = fis.read(buffer, 0, BUFFER_SIZE);len != -1;len = fis.read(buffer, 0 , BUFFER_SIZE))
bos.write(buffer, 0, len);
return bos;
}
else
throw new PortalException (String.format(Localization.getString(ERR_CANNOT_OPEN_FILE),fileName));
}
/**
* create a directory in repository if it's enabled by TR.properties
* services.directoryservice.<subrepo>.create-directory.enabled
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param folder folder to create
* @return
* 0: OK
* 101: Folder already exists error for FCKEditor
* 102: Invalid folder name error for FCKEditor
* 103: You have no permissions to create the folder error for FCKEDitor
* 110: Unknown error creating folder for FCKEditor
* @throws FileNotFoundException
*/
public int createDirectory (final String subRepo, final String id, final String folder)
throws FileNotFoundException
{
Configuration conf = getConfiguration();
if (conf.getBoolean(subRepo+"."+DirectoryService.KEY_CREATE_DIRECTORY_ENABLED))
{
String basePath = getBasePath(subRepo, id);
if (basePath == null || folder == null || folder.length() <= 0)
return DirectoryService.ERR_UNKNOWN;
// checking disabled folder names
for (DisabledFolderNames dfn : DisabledFolderNames.values() )
if (folder.matches(dfn.getItem()))
return ERR_INVALID_FOLDER;
try
{
File newDir = new File (basePath + securityCheck(folder));
if (newDir == null)
return ERR_UNKNOWN;
if (newDir.exists())
return ERR_FOLDER_EXISTS;
return newDir.mkdir() ? 0 : ERR_UNKNOWN;
}
catch(Exception e) {
return ERR_INVALID_FOLDER;
}
}
else
return ERR_INVALID_FOLDER;
}
/**
* save string to repository to given name
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id id entity id, root of entity
* @param path url in own directory
* @param fileName name of file to save
* @param data data to save
* @throws IOException
*/
public void saveToFile(final String subRepo, final String id,
final String path, final String fileName, final String data)
throws IOException, PortalException
{
String basePath = getBasePath(subRepo, id);
if (basePath == null || fileName == null || fileName.length() == 0)
return;
int quota = getConfiguration().getInt(subRepo + '.' + DirectoryService.KEY_QUOTA, 0);
// checking quotas
if (quota > 0 &&
((getSize(basePath) + data.length()) > quota))
throw new PortalException (Localization.getString(ERR_QUOTA_EXCEEDED));
File file = new File (basePath + securityCheck(path) + File.separatorChar + securityCheck (fileName));
PrintWriter out = new PrintWriter(new FileWriter(file));
out.print(data);
out.close();
}
/**
* checking given file to exists
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id id entity id, root of entity
* @param path url in own directory
* @param fileName name of file to check
* @return true if exists
* @throws FileNotFoundException
*/
public boolean isFileExists (final String subRepo, final String id,
final String path, final String fileName)
throws FileNotFoundException
{
String basePath = getBasePath(subRepo, id);
if (basePath == null)
return false;
File file = new File (basePath + securityCheck(path) + File.separatorChar + fileName);
return file.exists();
}
public long getSize(final String basePath)
{
return FileUtils.getSize(basePath);
}
/**
* create directory of identity in subRepo
*/
public boolean createIdDirectory (final String subRepo, final String id)
throws FileNotFoundException
{
return getBasePath(subRepo, id, true) != null;
}
private String getBasePath(final String subRepo, final String id, boolean createIfNotExists)
{
String result = new StringBuffer(repoPath).append(File.separatorChar).append(subRepo).
append(File.separatorChar).append(id).append(File.separatorChar).toString();
File dir = new File(result);
if (dir.exists() && dir.isDirectory())
return result;
if (createIfNotExists)
dir.mkdirs();
return result;
}
private String getBasePath(final String subRepo, final String id) throws FileNotFoundException
{
return getBasePath (subRepo, id, false);
}
/**
* checking url security, removing all of ../ ./
* @param url
* @return
*/
private String securityCheck(final String url) {
return url.compareTo("/") == 0
? ""
: url.replaceAll("\\.+\\/", "");
}
/**
* getting real path of subrepo
* @param subRepo
* @return
*/
public String getBasePath(String subRepo) {
return new StringBuffer(repoPath).append(File.separatorChar).append(subRepo).toString();
}
/**
* getting enable create directory property
* @param subRepo
* @return
*/
public boolean getCreateDirEnabled(String subRepo) {
return getConfiguration().getBoolean(subRepo + '.' + DirectoryService.KEY_CREATE_DIRECTORY_ENABLED);
}
/**
* getting enable private directory property
* @param subRepo
* @return
*/
public boolean getPrivateDirEnabled(String subRepo) {
return getConfiguration().getBoolean(subRepo+ '.' + DirectoryService.KEY_PRIVATE_ENABLED);
}
/**
* getting quota of subRepo
* @param subRepo
* @return 0 if no quota
*/
public long getQuota(String subRepo) {
return getConfiguration().getLong(subRepo+ '.' + DirectoryService.KEY_QUOTA);
}
public String getFileUrl(final String subRepo, final String id, final String fileName)
{
if (! isSubRepoEnabled(subRepo))
return null;
TemplateURI uri = new TemplateURI();
uri.setScreen(CTX_FILE_SERV);
uri.addPathInfo(CTX_REPO, subRepo);
uri.addPathInfo(CTX_ID, id);
uri.addPathInfo(CTX_FILE, fileName.replace('/', ','));
return uri.toString();
}
}
TurbineDirectory.java
package com.zamek.portal_zamek.services.directory;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import org.apache.commons.fileupload.FileItem;
import org.apache.turbine.services.TurbineServices;
import com.zamek.portal_zamek.PortalException;
import com.zamek.portal_zamek.util.FileInfo;
public class TurbineDirectory {
public static DirectoryService getService() {
return (DirectoryService) TurbineServices.getInstance().
getService(DirectoryService.SERVICE_NAME);
}
public static boolean isAvailable()
{
try
{
DirectoryService directory = getService();
return directory != null;
}
catch (Exception e) {
return false;
}
}
/**
* checking subrepo enabled in TR.properties
* @param subRepo
* @return
*/
public static boolean isSubRepoEnabled (final String subRepo)
{
return getService().isSubRepoEnabled(subRepo);
}
/**
* getting list of folders in given folder for FCKEditor.
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param includeMask default mask is *, if it's null
* @param excludeMask default mask is null
* @return list of directories in url directoy
* @throws FileNotFoundException
*/
public static List<String> getFolders (final String subRepo, final String id,
final String url, final String includeMask, final String excludeMask)
throws FileNotFoundException
{
return getService().getFolders(subRepo, id, url, includeMask, excludeMask);
}
/**
* getting list of files in given folder
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param url path url in own directory
* @param includeMask includeMask default mask is *, if it's null
* @param excludeMask excludeMask default mask is null
* @return list files in url directory
* @throws FileNotFoundException
*/
public static List<FileInfo> getFiles(final String subRepo, final String id,
final String url, final String includeMask, final String excludeMask)
throws FileNotFoundException
{
return getService().getFiles(subRepo, id, url, includeMask, excludeMask);
}
/**
* upload file to own directory using its original name
* You can set quota in TR.properties in
* services.directoryservice.<subrepo>.quota=nn
* quota is unlimited if it's 0
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param fileItem file to be uplode
* @return true if success
* @throws PortalException
* @throws Exception
*/
public static boolean uploadItemFile (final String subRepo, final String id,
final String path, final FileItem fileItem)
throws PortalException, Exception
{
return getService().uploadItemFile(subRepo, id, path, fileItem);
}
/**
* upload file to own directory using newName
* You can set quota in TR.properties in
* services.directoryservice.<subrepo>.quota=nn
* quota is unlimited if it's 0
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param path url in own directory
* @param fileItem file to be uplode
* @return true if success
* @throws PortalException
* @throws Exception
*/
public static boolean uploadItemFile (final String subRepo, final String id,
final String path, final FileItem fileItem, final String newName)
throws PortalException, Exception
{
return getService().uploadItemFile(subRepo, id, path, fileItem, newName);
}
/**
* checking to has private directory
* in TR.properties you can enable private directory:
* services.directoryservice.<subrepo>.private.enabled=true
* private directory is readable only for users, not to anonymous
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @return if private.enabled is true
*/
public static boolean hasPrivateDir (final String subRepo, final String id)
{
return getService().hasPrivateDir(subRepo, id);
}
/**
* get a file by url from repository
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param fileName name of file
* @return bytes of file
* @throws FileNotFoundException
* @throws IOException
* @throws PortalException
*/
public static ByteArrayOutputStream getFile (final String subRepo, final String id, final String fileName)
throws FileNotFoundException, IOException, PortalException
{
return getService().getFile(subRepo, id, fileName);
}
/**
* create a directory in repository if it's enabled by TR.properties
* services.directoryservice.<subrepo>.create-directory.enabled
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id entity id, root of entity
* @param folder folder to create
* @return
* 0: OK
* 101: Folder already exists error for FCKEditor
* 102: Invalid folder name error for FCKEditor
* 103: You have no permissions to create the folder error for FCKEDitor
* 110: Unknown error creating folder for FCKEditor
* @throws FileNotFoundException
*/
public static int createDirectory (final String subRepo, final String id, final String folder)
throws FileNotFoundException
{
return getService().createDirectory(subRepo, id, folder);
}
/**
* save string to repository to given name
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id id entity id, root of entity
* @param path url in own directory
* @param fileName name of file to save
* @param data data to save
* @throws IOException
*/
public static void saveToFile(final String subRepo, final String id,
final String path, final String fileName, final String data)
throws IOException, PortalException
{
getService().saveToFile(subRepo, id, path, fileName, data);
}
/**
* checking given file to exists
* @param subRepo subsystem path in repository forexample user, news, etc.
* @param id id entity id, root of entity
* @param path url in own directory
* @param fileName name of file to check
* @return true if exists
* @throws FileNotFoundException
*/
public static boolean isFileExists (final String subRepo, final String id,
final String path, final String fileName)
throws FileNotFoundException
{
return getService().isFileExists(subRepo, id, path, fileName);
}
public static boolean createIdDirectory(final String subRepo, final String id)
throws FileNotFoundException
{
return getService().createIdDirectory(subRepo, id);
}
/**
* getting real path of subrepo
* @param subRepo
* @return
*/
public static String getBasePath(String subRepo)
{
return getService().getBasePath(subRepo);
}
/**
* getting enable create directory property
* @param subRepo
* @return
*/
public static boolean getCreateDirEnabled(String subRepo)
{
return getService().getCreateDirEnabled(subRepo);
}
/**
* getting enable private directory property
* @param subRepo
* @return
*/
public static boolean getPrivateDirEnabled(String subRepo)
{
return getService().getPrivateDirEnabled(subRepo);
}
/**
* getting quota of subRepo
* @param subRepo
* @return 0 if no quota
*/
public long getQuota(String subRepo)
{
return getService().getQuota(subRepo);
}
/**
* return a link for a file
* @param subRepo
* @param id
* @param fileName
* @return
*/
public static String getFileUrl(final String subRepo, final String id, final String fileName)
{
return getService().getFileUrl(subRepo, id, fileName);
}
}
setting in TR.properties
In TR.properties, you can make different settings to different subrepositories. for example in this settings has a user's repository and a news module repository
services.DirectoryService.classname=com.zamek.portal_zamek.services.directory.TurbineDirectoryService services.DirectoryService.earlyInit=true services.DirectoryService.repository=resources # default repository root # properties of resources/user, this is the user's own repositories services.DirectoryService.user.quota=1024000 # 1Mb services.DirectoryService.user.create-directory.enabled=true # users can create subdirecories its own repository services.DirectoryService.user.private.enabled=true # users can make private directory to serve files only logged in users # properties of news modul in resources/news services.DirectoryService.news.quota=100000 # ~100Kb services.DirectoryService.news.create-directory.enabled=false #news cannott create directories services.DirectoryService.news.private.enabled=false #news cannot create private directory
Serving FCKEditor
Now we have to make an action to serve fckeditor. This is a simple VelocityAction descendants. There are following commands from FCKEditor:
GetFolders, need an xml response
GetFoldersAndFiles need an xml response
CreateFolder need an xml response
FileUpload need a string response
In different modules you can override two of methods:
boolean canCreateFolder() {
data.getUser().hasLoggedIn() && TurbineDirectory.getCreateDirEnabled(repo);
}
which is mean can create if user is has logged in and TR.properties enabled services.DirectoryService.<repo>.create-directory.enabled=true
boolean canUpload () {
return data.getUser().hasLoggedIn();
}
you can override it for example return super.canUpload() && data.getAcl().hasRole(SOME_ROLE);
All of FCKAction is here:
package com.zamek.portal_zamek.modules.actions;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.fileupload.FileItem;
import org.apache.turbine.util.RunData;
import org.apache.turbine.util.parser.ParameterParser;
import org.apache.turbine.util.uri.TurbineURI;
import org.apache.velocity.context.Context;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.zamek.portal_zamek.ZamekConst;
import com.zamek.portal_zamek.services.directory.DirectoryService;
import com.zamek.portal_zamek.services.directory.TurbineDirectory;
import com.zamek.portal_zamek.util.FileInfo;
/**
*
* @author zamek
* request: Command=GetFolders&Type=File&CurrentFolder=/Samples/Docs/
* response:
* <?xml version="1.0" encoding="utf-8" ?>
* <Connector command="GetFolders" resourceType="File">
* <CurrentFolder path="/Samples/Docs/" url="/UserFiles/File/Samples/Docs/" />
* <Folders>
* <Folder name="Documents" />
* <Folder name="Files" />
* <Folder name="Other Files" />
* <Folder name="Related" />
* </Folders>
* </Connector>
*
* request: Command=GetFoldersAndFiles&Type=File&CurrentFolder=/Samples/Docs/
* response:
* <?xml version="1.0" encoding="utf-8" ?>
* <Connector command="GetFoldersAndFiles" resourceType="File">
* <CurrentFolder path="/Samples/Docs/" url="/UserFiles/File/Samples/Docs/" />
* <Folders>
* <Folder name="Documents" />
* <Folder name="Files" />
* <Folder name="Other Files" />
* <Folder name="Related" />
* </Folders>
* <Files>
* <File name="XML Definition.doc" size="14" />
* <File name="Samples.txt" size="5" />
* <File name="Definition.txt" size="125" />
* <File name="External Resources.drw" size="840" />
* <File name="Todo.txt" size="2" />
* </Files>
* </Connector>
*
* request:Command=CreateFolder&Type=File&CurrentFolder=/Samples/Docs/&NewFolderName=FolderName
* response:
* <?xml version="1.0" encoding="utf-8" ?>
* <Connector command="CreateFolder" resourceType="File">
* <CurrentFolder path="/Samples/Docs/" url="/UserFiles/File/Samples/Docs/" />
* <Error number="0" />
* </Connector>
*
* request: Command=FileUpload&Type=File&CurrentFolder=/Samples/Docs/
* response: (Simple HTML):
* <script type="text/javascript">
* window.parent.frames['frmUpload'].OnUploadCompleted(0) ;
* </script>
* #OnUploadCompleted( 0 ) : no errors found on the upload process.
* #OnUploadCompleted( 201, 'FileName(1).ext' ) : the file has been uploaded successfully, but its name has been changed to "FileName(1).ext".
* #OnUploadCompleted( 202 ) : invalid file.
*
* Possible Error Numbers are:
* 0 : No Errors Found. The folder has been created.
* 101 : Folder already exists.
* 102 : Invalid folder name.
* 103 : You have no permissions to create the folder.
* 110 : Unknown error creating folder.
*/
public class FckAction extends AbstractAction {
private final static String LOGHEADER = "com.zamek.portal_zamek.modules.actions.";
protected final static String CTX_COMMAND = "Command";
protected final static String CTX_TYPE = "Type";
protected final static String CTX_CURRENT_FOLDER = "CurrentFolder";
protected final static String CTX_NEW_FOLDER = "NewFolderName";
protected final static String CTX_NEW_FILE = "NewFile";
protected final static String FT_FILE = "File";
protected final static String FT_IMAGE = "Image";
protected final static String FT_FLASH = "Flash";
protected final static String FT_MEDIA = "Media";
protected final static String CTX_FILE_SERV_ACTION = "FileServ";
private final static String CMD_GET_FOLDERS = "GetFolders";
private final static String CMD_GET_FOLDERS_AND_FILES = "GetFoldersAndFiles";
private final static String CMD_CREATE_FOLDER = "CreateFolder";
private final static String CMD_FILE_UPLOAD = "FileUpload";
protected final static String XML_CONNECTOR = "Connector";
protected final static String XML_COMMAND = "command";
protected final static String XML_CURRENT_FOLDER = "CurrentFolder";
protected final static String XML_PATH = "path";
protected final static String XML_URL = "url";
protected final static String XML_RESOURCE_TYPE = "resourceType";
protected final static String XML_FOLDERS = "Folders";
protected final static String XML_FOLDER = "Folder";
protected final static String XML_FILES = "Files";
protected final static String XML_FILE = "File";
protected final static String XML_NAME = "name";
protected final static String XML_SIZE = "size";
protected final static String XML_ERROR = "Error";
protected final static String XML_NUMBER = "Number";
protected final static int KILO_BYTE= 1024;
protected final static String CTX_RESPONSE="response";
protected final static int ERR_FOLDER_EXISTS = 101; // : Folder already exists.
protected final static int ERR_INVALID_FOLDER = 102; // : Invalid folder name.
protected final static int ERR_NO_PERMISSION = 103; // : You have no permissions to create the folder.
protected final static int ERR_UNKNOWN = 110; // : Unknown error creating folder.
protected final static int RES_UPLOAD_INVALID = 202;
protected final static int RES_UPLOAD_OK = 0;
protected final static int RES_UPLOAD_NAME_CHANGED = 201;
protected final static String UPLOAD_OK_RESULT =
"<script type=\"text/javascript\">window.parent.frames['frmUpload'].OnUploadCompleted(0,\"%s\");</script>";
protected final static String UPLOAD_ERROR_RESULT =
"<script type=\"text/javascript\">window.parent.frames['frmUpload'].OnUploadCompleted(%d);</script>";
protected String command = null,
type = null,
currentFolder = null,
scriptResult = null,
repo = null,
id = null;
protected RunData data;
protected Document document = null;
protected File serverDir = null;
protected Node rootNode = null;
public void doPerform(RunData data, Context ctx) throws Exception {
this.data = data;
data.declareDirectResponse();
data.setLayout(ZamekConst.DIRECT_RESPONSE_LAYOUT);
processRequest(data);
}
private void processRequest(RunData data) throws ServletException, IOException {
HttpServletResponse response = data.getResponse();
try {
ParameterParser pp = data.getParameters();
command = pp.get(CTX_COMMAND);
type = pp.get(CTX_TYPE);
currentFolder = pp.get(CTX_CURRENT_FOLDER);
repo = pp.get(DirectoryService.CTX_REPO);
id = pp.get(DirectoryService.CTX_ID);
if (command == null || command.length() == 0 || // possible abuse
repo == null || repo.length() == 0 ||
id == null || id.length() == 0)
return;
initDocument();
rootNode = createCommonXml();
doCommand();
if (rootNode == null) { // result of upload is a javascipt code
response.setContentType("text/html");
response.getOutputStream().print(scriptResult);
return;
}
response.setContentType("text/xml");
document.getDocumentElement().normalize();
try {
System.setProperty ( "javax.xml.transform.TransformerFactory" ,
"org.apache.xalan.xsltc.trax.TransformerFactoryImpl");
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new ByteArrayOutputStream ());
transformer.transform(source, result);
String xml = result.getOutputStream().toString();
response.getOutputStream().print(xml);
}
catch (Exception e) {
errorReport (data, LOGHEADER, e);
}
}
catch (Exception e) {
log.error(LOGHEADER+" error:"+e.getMessage());
}
}
protected void initDocument () throws ParserConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.newDocument();
}
protected Node createCommonXml () {
Element root = document.createElement(XML_CONNECTOR);
document.appendChild(root);
root.setAttribute(XML_COMMAND, command);
root.setAttribute(XML_RESOURCE_TYPE, type);
Element el = document.createElement(XML_CURRENT_FOLDER);
el.setAttribute(XML_PATH, currentFolder);
TemplateURI tu = new TemplateURI();
tu.setScreen(CTX_FILE_SERV_ACTION);
tu.addPathInfo(DirectoryService.CTX_REPO, repo);
tu.addPathInfo(DirectoryService.CTX_ID, id);
String fn = currentFolder.replace('/', ',');
tu.addPathInfo(DirectoryService.CTX_FILE, fn);
el.setAttribute(XML_URL, tu.toString());
root.appendChild(el);
return root;
}
protected void doCommand() throws Exception {
if (command.compareTo(CMD_GET_FOLDERS_AND_FILES) == 0) {
getFoldersAndFiles (rootNode);
return;
}
if (command.compareTo(CMD_GET_FOLDERS) == 0) {
getFolders(rootNode);
return;
}
if (command.compareTo(CMD_CREATE_FOLDER) == 0) {
createFolder (rootNode);
return;
}
if (command.compareTo(CMD_FILE_UPLOAD) == 0) {
scriptResult = fileUpload();
rootNode = null;
}
}
protected String getFileTypeMask() {
if (type.compareTo(FT_FILE) == 0)
return null;
if (type.compareTo(FT_IMAGE) == 0)
return ".*\\.gif|.*\\.jpg|.*\\.jpeg|.*\\.png";
if (type.compareTo(FT_FLASH) == 0)
return ".*\\.swf";
if (type.compareTo(FT_MEDIA) == 0)
return ".*\\.vmw|.*\\.mov";
return null;
}
protected void getFoldersAndFiles(Node root) throws Exception {
getFolders(root);
List<FileInfo> result = getFoldersAndFileList();
Element files = document.createElement(XML_FILES);
root.appendChild(files);
if (result != null && result.size() > 0) {
for (Iterator<FileInfo> it=result.iterator(); it.hasNext(); ) {
Element file = document.createElement(XML_FILE);
FileInfo fi = it.next();
files.appendChild(fi.setElement(file, KILO_BYTE));
}
}
}
protected void getFolders(Node root) throws Exception {
List<String> result = getFoldersList();
Element folders = document.createElement(XML_FOLDERS);
root.appendChild(folders);
if (result != null && result.size() > 0) {
for (Iterator<String> it=result.iterator(); it.hasNext(); ) {
Element subDir = document.createElement(XML_FOLDER);
subDir.setAttribute(XML_NAME, it.next());
folders.appendChild(subDir);
}
}
}
protected List<FileInfo> getFoldersAndFileList() throws Exception
{
return TurbineDirectory.getFiles(repo, id, currentFolder, getIncludeMask(), getExcludeMask());
}
protected List<String> getFoldersList() throws Exception
{
return TurbineDirectory.getFolders(repo, id, currentFolder, getIncludeMask(), getExcludeMask());
}
protected String getBaseUrl(RunData data) throws Exception
{
return repo+'/'+id;
}
protected int createFolder(Node root) throws Exception
{
String newFolder = data.getParameters().get(CTX_NEW_FOLDER);
int result = canCreateFolder() && newFolder != null
? TurbineDirectory.createDirectory(repo, id, newFolder)
: DirectoryService.ERR_NO_PERMISSION;
Element crf = document.createElement(XML_ERROR);
crf.setAttribute(XML_ERROR, Integer.toString(result));
root.appendChild(crf);
return result;
}
protected String fileUpload() throws Exception
{
int result = RES_UPLOAD_INVALID;
if (canUpload())
{
FileItem fItem = data.getParameters().getFileItem(CTX_NEW_FILE);
if (fItem != null)
{
try
{
if (TurbineDirectory.createIdDirectory(repo, id) &&
TurbineDirectory.uploadItemFile(repo, id, currentFolder, fItem))
return String.format(UPLOAD_OK_RESULT, fItem.getName());
}
catch (Exception e) {
log.error(LOGHEADER+"fileUpload error"+e.getMessage());
}
}
}
return String.format(UPLOAD_ERROR_RESULT, result);
}
/* overridable methods */
/**
* return rights to can create directory
* RunData is readable by protected data variable
*/
protected boolean canCreateFolder()
{
return data.getUser().hasLoggedIn() && TurbineDirectory.getCreateDirEnabled(repo);
}
/**
* return rights to can upload
* RunData is readable by protected data variable
*/
protected boolean canUpload () {
return data.getUser().hasLoggedIn();
}
/**
* getFolders() an getFolderAndFileList() using to set including name mask
* null is similar to *
* default value is null
* @return
*/
protected String getIncludeMask()
{
return null;
}
/**
* getFolders() an getFolderAndFileList() using to set excluding name mask
* null is similar to nothing
* default is null
* @return
*/
protected String getExcludeMask()
{
return null;
}
}
serving files to requests
We need to make a screen module to serve file requests. It is a descendants of RawScreen. We cannot use / as path separator in url because Turbine has a special url encoding method named path_info which is name/value format. We have to change / to , in url path parameters. Code of FileServ.java is:
package com.zamek.portal_zamek.modules.screens;
import java.io.ByteArrayOutputStream;
import javax.activation.MimetypesFileTypeMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.turbine.modules.screens.RawScreen;
import org.apache.turbine.util.RunData;
import com.zamek.portal_zamek.services.directory.DirectoryService;
import com.zamek.portal_zamek.services.directory.TurbineDirectory;
public class FileServ extends RawScreen {
private final static String LOGHEADER = "com.zamek.portal_zamek.modules.screens.FileServ.";
protected Log log = LogFactory.getLog(this.getClass());
@Override
protected void doOutput(RunData data) throws Exception {
if (log.isDebugEnabled())
log.debug(LOGHEADER + "doOutput, enter");
try {
String repo = data.getParameters().get(DirectoryService.CTX_REPO),
id = data.getParameters().get(DirectoryService.CTX_ID),
fileName = data.getParameters().get(DirectoryService.CTX_FILE);
if (repo == null || id == null || fileName == null)
return;
String fName = fileName.replace(',', '/'); // restore / in url
// private files only for hasloggedin users
if (fName.startsWith(DirectoryService.MASK_PRIVATE) &&
! data.getUser().hasLoggedIn() )
return;
ByteArrayOutputStream bos = TurbineDirectory.getFile(repo, id, fName);
if (bos == null)
return;
bos.writeTo(data.getResponse().getOutputStream());
}
catch (Exception e) {
log.error(LOGHEADER + "doOutput error:" + e.getMessage());
}
}
@Override
protected String getContentType(RunData data) {
String fileName = data.getParameters().get(DirectoryService.CTX_FILE);
if (StringUtils.isEmpty(fileName))
return null;
String ext = fileName.substring(fileName.lastIndexOf('.'));
return new MimetypesFileTypeMap().getContentType(ext);
}
}
setting FCKEditor in Velocity forms
The best way is to make two macros for setting FCKEditor:
#macro (declareFCK)
$page.addScript($content.getURI("scripts/FCKeditor/fckeditor.js"))
#end
which is initialize editor, and
#macro (textAreaFckField $name $value $height $toolbarSet $action $repo $id)
##pre("name:$name value:$value height:$height toolbarset:$toolbarSet action:$action, repo:$repo, id:$id")
<textarea id="$name" class="frminput" name="$name">$!value</textarea>
<script type="text/javascript">
#if ($height)
var oFCKeditor = new FCKeditor( '$name','100%','$height', 'Default', '') ;
#else
var oFCKeditor = new FCKeditor( '$name' ) ;
#end
oFCKeditor.BasePath = "$content.getURI('scripts/FCKeditor/')";
#if ($toolbarSet)
oFCKeditor.ToolbarSet = "$toolbarSet";
#else
oFCKeditor.ToolbarSet = "Default" ;
#end
#if ($action.length() != 0)
#set($connector=$link.setAction($action).addPathInfo("repo", $repo).addPathInfo("id",$id).toString())
oFCKeditor.Config.LinkBrowserURL = oFCKeditor.BasePath + "editor/filemanager/browser/default/browser.html?Connector=$connector";
oFCKeditor.Config.ImageBrowserURL = oFCKeditor.BasePath + "editor/filemanager/browser/default/browser.html?Connector=$connector/Type/Image";
oFCKeditor.Config.FlashBrowserURL = oFCKeditor.BasePath + "editor/filemanager/browser/default/browser.html?Connector=$connector/Type/Flash";
#else
oFCKeditor.Config.LinkBrowserURL = null;
oFCKeditor.Config.ImageBrowserURL = null;
oFCKeditor.Config.FlashBrowserURL = null;
#end
oFCKeditor.Config.AutoDetectLanguage = false ;
oFCKeditor.Config.DefaultLanguage = "hu" ;
oFCKeditor.ReplaceTextarea ();
</script>
#end
which is a form field for FCKEditor parameters are:
name: name of field
value: value of field
height: height of FCKEditor in pixels
toolbarset: there are different toolbarset, "" will be "Default" set
action: name of action to handle FCKEditor requests, default is FckAction
repo: name of sub repository, for example user. It is mapping under resources/ directory ( services.DirectoryService.repository in TR.props)
id: id of identity in repo. for example user's id. It will mapping under resources/user/42 if user's id is 42
Let's see a form example:
#headerTitle("User's data")
$data.setLayoutTemplate("Default.vm")
#declareFCK()
<table width="100%" border="0">
...
<form name="userreg" method="post" action='$link.setAction("UserReg").toString()'>
...
#textAreaFckField("intro" $showUser.Intro 500 "Default" "UserFile" "user" $showUser.UserId)
...
</form>
...
</table>
So, it works, but you need some utilities to complete First is FileInfo class:
package com.zamek.portal_zamek.util;
import org.w3c.dom.Element;
/**
* simple class to serve file informations for TurbineirectoryService
* @author zamek
*
*/
public class FileInfo {
private String name;
private long size;
public final static String XML_NAME = "name";
public final static String XML_SIZE = "size";
public FileInfo (String name, long size) {
this.name = name;
this.size = size;
}
public String getName () {
return name;
}
public long getSize () {
return size;
}
public Element setElement (Element element, long divider) {
if (element == null)
return null;
element.setAttribute(XML_NAME, name);
element.setAttribute(XML_SIZE,
Long.toString(
divider > 1 ? size / divider : size));
return element;
}
}
and the last (I promice!) class is FileUtil:
package com.zamek.portal_zamek.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Vector;
import org.apache.commons.fileupload.FileItem;
import com.zamek.portal_zamek.PortalException;
import com.zamek.portal_zamek.ZamekConst;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author zamek Created 2004.05.07.16:18:41
* @version $Id: FileUtils.java,v 1.2 2006/02/07 15:25:12 zamek Exp $
*/
public class FileUtils implements ZamekConst {
private final static String LOGHEADER = "com.zamek.portal_zamek.util.FileUtils.";
protected static Log log = LogFactory.getLog(FileUtils.class);
public final static int CMD_OK_EXITVALUE = 0;
public final static String BIN_DIRECTORY = "/bin/";
protected final static int MAX_BUFFER_SIZE = 1024;
/**
* Execute a system shell command
*
* @param path
* path of command
* @param command
* command
* @param params
* parameters of command
* @throws PortalException
* @throws IOException
* @throws InterruptedException
*/
public static int executeShell(final String path, final String command, final String[] params)
throws PortalException, IOException, InterruptedException {
if (command == null || command.length() == 0)
throw new PortalException (MSG_EMPTY_COMMAND);
StringBuffer cmd = new StringBuffer(pathEnd(path));
cmd.append(command);
if (params != null && params.length > 0)
for (int i = 0; i < params.length; i++)
cmd.append(" ").append(params[i]);
Process process = Runtime.getRuntime().exec(cmd.toString());
process.waitFor();
if (process.exitValue() != CMD_OK_EXITVALUE)
throw new PortalException(MSG_ERROR_EXIT + process.exitValue());
return process.exitValue();
}
/**
* Make a directory from path. Path must be exists!
*
* @param path
* parent path
* @param newDir
* name of new directory
* @throws PortalException
*/
public static String makeDir(String path, final String newDir)
throws PortalException {
File file = new File(path);
if (! file.isDirectory() || ! file.exists() )
throw new PortalException(MSG_PARENT_NOTEXISTS + path);
path = pathEnd(path);
String dirName = path + newDir;
file = new File(dirName);
if (file.exists())
return dirName + "/";
file.mkdir();
return dirName + "/";
}
/**
* Make a named directory
* @param newDir path and name of new directory
*/
public static void makeDir (final String newDir) {
File file = new File (newDir);
if (file.exists())
return;
file.mkdir();
}
/**
* getting size of file or directory
* @param file File object representing a file or Directory
* @return size of file or Directory
*/
public static long getSize (File file) {
if (file == null)
return 0;
if (file.isFile())
return file.length();
File[] files = file.listFiles();
if (files == null)
return 0;
long size = 0;
for (int i=0; i<files.length; i++)
size += getSize (files[i]);
return size;
}
/**
* getting size of file or Directory
* @param name String name of file or directory
* @return size of file or directory
*/
public static long getSize(final String name) {
File file = new File(name);
return getSize (file);
}
/**
* Remove directory
*
* @param dir
* name of deleting directory
* @param force
* force delete if not empty
* @throws PortalException
* @throws IOException
*/
public static void removeDir(final String dir, boolean force)
throws PortalException, IOException {
File file = new File(dir);
if (!file.isDirectory())
throw new PortalException (MSG_DIR_NOT_EXISTS);
File[] files = file.listFiles();
if (!force && files.length > 0)
throw new PortalException(MSG_DIR_NOT_EMPTY);
if (force && files.length > 0) {
for (int i = 0; i < files.length; i++)
files[i].delete();
}
file.delete();
}
/**
* delete directory if it is empty
*
* @param dir
* name of deleteing directory
* @throws PortalException
* @throws IOException
*/
public void removeDir(final String dir) throws PortalException, IOException {
removeDir(dir, false);
}
/**
* append pathSeparator after path if it is not exists
*
* @param path
* @return
*/
public static String pathEnd(final String path) {
return path.charAt(path.length() - 1) == File.separatorChar ? path
: path + File.separatorChar;
}
/**
* return less long value
*
* @param a
* @param b
* @return
*/
public static long min(long a, long b) {
return a < b ? a : b;
}
/**
* return greates long value
*
* @param a
* @param b
* @return
*/
public static long max(long a, long b) {
return a > b ? a : b;
}
/**
* return less int value
*
* @param a
* @param b
* @return
*/
public static int min(int a, int b) {
return a < b ? a : b;
}
/**
* return greatest int value
*
* @param a
* @param b
* @return
*/
public static int max(int a, int b) {
return a > b ? a : b;
}
/**
* delete file
*
* @param name
* @throws PortalException
* @throws IOException
*/
public static void deleteFile(final String name) throws PortalException,
IOException {
File file = new File(name);
if (!file.exists())
throw new PortalException(MSG_FILE_NOT_EXISTS + name);
file.delete();
}
/**
* secure file delete. It must be setting separate path and filename.
* remove beginning separatorChar from name. (it cannot be delete from /)
* @param path
* @param file
* @throws PortalException
* @throws IOException
*/
public static void deleteFile(String path, String file) throws PortalException, IOException {
path = pathEnd(path);
while (file.charAt(0) == File.separatorChar)
file = file.substring(1);
File f = new File (new StringBuffer(path).append(file).toString());
if (!f.exists())
throw new PortalException(new StringBuffer (MSG_FILE_NOT_EXISTS).append(f.getName()).toString());
f.delete();
}
/**
* copy file.
* file másolása. Ha targetCheck true és létezik a cél file,
* PortalException-t generál
*
* @param source
* source (include path)
* @param dest
* target (include path)
* @param targetCheck
* checking target before copy.
* Throws an PortalException if it is true and target is exists
* @throws PortalException
* @throws IOException
*/
public static void copyFile(final String source, final String dest, boolean targetCheck)
throws PortalException, IOException {
if (log.isDebugEnabled())
log.debug(LOGHEADER + "copyFile source:"+source+", dest:"+dest);
File src = new File(source);
if (!src.exists())
throw new PortalException(MSG_FILE_NOT_EXISTS + source);
File dst = new File(dest);
if (targetCheck && dst.exists())
throw new PortalException(MSG_TARGET_EXISTS + dest);
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dst);
byte[] buffer = new byte[min((int) src.length(), MAX_BUFFER_SIZE)];
while (fis.read(buffer) != -1)
fos.write(buffer);
fis.close();
fos.close();
}
/**
* copy file. It doesn't check exists of target
*
* @param source
* source (include path)
* @param dest
* target (include path)
* @throws PortalException
* @throws IOException
*/
public static void copyFile(final String source, final String dest)
throws PortalException, IOException {
copyFile(source, dest, false);
}
/**
* move file.
* file mozgatása. Ha targetCheck true és létezik a cél file,
* PortalException-t generál
*
* @param source
* source (include path)
* @param dest
* target (include path)
* @param targetCheck
* checking target before copy.
* Throws an PortalException if it is true and target is exists
* @throws PortalException
* @throws IOException
*/
public static void moveFile(final String source, final String dest, boolean targetCheck)
throws PortalException, IOException {
copyFile(source, dest, targetCheck);
deleteFile(source);
}
/**
* move file. it doesn't check exists of target
*
* @param source
* source (include path)
* @param dest
* target (include path)
* @throws PortalException
* @throws IOException
*/
public static void moveFile(String source, String dest)
throws PortalException, IOException {
moveFile(source, dest, false);
}
/**
* copy uploaded file to toDir with newName.
* @param fileItem
* upload fileItem
* @param toDir
* target directory
* @param newName
* name of new fileName
* @throws PortalException
* if fileItem == null || fielItem.size() == 0
* @throws Exception
*/
public static String uploadFile(FileItem fileItem, final String toDir,
final String newName) throws PortalException, Exception {
if (log.isDebugEnabled())
log.debug(LOGHEADER + "uploadFile enter, todir:"+toDir+
",newname:"+newName);
if (fileItem == null || fileItem.getSize() == 0)
throw new PortalException(MSG_FILE_NULL_OR_EMPTY);
StringBuffer sb = new StringBuffer(pathEnd(toDir)).append(newName);
fileItem.write(new File(sb.toString()));
return sb.toString();
}
/**
* separate filename into path, name, ext
*
* @param source
* input filaname
* @return [0] : path, [1]: name, [2]: ext
*/
public static String[] stripFileName(final String source) {
String[] res = new String[3];
int extPos = source.lastIndexOf('.');
res[2] = extPos > 0 ? source.substring(extPos) : EMPTY_STR;
String src = source.substring(0, extPos);
int namePos = source.lastIndexOf(File.separatorChar);
if (namePos >= 0) {
res[1] = src.substring(++namePos);
res[0] = src.substring(0, namePos);
}
else {
res[0] = EMPTY_STR;
res[1] = EMPTY_STR;
}
return res;
}
/**
* get extent of filename
* @param name
* @return
*/
public static String getFileExt(final String name) {
int extPos = name.lastIndexOf('.');
return extPos > 0 ? name.substring(extPos) : EMPTY_STR;
}
/**
* extract name from absolutePath+fileName
* @param fileName
* @return
*/
public static String getFileName(final String fileName) {
if (StringUtils.isEmpty(fileName))
return fileName;
int namePos = fileName.lastIndexOf(File.pathSeparator);
return namePos == -1
? fileName
: fileName.substring(namePos);
}
/**
* list content of directory. It can filter with optional parameters
* @param dir
* listing directory. It check exists, readable, directory check
* @param mask
* Optional filter mask. It masks with String.matches()
* @return list of files
*/
public static List<String> getFilesInDirectory(final String dir, final String mask) {
String[] files;
Vector<String> result = new Vector<String>();
File directory = new File(dir);
if (directory.exists() && directory.canRead()
&& directory.isDirectory()) {
files = directory.list();
if (mask != null && mask.length() > 0) {
for (int i = 0; i < files.length; i++) {
String fName = files[i];
if (fName.matches(mask))
result.add(fName);
}
}
else {
for (int i = 0; i < files.length; i++)
result.add(files[i]);
}
return result;
}
return null;
}
public static List<String> getDirectoryNames (final String dir,
final String includeMask, final String excludeMask) {
String[] files;
Vector<String> result = new Vector<String>();
File directory = new File(dir);
if (directory.exists() && directory.canRead()
&& directory.isDirectory()) {
files = directory.list();
for (int i=0; i < files.length; i++) {
String cName = files[i];
File subDir = new File (dir + File.separatorChar + cName);
if (! subDir.isDirectory())
continue;
if (includeMask == null && excludeMask == null) {
result.add(cName);
continue;
}
if (excludeMask != null && cName.matches(excludeMask))
continue;
if (includeMask != null && cName.matches(includeMask))
result.add(cName);
}
return result;
}
return null;
}
public static List<FileInfo> getFileNames (final String dir,
final String includeMask, final String excludeMask) {
String[] files;
Vector<FileInfo> result = new Vector<FileInfo>();
File directory = new File(dir);
if (directory.exists() && directory.canRead()
&& directory.isDirectory()) {
files = directory.list();
for (int i=0; i < files.length; i++) {
String cName = files[i];
File file = new File (dir + File.separatorChar + cName);
if (! file.isFile())
continue;
if (includeMask == null && excludeMask == null) {
result.add(new FileInfo(cName, file.length()));
continue;
}
if (excludeMask != null && cName.matches(excludeMask))
continue;
if ( (includeMask == null) || (includeMask != null && cName.matches(includeMask)) ) {
result.add(new FileInfo(cName, file.length()));
}
}
return result;
}
return null;
}
/**
* list content of directory. It can filter with optional parameters
* @param dir
* listing directory. It check exists, readable, directory check
* @param includeMask
* Optional filter mask. It masks with String.matches()
* @param excludeMask
* Optional filter mask. It masks with String.matches()
* @return list of files
*/
public static List getFilesInDirectory(final String dir, final String includeMask, final String excludeMask) {
String[] files;
Vector<String> result = new Vector<String>();
File directory = new File(dir);
boolean chkInclude = includeMask != null && includeMask.length() > 0,
chkExclude = excludeMask != null && excludeMask.length() > 0;
if (directory.exists() && directory.canRead()
&& directory.isDirectory()) {
files = directory.list();
for (int i = 0; i < files.length; i++) {
String fName = files[i];
if ( (chkExclude && (! fName.matches(excludeMask)) &&
chkInclude && fName.matches(includeMask)))
result.add(fName);
else
if (chkInclude && fName.matches(includeMask))
result.add(fName);
else
result.add(fName);
}
return result;
}
return null;
}
/**
* checks end of name with needExtent
* @param name
* @param needExtent
* @return
*/
public static boolean checkExtent (final String name, final String needExtent) {
return needExtent != null && name != null &&
name.length() >= needExtent.length() &&
name.endsWith(needExtent);
}
final static char BACK_SLASH = '\\',
SLASH = '/',
COLON = ':',
SPACE = ' ',
UNDER_LINE = '_';
final static char [] INTERNATIONALS = {'á','í','ű','ő','ü','ö','ú','ó','é',
'Á','Í','Ű','Ő','Ü','Ö','Ú','Ó','É',
'?','?','?','?'};
final static char[] ASCII = {'a','i','u','o','u','o','u','o','e',
'A','I','U','O','U','O','U','O','E',
'u','o','U','O'};
/**
* Normalize file name.
* replace ':' to '_', '\' to '/',
* international chars to ascii representant or '_'
*
* @param s
* @return normalized filename
*/
public static String normalizeFileName(String s) {
if (StringUtils.isEmpty(s))
return s;
if (s.indexOf(BACK_SLASH) > 0 && s.indexOf(COLON) > 0)
s = processWinFileName (s);
s = s.replace(SPACE, UNDER_LINE);
return changeInternationalChars(s);
}
private static String processWinFileName(String s) {
s= s.replace(COLON, UNDER_LINE);
return s.replace(BACK_SLASH, SLASH);
}
private static String changeInternationalChars(String s) {
for (short i=0; i<s.length(); i++) {
char at = s.charAt(i);
if (at > 127) {
for (short j=0; j<INTERNATIONALS.length; j++) {
if (at == INTERNATIONALS[j]) {
s = s.replace(at, ASCII [j]);
break;
}
}
s = s.replace(at, UNDER_LINE);
}
}
return s;
}
}