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 'T'urbine'D'irectory 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 'V'elocity'A'ction descendants. There are following commands from FCKEditor:
- 'G'et'F'olders, need an xml response
- 'G'et'F'olders'A'nd'F'iles need an xml response
- 'C'reate'F'older need an xml response
- 'F'ile'U'pload 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.'D'irectory'S'ervice.<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 'FC'KA'*_ction 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_* 'R'aw'S'creen. 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 'F'ile'S'*_erv.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_* 'FC'KE'ditor in pixels
- toolbarset: there are different toolbarset, "" will be "Default" set
- action: name of action to handle 'FC'KE'ditor requests, default is 'F'ck'A'*_ction
- 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_* 'F'ile'I'*_nfo 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_* 'F'ile'U_'_'til:
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 Math.min(a, b); } /** * return greates long value * * @param a * @param b * @return */ public static long max(long a, long b) { return Math.max(a, b); } /** * return less int value * * @param a * @param b * @return */ public static int min(int a, int b) { return Math.min(a, b); } /** * return greatest int value * * @param a * @param b * @return */ public static int max(int a, int b) { return Math.max(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[Math.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; } }