0001: /*
0002: * argun 1.0
0003: * Web 2.0 delivery framework
0004: * Copyright (C) 2007 Hammurapi Group
0005: *
0006: * This program is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation; either
0009: * version 2 of the License, or (at your option) any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU Lesser General Public
0017: * License along with this library; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0019: *
0020: * URL: http://www.hammurapi.biz
0021: * e-Mail: support@hammurapi.biz
0022: */
0023: package biz.hammurapi.web;
0024:
0025: import java.io.ByteArrayOutputStream;
0026: import java.io.IOException;
0027: import java.io.InputStream;
0028: import java.security.NoSuchAlgorithmException;
0029: import java.sql.PreparedStatement;
0030: import java.sql.ResultSet;
0031: import java.sql.SQLException;
0032: import java.text.MessageFormat;
0033: import java.util.ArrayList;
0034: import java.util.Collection;
0035: import java.util.Date;
0036: import java.util.Enumeration;
0037: import java.util.HashMap;
0038: import java.util.Iterator;
0039: import java.util.Map;
0040:
0041: import javax.servlet.ServletOutputStream;
0042: import javax.servlet.http.HttpServletRequest;
0043: import javax.servlet.http.HttpServletResponse;
0044:
0045: import org.apache.commons.codec.digest.DigestUtils;
0046: import org.apache.commons.fileupload.FileItemIterator;
0047: import org.apache.commons.fileupload.FileItemStream;
0048: import org.apache.commons.fileupload.FileUploadException;
0049: import org.apache.commons.fileupload.servlet.ServletFileUpload;
0050: import org.apache.commons.fileupload.util.Streams;
0051: import org.apache.log4j.Logger;
0052: import org.w3c.dom.Element;
0053:
0054: import biz.hammurapi.config.ConfigurationException;
0055: import biz.hammurapi.config.Context;
0056: import biz.hammurapi.config.MapContext;
0057: import biz.hammurapi.config.MutableContext;
0058: import biz.hammurapi.config.PropertyParser;
0059: import biz.hammurapi.config.SimpleContext;
0060: import biz.hammurapi.sql.DatabaseObject;
0061: import biz.hammurapi.sql.Parameterizer;
0062: import biz.hammurapi.sql.RowProcessorEx;
0063: import biz.hammurapi.sql.SQLExceptionEx;
0064: import biz.hammurapi.sql.SQLProcessor;
0065: import biz.hammurapi.sql.columns.Column;
0066: import biz.hammurapi.web.eval.EvaluationResult;
0067: import biz.hammurapi.web.eval.EvaluatorFactory;
0068: import biz.hammurapi.web.file.sql.DbFileImpl;
0069: import biz.hammurapi.web.file.sql.DbFileOwnerType;
0070: import biz.hammurapi.web.file.sql.DbFileType;
0071: import biz.hammurapi.web.file.sql.DbFileTypeAction;
0072: import biz.hammurapi.web.file.sql.DbFileTypeActionImpl;
0073: import biz.hammurapi.web.file.sql.FileEngine;
0074: import biz.hammurapi.web.file.sql.FileInfo;
0075: import biz.hammurapi.web.file.sql.FileInfoImpl;
0076: import biz.hammurapi.web.menu.Menu;
0077: import biz.hammurapi.web.menu.MenuActions;
0078: import biz.hammurapi.web.util.Escaper;
0079: import biz.hammurapi.web.util.GuidGenerator;
0080: import biz.hammurapi.xml.dom.AbstractDomObject;
0081: import biz.hammurapi.xml.dom.CompositeDomSerializer;
0082: import biz.hammurapi.xml.dom.DomSerializable;
0083:
0084: /**
0085: * Performs file operations
0086: * @author Pavel Vlasov
0087: */
0088: public class FileActions extends ActionsBase {
0089: public static final String ACTION_ID = "actionId";
0090: public static final String ROOT_CONTENT_TYPE = "application/octet-stream";
0091: private static final int MAX_FILE_SIZE = 50 * 1024 * 1024; // 50 Mb
0092:
0093: /**
0094: * Parameters and referrers of file actions are stored in session under keys ACTION_INFO_PREFIX<invocation id;
0095: * Info is an object array with first element being a map of parameters and the second being referrer if any
0096: */
0097: public static final String ACTION_INFO_PREFIX = FileActions.class
0098: .getName()
0099: + ":info:";
0100:
0101: /**
0102: * Action counter is incremented every time action is performed. It is used for keying action parameters into session attributed
0103: */
0104: private long actionCounter;
0105:
0106: private static final Logger logger = Logger
0107: .getLogger(FileActions.class);
0108:
0109: protected static FileEngine getEngine(HttpServletRequest request) {
0110: return new FileEngine((SQLProcessor) getGlobal(request,
0111: "sql-processor"));
0112: }
0113:
0114: /**
0115: * Retrieves content of the file.
0116: * @param request
0117: * @param response
0118: * @throws IOException
0119: * @throws SQLException
0120: */
0121: public void download(HttpServletRequest request,
0122: final HttpServletResponse response, ActionServlet servlet,
0123: String path) throws IOException, SQLException {
0124: retrieve(request, response, true, path);
0125: }
0126:
0127: /**
0128: * Retrieves content of the file.
0129: * @param request
0130: * @param response
0131: * @throws IOException
0132: * @throws SQLException
0133: */
0134: public void get(HttpServletRequest request,
0135: final HttpServletResponse response, ActionServlet servlet,
0136: String path) throws IOException, SQLException {
0137: retrieve(request, response, false, path);
0138: }
0139:
0140: /**
0141: * Retrieves content of the file.
0142: * @param request
0143: * @param response
0144: * @throws IOException
0145: * @throws SQLException
0146: */
0147: private void retrieve(HttpServletRequest request,
0148: final HttpServletResponse response,
0149: final boolean setFileName, String path) throws IOException,
0150: SQLException {
0151: final String fileId;
0152: if (isBlank(path)) {
0153: fileId = request.getParameter("ID");
0154: } else {
0155: int idx = path.indexOf("/");
0156: if (idx == -1) {
0157: response.getWriter().write("Invalid file path");
0158: return;
0159: }
0160: fileId = path.substring(0, idx);
0161: }
0162:
0163: if (fileId == null) {
0164: response.getWriter().write("File ID parameter is missing");
0165: return;
0166: }
0167:
0168: SQLProcessor processor = (SQLProcessor) getGlobal(request,
0169: "sql-processor");
0170: processor.processSelect("SELECT * FROM DB_FILE WHERE ID=?",
0171: new Parameterizer() {
0172:
0173: public void parameterize(PreparedStatement ps)
0174: throws SQLException {
0175: ps.setInt(1, Integer.parseInt(fileId));
0176: }
0177:
0178: }, new RowProcessorEx() {
0179:
0180: public void onEmptyResultSet() throws SQLException {
0181: try {
0182: response.getWriter().write(
0183: "File " + fileId + " not found");
0184: } catch (IOException e) {
0185: throw new SQLExceptionEx(e);
0186: }
0187:
0188: }
0189:
0190: public boolean process(ResultSet rs)
0191: throws SQLException {
0192: response.setContentType(rs
0193: .getString("CONTENT_TYPE"));
0194: response.setContentLength(rs
0195: .getInt("FILE_SIZE"));
0196: if (setFileName) {
0197: response.setHeader("Content-Disposition",
0198: "attachment; filename=\""
0199: + rs.getString("NAME")
0200: + "\"");
0201: }
0202: try {
0203: InputStream in = rs
0204: .getBinaryStream("FILE_CONTENT");
0205: ServletOutputStream out = response
0206: .getOutputStream();
0207: byte[] buf = new byte[4096];
0208: int l;
0209: while ((l = in.read(buf)) != -1) {
0210: out.write(buf, 0, l);
0211: }
0212: in.close();
0213: out.close();
0214: } catch (IOException e) {
0215: throw new SQLExceptionEx(
0216: "Could not retrieve file content: "
0217: + e, e);
0218: }
0219: return true;
0220: }
0221:
0222: });
0223: }
0224:
0225: /**
0226: * Uploads file content to database (update).
0227: * @param request
0228: * @param response
0229: * @throws IOException
0230: * @throws SQLException
0231: * @throws ConfigurationException
0232: * @throws FileUploadException
0233: * @throws HammurapiWebException
0234: * @throws NoSuchAlgorithmException
0235: */
0236: public void update(HttpServletRequest request,
0237: final HttpServletResponse response) throws Exception {
0238: boolean isMultipart = ServletFileUpload
0239: .isMultipartContent(request);
0240: SimpleContext mc = new SimpleContext();
0241: FileEngine engine = getEngine(request);
0242:
0243: DbFileImpl dbFile = new DbFileImpl(false) {
0244: public void copy(DatabaseObject source) {
0245: super .copy(source);
0246:
0247: // Clean modified flags.
0248: Iterator it = columns.iterator();
0249: while (it.hasNext()) {
0250: ((Column) it.next()).clearModified();
0251: }
0252: }
0253: };
0254:
0255: String referrer = getReferrer(request);
0256:
0257: if (isMultipart) {
0258: ServletFileUpload upload = new ServletFileUpload();
0259:
0260: FileItemIterator iter = upload.getItemIterator(request);
0261: while (iter.hasNext()) {
0262: FileItemStream item = iter.next();
0263: String name = item.getFieldName();
0264: if (item.isFormField()) {
0265: String value = Streams.asString(item.openStream());
0266: mc.set(name, value);
0267: if (ACTION_ID.equals(name)) {
0268: Object[] info = (Object[]) request.getSession()
0269: .getAttribute(
0270: ACTION_INFO_PREFIX + value);
0271: if (info != null) {
0272: if (info[1] != null) {
0273: Iterator it = ((Map) info[0])
0274: .entrySet().iterator();
0275: while (it.hasNext()) {
0276: Map.Entry entry = (Map.Entry) it
0277: .next();
0278: mc.set((String) entry.getKey(),
0279: entry.getValue());
0280: }
0281: }
0282: if (info[1] != null) {
0283: referrer = (String) info[1];
0284: }
0285: }
0286: }
0287: } else if ("CONTENT_PART".equals(name)) { // Form fields has been taken care of above.
0288: // Update file name as well.
0289: String fileName = item.getName();
0290: int idx = fileName.lastIndexOf("/");
0291: idx = Math.max(idx, fileName.lastIndexOf("\\"));
0292: mc.set("NAME",
0293: idx == fileName.length() - 1 ? fileName
0294: : fileName.substring(idx + 1));
0295:
0296: InputStream in = item.openStream();
0297: ByteArrayOutputStream out = new ByteArrayOutputStream(); // Lazy approach. Might have to be changed to upload big files.
0298: int length = 0;
0299: byte[] buf = new byte[4096];
0300: int l;
0301: while ((l = in.read(buf)) != -1) {
0302: out.write(buf, 0, l);
0303: length += l;
0304: if (length > MAX_FILE_SIZE) {
0305: throw new IOException("File is too big");
0306: }
0307: }
0308: in.close();
0309: out.close();
0310:
0311: mc.set("FILE_SIZE", new Long(length));
0312: byte[] bytes = out.toByteArray();
0313: mc.set("FILE_CONTENT", bytes);
0314: mc.set("DIGEST", DigestUtils.shaHex(bytes));
0315: mc.set("DIGEST_ALGORITHM", "SHA");
0316: if (item.getContentType() != null) {
0317: mc.set("CONTENT_TYPE", item.getContentType());
0318: }
0319: }
0320: }
0321:
0322: // Update fields
0323: String fileId = (String) mc.get("ID");
0324: if (fileId == null) {
0325: response.getWriter().write("ID parameter is missing");
0326: return;
0327: }
0328: FileInfo fileInfo = engine.getFileInfo(Integer
0329: .parseInt(fileId));
0330: if (fileInfo == null) {
0331: response.getWriter()
0332: .write("Invalid file ID: " + fileId);
0333: return;
0334: }
0335: dbFile.copy((DatabaseObject) fileInfo);
0336: dbFile.configure(mc, converter);
0337: if (dbFile.getContentType() == null) {
0338: dbFile.setContentType(ROOT_CONTENT_TYPE);
0339: }
0340: if (referrer == null) {
0341: referrer = (String) mc.get("_referrer");
0342: }
0343: } else { // Try to read text from fileText field
0344: String fileId = request.getParameter("ID");
0345: if (fileId == null) {
0346: response.getWriter().write("ID parameter is missing");
0347: return;
0348: }
0349: FileInfo fileInfo = engine.getFileInfo(Integer
0350: .parseInt(fileId));
0351:
0352: if (fileInfo == null) {
0353: response.getWriter()
0354: .write("Invalid file ID: " + fileId);
0355: return;
0356: }
0357:
0358: dbFile.copy((DatabaseObject) fileInfo);
0359:
0360: dbFile.configure(new RequestParametersContext(request),
0361: converter);
0362:
0363: String actionId = request.getParameter(ACTION_ID);
0364: if (actionId != null) {
0365: Object[] info = (Object[]) request.getSession()
0366: .getAttribute(ACTION_INFO_PREFIX + actionId);
0367: if (info != null) {
0368: dbFile.configure(new MapContext((Map) info[0]),
0369: converter);
0370: if (info[1] != null) {
0371: referrer = (String) info[1];
0372: }
0373: }
0374: }
0375:
0376: String text = request.getParameter("fileText");
0377: if (text != null) {
0378: text = MenuActions.normalizeLineSeparator(text);
0379: byte[] textBytes = text.getBytes();
0380: dbFile.setFileSize(new Long(textBytes.length));
0381: dbFile.setFileContent(textBytes);
0382: dbFile.setDigest(DigestUtils.shaHex(dbFile
0383: .getFileContent()));
0384: dbFile.setDigestAlgorithm("SHA");
0385: if (dbFile.getContentType() == null) {
0386: dbFile.setContentType("text/plain");
0387: }
0388: }
0389: }
0390:
0391: GuidGenerator guidGenerator = (GuidGenerator) getGlobal(
0392: request, "db/guid-generator");
0393: if (guidGenerator != null && isBlank(dbFile.getGuid())) {
0394: dbFile.setGuid(guidGenerator.nextGUID());
0395: }
0396:
0397: if (dbFile.isModified()) {
0398: dbFile.setLastModified(System.currentTimeMillis());
0399: engine.updateDbFile(dbFile);
0400: }
0401:
0402: if (referrer == null) {
0403: StringBuffer rb = new StringBuffer(
0404: (String) new RequestContext(request)
0405: .get("context-path"));
0406: rb
0407: .append("/ef/xmenu/system/FileActions/getInfo/file/list.html");
0408: if (dbFile.getParent() != null) {
0409: rb.append("?ID=");
0410: rb.append(dbFile.getParent());
0411: } else if (dbFile.getOwnerId() != null
0412: && dbFile.getOwnerType() != null) {
0413: rb.append("?OWNER_TYPE=");
0414: rb.append(dbFile.getOwnerType());
0415: rb.append("&OWNER_ID=");
0416: rb.append(dbFile.getOwnerId());
0417: }
0418: referrer = rb.toString();
0419: }
0420:
0421: response.sendRedirect(referrer);
0422: }
0423:
0424: /**
0425: * Uploads file content to database (update).
0426: * @param request
0427: * @param response
0428: * @throws IOException
0429: * @throws SQLException
0430: * @throws ConfigurationException
0431: * @throws FileUploadException
0432: * @throws HammurapiWebException
0433: */
0434: public void create(HttpServletRequest request,
0435: final HttpServletResponse response) throws IOException,
0436: SQLException, ConfigurationException, FileUploadException,
0437: HammurapiWebException {
0438: boolean isMultipart = ServletFileUpload
0439: .isMultipartContent(request);
0440: String referrer = getReferrer(request);
0441:
0442: // Read fields
0443: DbFileImpl dbFile = new DbFileImpl(true);
0444: if (isMultipart) {
0445: ServletFileUpload upload = new ServletFileUpload();
0446: MutableContext mc = new SimpleContext();
0447: FileItemIterator iter = upload.getItemIterator(request);
0448: while (iter.hasNext()) {
0449: FileItemStream item = iter.next();
0450: String name = item.getFieldName();
0451: if (item.isFormField()) {
0452: String value = Streams.asString(item.openStream());
0453: mc.set(name, value);
0454: if (ACTION_ID.equals(name)) {
0455: Object[] info = (Object[]) request.getSession()
0456: .getAttribute(
0457: ACTION_INFO_PREFIX + value);
0458: if (info != null) {
0459: Iterator it = ((Map) info[0]).entrySet()
0460: .iterator();
0461: while (it.hasNext()) {
0462: Map.Entry entry = (Map.Entry) it.next();
0463: mc.set((String) entry.getKey(), entry
0464: .getValue());
0465: }
0466: if (info[1] != null) {
0467: referrer = (String) info[1];
0468: }
0469: }
0470: }
0471: } else if ("CONTENT_PART".equals(name)) { // Form fields has been taken care of above.
0472: String fileName = item.getName();
0473: int idx = fileName.lastIndexOf("/");
0474: idx = Math.max(idx, fileName.lastIndexOf("\\"));
0475: dbFile
0476: .setName(idx == fileName.length() - 1 ? fileName
0477: : fileName.substring(idx + 1));
0478: InputStream in = item.openStream();
0479: ByteArrayOutputStream out = new ByteArrayOutputStream(); // Lazy approach. Might have to be changed to upload big files.
0480: int length = 0;
0481: byte[] buf = new byte[4096];
0482: int l;
0483: while ((l = in.read(buf)) != -1) {
0484: out.write(buf, 0, l);
0485: length += l;
0486: if (length > MAX_FILE_SIZE) {
0487: throw new IOException("File is too big");
0488: }
0489: }
0490: in.close();
0491: out.close();
0492: dbFile.setFileSize(new Long(length));
0493: dbFile.setFileContent(out.toByteArray());
0494: dbFile.setDigest(DigestUtils.shaHex(dbFile
0495: .getFileContent()));
0496: dbFile.setDigestAlgorithm("SHA");
0497: if (item.getContentType() == null) {
0498: if (dbFile.getContentType() == null) {
0499: dbFile.setContentType(ROOT_CONTENT_TYPE);
0500: }
0501: } else {
0502: dbFile.setContentType(item.getContentType());
0503: }
0504: }
0505: }
0506: dbFile.configure(mc, converter);
0507: if (referrer == null) {
0508: referrer = (String) mc.get("_referrer");
0509: }
0510: } else { // Try to read text from fileText field
0511: String text = request.getParameter("fileText");
0512: dbFile.configure(new RequestParametersContext(request),
0513: converter);
0514: String actionId = request.getParameter(ACTION_ID);
0515: if (actionId != null) {
0516: Object[] info = (Object[]) request.getSession()
0517: .getAttribute(ACTION_INFO_PREFIX + actionId);
0518: if (info != null) {
0519: dbFile.configure(new MapContext((Map) info[0]),
0520: converter);
0521: if (info[1] != null) {
0522: referrer = (String) info[1];
0523: }
0524: }
0525: }
0526: if (text != null) {
0527: text = MenuActions.normalizeLineSeparator(text);
0528: byte[] textBytes = text.getBytes();
0529: dbFile.setFileSize(new Long(textBytes.length));
0530: dbFile.setFileContent(textBytes);
0531: dbFile.setDigest(DigestUtils.shaHex(dbFile
0532: .getFileContent()));
0533: dbFile.setDigestAlgorithm("SHA");
0534: if (dbFile.getContentType() == null) {
0535: dbFile.setContentType("text/plain");
0536: }
0537: }
0538: }
0539:
0540: GuidGenerator guidGenerator = (GuidGenerator) getGlobal(
0541: request, "db/guid-generator");
0542: if (guidGenerator != null && isBlank(dbFile.getGuid())) {
0543: dbFile.setGuid(guidGenerator.nextGUID());
0544: }
0545:
0546: if (dbFile.isModified()) {
0547: dbFile.setLastModified(System.currentTimeMillis());
0548: getEngine(request).insertDbFile(dbFile);
0549: }
0550:
0551: if (referrer == null) {
0552: StringBuffer rb = new StringBuffer(
0553: (String) new RequestContext(request)
0554: .get("context-path"));
0555: rb
0556: .append("/ef/xmenu/system/FileActions/getInfo/file/list.html");
0557: if (dbFile.getParent() != null) {
0558: rb.append("?ID=");
0559: rb.append(dbFile.getParent());
0560: } else if (dbFile.getOwnerId() != null
0561: && dbFile.getOwnerType() != null) {
0562: rb.append("?OWNER_TYPE=");
0563: rb.append(dbFile.getOwnerType());
0564: rb.append("&OWNER_ID=");
0565: rb.append(dbFile.getOwnerId());
0566: }
0567: referrer = rb.toString();
0568: }
0569:
0570: response.sendRedirect(referrer);
0571: }
0572:
0573: private Map fileTypes;
0574:
0575: /**
0576: * Returns file info by ID parameter or ownerId and ownerType parameters.
0577: * @param request
0578: * @param response
0579: * @return
0580: * @throws SQLException
0581: * @throws ConfigurationException
0582: */
0583: public Object getInfo(HttpServletRequest request,
0584: final HttpServletResponse response) throws SQLException,
0585: ConfigurationException {
0586: loadFileTypes(request);
0587:
0588: String fileId = request.getParameter("ID");
0589: FileEngine engine = getEngine(request);
0590: if (fileId != null) {
0591: FileInfoEx ret = (FileInfoEx) engine.getFileInfo(Integer
0592: .parseInt(fileId), FileInfoEx.class);
0593: if (ret == null) {
0594: return "Invalid file ID";
0595: }
0596: ret.setAttribute("parameters", parametersToMap(request));
0597: if ("special/directory".equals(ret.getContentType())) {
0598: ArrayList children = new ArrayList();
0599: ret.setAttribute("children", children);
0600: engine.getFileInfoByParent(ret.getId(), children,
0601: FileInfoEx.class);
0602: injectIcons(children, new RequestContext(request));
0603: }
0604:
0605: String referrer = getReferrer(request);
0606:
0607: String actionId = request.getParameter(ACTION_ID);
0608: if (!isBlank(actionId)) {
0609: ret.setAttribute("actionId", actionId);
0610: Object[] info = (Object[]) request.getSession()
0611: .getAttribute(ACTION_INFO_PREFIX + actionId);
0612: if (info != null) {
0613: Map parameters = (Map) info[0];
0614: if (parameters != null) {
0615: ret.setAttribute("parameters", parameters);
0616: }
0617:
0618: if (info[1] != null) {
0619: referrer = (String) info[1];
0620: }
0621: }
0622: }
0623:
0624: if (referrer != null) {
0625: ret.setAttribute("referrer", referrer);
0626: }
0627:
0628: return ret;
0629: }
0630:
0631: FileInfoEx ret = new FileInfoEx(); // Virtual directory
0632: ret.setAttribute("virtual", "yes");
0633: ret.configure(new RequestParametersContext(request), converter);
0634: ret.setAttribute("parameters", parametersToMap(request));
0635: ArrayList children = new ArrayList();
0636: ret.setAttribute("children", children);
0637:
0638: // Virtual root directory for OWNER.
0639: String ownerId = request.getParameter("OWNER_ID");
0640: String ownerType = request.getParameter("OWNER_TYPE");
0641:
0642: String actionId = request.getParameter(ACTION_ID);
0643: if (!isBlank(actionId)) {
0644: Object[] info = (Object[]) request.getSession()
0645: .getAttribute(ACTION_INFO_PREFIX + actionId);
0646: if (info != null) {
0647: Map parameters = (Map) info[0];
0648: if (isBlank(ownerId)) {
0649: ownerId = (String) parameters.get("OWNER_ID");
0650: }
0651: if (isBlank(ownerType)) {
0652: ownerType = (String) parameters.get("OWNER_TYPE");
0653: }
0654: }
0655: }
0656:
0657: if (ownerId != null && ownerType != null) {
0658: engine.getFileInfoByOwner(ownerType, ownerId, children,
0659: FileInfoEx.class);
0660: DbFileOwnerType ot = engine.getDbFileOwnerType(ownerType);
0661: if (ot != null && !isBlank(ot.getHomeUrl())) {
0662: final String owi = ownerId;
0663: final String owt = ownerType;
0664:
0665: Context ctx = new RequestContext(request) {
0666: public Object get(String name) {
0667: if ("OWNER_ID".equals(name)) {
0668: return owi;
0669: }
0670: if ("OWNER_TYPE".equals(name)) {
0671: return owt;
0672: }
0673: return super .get(name);
0674: }
0675: };
0676: PropertyParser pp = new PropertyParser(ctx, false);
0677: ret.setAttribute("owner-home-url", pp.parse(ot
0678: .getHomeUrl()));
0679: }
0680: } else {
0681: // Virtual root directory
0682: engine.getRootFileInfo(children, FileInfoEx.class);
0683: }
0684: injectIcons(children, new RequestContext(request));
0685: return ret;
0686: }
0687:
0688: /**
0689: * name -> value
0690: * Multivalue parameters are not supported.
0691: * @param request
0692: * @return
0693: */
0694: private Map parametersToMap(HttpServletRequest request) {
0695: Map parameterMap = new HashMap();
0696: Enumeration pNames = request.getParameterNames();
0697: while (pNames.hasMoreElements()) {
0698: String pName = (String) pNames.nextElement();
0699: parameterMap.put(pName, request.getParameter(pName));
0700: }
0701: return parameterMap;
0702: }
0703:
0704: private void loadFileTypes(HttpServletRequest request) {
0705: synchronized (this ) {
0706: if (fileTypes == null) {
0707: PropertyParser pp = new PropertyParser(
0708: new RequestContext(request), false);
0709: fileTypes = new HashMap();
0710: Collection rawTypes = getEngine(request)
0711: .getDbFileType();
0712: Iterator it = rawTypes.iterator();
0713: while (it.hasNext()) {
0714: DbFileType type = (DbFileType) it.next();
0715: if (!isBlank(type.getIconUrl())) {
0716: type.setIconUrl(pp.parse(type.getIconUrl()));
0717: }
0718: if (fileTypes.put(type.getMimeType(), type) != null) {
0719: logger.warn("Duplicate file typedefinition: "
0720: + type.getMimeType());
0721: }
0722: }
0723: }
0724: }
0725: }
0726:
0727: private Object injectIcons(Collection col, Context context) {
0728: if (fileTypes != null) {
0729: Iterator it = col.iterator();
0730: while (it.hasNext()) {
0731: Object next = it.next();
0732: if (next instanceof FileInfoEx) {
0733: FileInfoEx fix = (FileInfoEx) next;
0734: String contentType = fix.getContentType();
0735: if (contentType == null) {
0736: contentType = ROOT_CONTENT_TYPE;
0737: }
0738: DbFileType dft = (DbFileType) fileTypes
0739: .get(contentType);
0740: if (!ROOT_CONTENT_TYPE.equals(contentType)
0741: && (dft == null || isBlank(dft.getIconUrl()))) {
0742: int idx = contentType.indexOf("/");
0743: if (idx != -1) {
0744: dft = (DbFileType) fileTypes
0745: .get(contentType.substring(0, idx));
0746: }
0747:
0748: if (dft == null || isBlank(dft.getIconUrl())) {
0749: dft = (DbFileType) fileTypes
0750: .get(ROOT_CONTENT_TYPE);
0751: }
0752: }
0753:
0754: if (dft != null && !isBlank(dft.getIconUrl())) {
0755: fix.setColumnAttribute("CONTENT_TYPE",
0756: "icon-url", dft.getIconUrl());
0757: }
0758: }
0759: }
0760: }
0761: return col;
0762: }
0763:
0764: /**
0765: * Returns file record with content as text
0766: */
0767: public Object getAsText(HttpServletRequest request,
0768: final HttpServletResponse response) throws SQLException {
0769: String fileId = request.getParameter("ID");
0770: if (fileId == null) {
0771: return "ID parameter is missing";
0772: }
0773:
0774: DbFileEx ret = (DbFileEx) getEngine(request).getDbFile(
0775: Integer.parseInt(fileId), DbFileEx.class);
0776: if (ret == null) {
0777: return "Invalid file ID";
0778: }
0779:
0780: if (ret.getFileContent() != null) {
0781: ((DbFileImpl) ret).setColumnAttribute("FILE_CONTENT",
0782: "as-text", new String(ret.getFileContent()));
0783: ret.setFileContent(null);
0784: }
0785:
0786: String referrer = getReferrer(request);
0787:
0788: String actionId = request.getParameter(ACTION_ID);
0789: if (!isBlank(actionId)) {
0790: ret.setAttribute("actionId", actionId);
0791: Object[] info = (Object[]) request.getSession()
0792: .getAttribute(ACTION_INFO_PREFIX + actionId);
0793: if (info != null) {
0794: Map parameters = (Map) info[0];
0795: if (parameters != null) {
0796: ret.setAttribute("parameters", parameters);
0797: }
0798:
0799: if (info[1] != null) {
0800: referrer = (String) info[1];
0801: }
0802: }
0803: }
0804:
0805: if (referrer != null) {
0806: ret.setAttribute("referrer", referrer);
0807: }
0808:
0809: return ret;
0810: }
0811:
0812: /**
0813: * Returns file record with content as text
0814: */
0815: public Object redirectTo(HttpServletRequest request,
0816: final HttpServletResponse response) throws SQLException {
0817: String fileId = request.getParameter("ID");
0818: if (fileId == null) {
0819: return "ID parameter is missing";
0820: }
0821:
0822: DbFileEx ret = (DbFileEx) getEngine(request).getDbFile(
0823: Integer.parseInt(fileId), DbFileEx.class);
0824: if (ret == null) {
0825: return "Invalid file ID";
0826: }
0827:
0828: if (ret.getFileContent() == null) {
0829: return "Link is blank";
0830: }
0831:
0832: return new SimpleRedirect(new String(ret.getFileContent()),
0833: "Redirecting to " + ret.getName());
0834: }
0835:
0836: /**
0837: * Deletes file from the databse.
0838: * @param request Shall have ID parameter.
0839: * @param response
0840: * @throws SQLException
0841: */
0842: public Object delete(HttpServletRequest request,
0843: HttpServletResponse response) throws SQLException {
0844: String fileId = request.getParameter("ID");
0845: if (fileId == null) {
0846: return "ID parameter is missing";
0847: }
0848:
0849: int result = getEngine(request).deleteDbFile(
0850: Integer.parseInt(fileId));
0851: if (result != 1) {
0852: return "Invalid file ID";
0853: }
0854:
0855: String referrer = getReferrer(request);
0856: if (referrer == null) {
0857: return "File deleted";
0858: }
0859: return new SimpleRedirect(referrer, "File deleted");
0860: }
0861:
0862: /**
0863: * file [type, action name] -> action record.
0864: * Actions of supertypes, e.g. image is propagated to subtypes e.g. image/gif
0865: * application/octet-stream is this the root type.
0866: */
0867: private Map fileTypeActionMap;
0868:
0869: /**
0870: * Performs action on a file.
0871: * @param request Parameters ID, action
0872: * @param response
0873: * @return
0874: * @throws SQLException
0875: */
0876: public Object performAction(HttpServletRequest request,
0877: HttpServletResponse response) throws SQLException {
0878: loadFileTypeActions(request);
0879:
0880: String action = request.getParameter("action");
0881: if (action == null) {
0882: return "action request parameter is missing";
0883: }
0884:
0885: String contentType = request.getParameter("fileType");
0886: String fileId = request.getParameter("ID");
0887:
0888: if (contentType == null) {
0889: if (fileId == null) {
0890: return "ID or fileType request parameter is missing";
0891: }
0892: FileInfo info = getEngine(request).getFileInfo(
0893: Integer.parseInt(fileId));
0894: if (info == null) {
0895: return "Invalid file ID";
0896: }
0897:
0898: contentType = info.getContentType();
0899: }
0900:
0901: if (contentType == null) {
0902: contentType = ROOT_CONTENT_TYPE;
0903: }
0904:
0905: Collection key = new ArrayList();
0906: key.add(contentType);
0907: key.add(action);
0908: DbFileTypeAction actionRecord = (DbFileTypeAction) fileTypeActionMap
0909: .get(key);
0910: if (actionRecord == null
0911: && !ROOT_CONTENT_TYPE.equals(contentType)) {
0912: int idx = contentType.indexOf('/');
0913: if (idx != -1) {
0914: key.clear();
0915: key.add(contentType.substring(0, idx));
0916: key.add(action);
0917: actionRecord = (DbFileTypeAction) fileTypeActionMap
0918: .get(key);
0919: }
0920:
0921: if (actionRecord == null) {
0922: key.clear();
0923: key.add(ROOT_CONTENT_TYPE);
0924: key.add(action);
0925: actionRecord = (DbFileTypeAction) fileTypeActionMap
0926: .get(key);
0927: }
0928: }
0929:
0930: if (actionRecord == null) {
0931: return "Action '" + action
0932: + "' is not defined for content type '"
0933: + contentType + "'";
0934: }
0935:
0936: StringBuffer redirect = new StringBuffer();
0937: if (isBlank(actionRecord.getActionXid())) {
0938: if (isBlank(actionRecord.getActionUrl())) {
0939: return "Invalid action record for action '" + action
0940: + "' is not defined for content type '"
0941: + contentType
0942: + "', neither XID nor URL defined.";
0943: }
0944: redirect.append(actionRecord.getActionUrl());
0945: } else {
0946: Menu menu = getMenu(request).findByXid(
0947: actionRecord.getActionXid());
0948: if (menu == null) {
0949: return "Action not available: '" + action
0950: + "' for content type '" + contentType + "'";
0951: }
0952: redirect.append(menu.get("href",
0953: new RequestContext(request)));
0954: }
0955:
0956: if (redirect.indexOf("?") == -1) {
0957: redirect.append("?");
0958: } else {
0959: redirect.append("&");
0960: }
0961:
0962: long this ActionId;
0963: synchronized (this ) {
0964: this ActionId = actionCounter++;
0965: }
0966: redirect.append("actionId=");
0967: redirect.append(this ActionId);
0968: if (fileId != null) {
0969: redirect.append("&ID=");
0970: redirect.append(fileId);
0971: }
0972: request.getSession().setAttribute(
0973: ACTION_INFO_PREFIX + this ActionId,
0974: new Object[] { parametersToMap(request),
0975: getReferrer(request) });
0976: return new SimpleRedirect(redirect.toString(),
0977: "Redirecting to action '" + action
0978: + "' for content type '" + contentType + "'");
0979: }
0980:
0981: /**
0982: * Helper class to inject thread-specific things into an instance shared by many threads.
0983: * @author Pavel Vlasov
0984: */
0985: private class DomSerializableThreadLocal extends ThreadLocal
0986: implements DomSerializable {
0987:
0988: public void toDom(Element holder) {
0989: Object obj = get();
0990: if (obj == null) {
0991: holder.setAttribute("is-null", "yes");
0992: } else {
0993: CompositeDomSerializer.getThreadInstance()
0994: .toDomSerializable(obj).toDom(holder);
0995: }
0996: }
0997:
0998: }
0999:
1000: private void loadFileTypeActions(HttpServletRequest request) {
1001: synchronized (this ) {
1002: if (fileTypeActionMap == null) {
1003: PropertyParser pp = new PropertyParser(
1004: new RequestContext(request), false);
1005: fileTypeActionMap = new HashMap();
1006: Collection rawActions = getEngine(request)
1007: .getDbFileTypeAction();
1008: Iterator it = rawActions.iterator();
1009: while (it.hasNext()) {
1010: DbFileTypeActionImpl action = (DbFileTypeActionImpl) it
1011: .next();
1012: action.setColumnAttribute("TEMPLATE_CONTENT",
1013: "instantiated",
1014: new DomSerializableThreadLocal());
1015: if (!isBlank(action.getIconUrl())) {
1016: action
1017: .setIconUrl(pp.parse(action
1018: .getIconUrl()));
1019: }
1020: if (!isBlank(action.getActionUrl())) {
1021: action.setActionUrl(pp.parse(action
1022: .getActionUrl()));
1023: }
1024: Collection key = new ArrayList();
1025: key.add(action.getMimeType());
1026: key.add(action.getName());
1027: if (fileTypeActionMap.put(key, action) != null) {
1028: logger
1029: .warn("Duplicate file type/action definition: "
1030: + action.getMimeType()
1031: + ", "
1032: + action.getName());
1033: }
1034: }
1035: }
1036: }
1037: }
1038:
1039: // /**
1040: // * Retrieves list of available actions for a given file
1041: // * @param context
1042: // * @return
1043: // */
1044: // public Collection getFileActions(int fileId, Context context) {
1045: // // TODO
1046: // }
1047:
1048: public static class FileInfoEx extends FileInfoImpl {
1049:
1050: public FileInfoEx() {
1051: super ();
1052: }
1053:
1054: public FileInfoEx(boolean force) {
1055: super (force);
1056: }
1057:
1058: public FileInfoEx(ResultSet rs) throws SQLException {
1059: super (rs);
1060: }
1061:
1062: public void toDom(Element holder) {
1063: super .toDom(holder);
1064: AbstractDomObject.addTextElement(holder, "last-modified",
1065: new Date(getLastModified()).toString());
1066:
1067: if (getFileSize() != null) {
1068: long size = getFileSize().longValue();
1069: if (size < 1024) {
1070: AbstractDomObject.addTextElement(holder, "size",
1071: String.valueOf(size));
1072: } else if (size < 1024 * 1024) {
1073: AbstractDomObject.addTextElement(holder, "size",
1074: MessageFormat.format("{0,number,0.##} Kb",
1075: new Object[] { new Double(
1076: size / 1024) }));
1077: } else {
1078: AbstractDomObject.addTextElement(holder, "size",
1079: MessageFormat.format("{0,number,0.##} Mb",
1080: new Object[] { new Double(size
1081: / (1024 * 1024)) }));
1082: }
1083: }
1084: }
1085:
1086: }
1087:
1088: public static class DbFileEx extends DbFileImpl {
1089:
1090: public DbFileEx() {
1091: super ();
1092: }
1093:
1094: public DbFileEx(boolean force) {
1095: super (force);
1096: }
1097:
1098: public DbFileEx(ResultSet rs) throws SQLException {
1099: super (rs);
1100: }
1101:
1102: public void toDom(Element holder) {
1103: super .toDom(holder);
1104: AbstractDomObject.addTextElement(holder, "last-modified",
1105: new Date(getLastModified()).toString());
1106:
1107: if (getFileSize() != null) {
1108: long size = getFileSize().longValue();
1109: if (size < 1024) {
1110: AbstractDomObject.addTextElement(holder, "size",
1111: String.valueOf(size));
1112: } else if (size < 1024 * 1024) {
1113: AbstractDomObject.addTextElement(holder, "size",
1114: MessageFormat.format("{0,number,0.##} Kb",
1115: new Object[] { new Double(
1116: size / 1024) }));
1117: } else {
1118: AbstractDomObject.addTextElement(holder, "size",
1119: MessageFormat.format("{0,number,0.##} Mb",
1120: new Object[] { new Double(size
1121: / (1024 * 1024)) }));
1122: }
1123: }
1124: }
1125:
1126: }
1127:
1128: /**
1129: *
1130: * @param request
1131: * @param response
1132: * @return List of file types with 'create' action.
1133: */
1134: public Object getCreateFileTypes(HttpServletRequest request,
1135: HttpServletResponse response) {
1136: loadFileTypeActions(request);
1137: loadFileTypes(request);
1138: Collection ret = new ArrayList();
1139: ret.add(parametersToMap(request));
1140: Iterator it = fileTypes.values().iterator();
1141: while (it.hasNext()) {
1142: DbFileType dft = (DbFileType) it.next();
1143: Collection key = new ArrayList();
1144: key.add(dft.getMimeType());
1145: key.add("Create");
1146: if (fileTypeActionMap.containsKey(key)) {
1147: ret.add(dft);
1148: }
1149: }
1150: return ret;
1151: }
1152:
1153: /**
1154: * Returns a list of actions available for a given file or file type
1155: * @param request Parameters ID of fileType
1156: * @param response
1157: * @return
1158: * @throws SQLException
1159: * @throws HammurapiWebException
1160: */
1161: public Object getActions(HttpServletRequest request,
1162: HttpServletResponse response) throws SQLException,
1163: HammurapiWebException {
1164: loadFileTypeActions(request);
1165:
1166: String contentType = request.getParameter("fileType");
1167:
1168: FileInfo info = null;
1169:
1170: if (contentType == null) {
1171: String fileId = request.getParameter("ID");
1172: if (fileId == null) {
1173: return "ID or fileType request parameter is missing";
1174: }
1175: info = getEngine(request).getFileInfo(
1176: Integer.parseInt(fileId));
1177: if (info == null) {
1178: return "Invalid file ID";
1179: }
1180:
1181: contentType = info.getContentType();
1182: }
1183:
1184: if (contentType == null) {
1185: contentType = ROOT_CONTENT_TYPE;
1186: }
1187:
1188: // Combine actions from self, super and root
1189: Map actions = new HashMap();
1190: Iterator it = fileTypeActionMap.values().iterator();
1191: while (it.hasNext()) {
1192: DbFileTypeAction dfta = (DbFileTypeAction) it.next();
1193: if (ROOT_CONTENT_TYPE.equals(dfta.getMimeType())) {
1194: actions.put(dfta.getName(), dfta);
1195: }
1196: }
1197:
1198: if (!ROOT_CONTENT_TYPE.equals(contentType)) {
1199: int idx = contentType.indexOf("/");
1200: if (idx != -1) {
1201: String super Type = contentType.substring(0, idx);
1202: it = fileTypeActionMap.values().iterator();
1203: while (it.hasNext()) {
1204: DbFileTypeAction dfta = (DbFileTypeAction) it
1205: .next();
1206: if (super Type.equals(dfta.getMimeType())) {
1207: actions.put(dfta.getName(), dfta);
1208: }
1209: }
1210: }
1211:
1212: it = fileTypeActionMap.values().iterator();
1213: while (it.hasNext()) {
1214: DbFileTypeAction dfta = (DbFileTypeAction) it.next();
1215: if (contentType.equals(dfta.getMimeType())) {
1216: actions.put(dfta.getName(), dfta);
1217: }
1218: }
1219: }
1220:
1221: Collection ret = new ArrayList();
1222: ret.add(parametersToMap(request));
1223:
1224: Map context = new HashMap();
1225:
1226: RequestContext requestContext = new RequestContext(request);
1227: context.put("requestContext", requestContext);
1228: context.put("context-path", requestContext.get("context-path"));
1229: context.put("request", request);
1230: context.put("response", response);
1231: context.put("escaper", new Escaper());
1232: context.put("fileInfo", info);
1233: context.put("ID", info == null ? null : new Integer(info
1234: .getId()));
1235:
1236: it = actions.values().iterator();
1237: while (it.hasNext()) {
1238: DbFileTypeActionImpl dftai = (DbFileTypeActionImpl) it
1239: .next();
1240: ThreadLocal thl = (ThreadLocal) dftai.getColumnAttribute(
1241: "TEMPLATE_CONTENT", "instantiated");
1242: thl.set(null);
1243: if (!isBlank(dftai.getTemplateContent())) {
1244: EvaluatorFactory eFactory = (EvaluatorFactory) getGlobal(
1245: request, "db/EvaluatorFactory");
1246: if (eFactory == null) {
1247: return "Evaluator factory is not set";
1248: }
1249:
1250: context.put("icon", isBlank(dftai.getIconUrl()) ? ""
1251: : "<img src=\"" + dftai.getIconUrl() + "\"/>");
1252: context.put("action", dftai);
1253:
1254: EvaluationResult result = eFactory.evaluate(dftai
1255: .getTemplateContentType(), dftai
1256: .getTemplateContent(), "Template for action "
1257: + dftai.getName() + " for file type "
1258: + dftai.getMimeType(), context, this .getClass()
1259: .getClassLoader());
1260:
1261: thl.set(result.getOutput());
1262: }
1263: ret.add(dftai);
1264: }
1265:
1266: return ret;
1267: }
1268:
1269: /**
1270: * Returns action info by actionId parameter and also request parameters
1271: * @param request
1272: * @param response
1273: * @return
1274: */
1275: public Object actionInfo(HttpServletRequest request,
1276: final HttpServletResponse response) {
1277: Map ret = new HashMap();
1278: ret.put("request-parameters", parametersToMap(request));
1279:
1280: String actionId = request.getParameter(ACTION_ID);
1281: if (actionId != null) {
1282: Object[] info = (Object[]) (isBlank(actionId) ? null
1283: : request.getSession().getAttribute(
1284: ACTION_INFO_PREFIX + actionId));
1285: Map parameters = (Map) (info == null ? null : info[0]);
1286: if (parameters != null) {
1287: ret.put("action-parameters", parameters);
1288: }
1289: String referrer = (String) (info == null ? null : info[1]);
1290: if (referrer != null) {
1291: ret.put("referrer", referrer);
1292: }
1293: }
1294:
1295: return ret;
1296: }
1297:
1298: }
|