/* ===============================================================================
*
* Part of the InfoGlue Content Management Platform (www.infoglue.org)
*
* ===============================================================================
*
* Copyright (C)
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2, as published by the
* Free Software Foundation. See the file LICENSE.html for more information.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc. / 59 Temple
* Place, Suite 330 / Boston, MA 02111-1307 / USA.
*
* ===============================================================================
*/
package org.infoglue.cms.applications.contenttool.actions;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.apache.xerces.parsers.DOMParser;
import org.infoglue.cms.controllers.kernel.impl.simple.AccessRightController;
import org.infoglue.cms.controllers.kernel.impl.simple.ContentController;
import org.infoglue.cms.controllers.kernel.impl.simple.ContentControllerProxy;
import org.infoglue.cms.controllers.kernel.impl.simple.ContentStateController;
import org.infoglue.cms.controllers.kernel.impl.simple.ContentTypeDefinitionController;
import org.infoglue.cms.controllers.kernel.impl.simple.ContentVersionController;
import org.infoglue.cms.entities.content.ContentVO;
import org.infoglue.cms.entities.content.ContentVersion;
import org.infoglue.cms.entities.content.ContentVersionVO;
import org.infoglue.cms.entities.management.ContentTypeAttribute;
import org.infoglue.cms.entities.management.ContentTypeDefinitionVO;
import org.infoglue.cms.exception.AccessConstraintException;
import org.infoglue.cms.exception.ConstraintException;
import org.infoglue.cms.util.AccessConstraintExceptionBuffer;
import org.infoglue.cms.util.CmsPropertyHandler;
import org.infoglue.cms.util.ConstraintExceptionBuffer;
import org.infoglue.cms.util.XMLHelper;
import org.infoglue.deliver.controllers.kernel.impl.simple.PageEditorHelper;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
/**
* This is the action-class for UpdateContentVersionVersion
*
* @author Mattias Bogeblad
*/
public class UpdateContentVersionAttributeAction extends ViewContentVersionAction
{
private final static Logger logger = Logger.getLogger(UpdateContentVersionAttributeAction.class.getName());
private static final long serialVersionUID = 1L;
private ContentVersionVO contentVersionVO;
private Integer contentId;
private Integer languageId;
private Integer contentVersionId;
private String attributeName;
private String deliverContext = "infoglueDeliverWorking";
private ConstraintExceptionBuffer ceb;
public UpdateContentVersionAttributeAction()
{
this(new ContentVersionVO());
}
public UpdateContentVersionAttributeAction(ContentVersionVO contentVersionVO)
{
this.contentVersionVO = contentVersionVO;
this.ceb = new ConstraintExceptionBuffer();
}
public String doExecute() throws Exception
{
logger.info("Updating content version attribute....");
logger.info("contentId:" + contentId);
logger.info("languageId:" + languageId);
logger.info("attributeName:" + attributeName);
super.initialize(this.contentVersionId, this.contentId, this.languageId);
this.contentVersionVO = this.getContentVersionVO();
String attributeValue = getRequest().getParameter(this.attributeName);
logger.info("attributeValue:" + attributeValue);
if(attributeValue != null)
{
setAttributeValue(this.contentVersionVO, this.attributeName, attributeValue);
ceb.throwIfNotEmpty();
this.contentVersionVO.setVersionModifier(this.getInfoGluePrincipal().getName());
ContentVersionController.getContentVersionController().update(this.contentId, this.languageId, this.contentVersionVO, this.getInfoGluePrincipal());
}
return "success";
}
private static Boolean active = new Boolean(false);
/**
* This command updates a certain attribute in a content version.
* Lately we added a timeout on the wait for other threads to finish to avoid locks.
*/
public String doSaveAndReturnValue()
{
/*
try
{
Random rand = new Random();
int randomNum = rand.nextInt((10 - 1) + 1) + 1;
System.out.println("Sleep...");
Thread.sleep(randomNum*1000);
}
catch (Exception e)
{
}
*/
int index = 0;
while(active && index < 100)
{
logger.info("Waiting for previous thread..");
try
{
Thread.sleep(50);
}
catch (Exception e)
{
}
index++;
}
synchronized(active)
{
active = new Boolean(true);
}
try
{
logger.info("Updating content version attribute through ajax....");
logger.info("contentId:" + contentId);
logger.info("languageId:" + languageId);
logger.info("attributeName:" + attributeName);
super.initialize(this.contentVersionId, this.contentId, this.languageId);
this.contentVersionVO = this.getContentVersionVO();
if(this.contentVersionVO == null)
{
ContentVO contentVO = ContentController.getContentController().getContentVOWithId(contentId);
ContentTypeDefinitionVO contentTypeDefinitionVO = ContentTypeDefinitionController.getController().getContentTypeDefinitionVOWithId(contentVO.getContentTypeDefinitionId());
StringBuffer sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><article xmlns=\"x-schema:ArticleSchema.xml\"><attributes>");
List contentTypeAttributes = ContentTypeDefinitionController.getController().getContentTypeAttributes(contentTypeDefinitionVO, true);
Iterator contentTypeAttributesIterator = contentTypeAttributes.iterator();
while(contentTypeAttributesIterator.hasNext())
{
ContentTypeAttribute contentTypeAttribute = (ContentTypeAttribute)contentTypeAttributesIterator.next();
String initialValue = contentTypeAttribute.getContentTypeAttribute("initialData").getContentTypeAttributeParameterValue().getValue("label");
if(initialValue == null || initialValue.trim().equals(""))
initialValue = "State " + contentTypeAttribute.getName();
sb.append("<" + contentTypeAttribute.getName() + "><![CDATA[" + initialValue + "]]></" + contentTypeAttribute.getName() + ">");
}
sb.append("</attributes></article>");
ContentVersionVO contentVersionVO = new ContentVersionVO();
contentVersionVO.setVersionComment("Autocreated");
contentVersionVO.setVersionModifier(this.getInfoGluePrincipal().getName());
contentVersionVO.setVersionValue(sb.toString());
this.contentVersionVO = ContentVersionController.getContentVersionController().create(contentId, languageId, contentVersionVO, null);
}
else if(!this.contentVersionVO.getStateId().equals(ContentVersionVO.WORKING_STATE))
{
ContentVersionVO latestContentVersionVO = ContentVersionController.getContentVersionController().getLatestContentVersionVO(this.contentVersionVO.getContentId(), this.contentVersionVO.getLanguageId());
if(latestContentVersionVO.getId().intValue() > this.contentVersionVO.getId().intValue() && latestContentVersionVO.getStateId().equals(ContentVersionVO.WORKING_STATE))
{
logger.warn("The version is actually not in published state any more: " + latestContentVersionVO.getContentId());
this.contentVersionVO = latestContentVersionVO;
this.contentVersionId = latestContentVersionVO.getId();
}
else
{
ContentVersionVO contentVersion = ContentStateController.changeState(this.contentVersionVO.getContentVersionId(), ContentVersionVO.WORKING_STATE, "Edit on sight", false, null, this.getInfoGluePrincipal(), this.contentVersionVO.getContentId(), new ArrayList());
logger.info("Changed state on: " + latestContentVersionVO.getContentId());
this.contentVersionId = contentVersion.getContentVersionId();
this.contentVersionVO = contentVersion;
}
}
String attributeValue = getRequest().getParameter(this.attributeName);
logger.info("*************************************************");
logger.info("** SAVING **");
logger.info("*************************************************");
logger.info("attributeValue real:" + attributeValue);
/*
for(int i=0; i<attributeValue.length(); i++)
logger.info("c:" + (int)attributeValue.charAt(i) + "-" + Integer.toHexString((int)attributeValue.charAt(i)));
*/
if(attributeValue != null)
{
boolean enableCustomCharactersParsing = CmsPropertyHandler.getEnableCustomCharactersParsing();
logger.info("enableCustomCharactersParsing: " + enableCustomCharactersParsing);
if (enableCustomCharactersParsing)
{
boolean isUTF8 = false;
boolean hasUnicodeChars = false;
if(attributeValue.indexOf((char)65533) > -1)
isUTF8 = true;
for(int i=0; i<attributeValue.length(); i++)
{
int c = (int)attributeValue.charAt(i);
//logger.error("c2:" + c + "-" + Integer.toHexString(c));
if(c > 255 && c < 65533)
hasUnicodeChars = true;
}
logger.debug("isUTF8:" + isUTF8);
logger.debug("hasUnicodeChars:" + hasUnicodeChars);
if(!isUTF8 && !hasUnicodeChars)
{
String fromEncoding = CmsPropertyHandler.getUploadFromEncoding();
if(fromEncoding == null)
fromEncoding = "iso-8859-1";
String toEncoding = CmsPropertyHandler.getUploadToEncoding();
if(toEncoding == null)
toEncoding = "utf-8";
String[] controlChars = CmsPropertyHandler.getCustomCharactersForConversionParsed();
if (logger.isInfoEnabled())
{
logger.info("controlChars: " + Arrays.toString(controlChars));
}
boolean convert = true;
for(String charToTest : controlChars)
{
if(logger.isInfoEnabled())
{
logger.info("Index for " + charToTest + ":" + attributeValue.indexOf(charToTest));
}
if(attributeValue.indexOf(charToTest) > -1)
{
convert = false;
break;
}
}
if(convert)
{
attributeValue = new String(attributeValue.getBytes(fromEncoding), toEncoding);
}
}
}
logger.info("\n\nattributeValue original:" + attributeValue);
attributeValue = parseInlineAssetReferences(attributeValue);
logger.info("attributeValue transformed:" + attributeValue + "\n\n");
setAttributeValue(this.contentVersionVO, this.attributeName, attributeValue);
ceb.throwIfNotEmpty();
this.contentVersionVO.setVersionModifier(this.getInfoGluePrincipal().getName());
ContentVersionController.getContentVersionController().update(this.contentId, this.languageId, this.contentVersionVO, this.getInfoGluePrincipal());
logger.info("*************************************************");
attributeValue = PageEditorHelper.parseAttributeForInlineEditing(attributeValue, true, getDeliverContext(), contentId, languageId);
}
this.getResponse().setContentType("text/plain; charset=utf-8");
this.getResponse().setCharacterEncoding("utf-8");
//this.getResponse().setContentType("text/plain");
this.getResponse().getWriter().println(attributeValue);
}
catch (ConstraintException ce)
{
logger.info("Error saving attribute - not allowed by validation: " + ce.getMessage());
this.getResponse().setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
try
{
final String errorCode = ce.getErrorCode();
String localizedErrorMessage;
if(errorCode.length() > 5)
{
localizedErrorMessage = StringEscapeUtils.unescapeXml(errorCode);
}
else
{
localizedErrorMessage = getLocalizedErrorMessage(getLocale(), errorCode);
String requiredErrorCode = "300";
if (requiredErrorCode.equals(errorCode))
{
try
{
String fieldName = ce.getFieldName();
fieldName = fieldName.substring(fieldName.lastIndexOf(".") + 1);
ContentVO contentVO = ContentController.getContentController().getContentVOWithId(this.contentId);
String fieldDisplayName = ContentTypeDefinitionController.getController().getContentTypeDefinitionAttributeDisplayName(contentVO.getContentTypeDefinitionId(), fieldName, getLocale().getLanguage());
if (fieldDisplayName == null)
{
logger.info("Did not find a display name for attribute. " + fieldName);
}
else
{
localizedErrorMessage += " (" + fieldDisplayName + ")";
}
}
catch (Exception ex)
{
logger.warn("Failed to get the display name for required field validation error. ContentId: " + contentId + ". FieldName: " + ce.getFieldName() + ". Message: " + ex.getMessage());
logger.info("Failed to get the display name for required field validation error. ContentId: " + contentId + ". FieldName: " + ce.getFieldName(), ex);
}
}
}
this.getResponse().setCharacterEncoding("utf-8");
this.getResponse().getWriter().println(localizedErrorMessage);
}
catch (IOException ex)
{
logger.error("Error when reporting constraint exception for content attribute update. Message: " + ex.getMessage());
}
return NONE;
}
catch (Throwable t)
{
logger.error("Error saving attribute: " + t.getMessage(), t);
this.getResponse().setStatus(this.getResponse().SC_INTERNAL_SERVER_ERROR);
return ERROR;
}
finally
{
synchronized(active)
{
active = new Boolean(false);
}
}
return NONE;
}
public String doGetAttributeValue() throws Exception
{
try
{
super.initialize(this.contentVersionId, this.contentId, this.languageId);
this.contentVersionVO = this.getContentVersionVO();
if(this.contentVersionVO == null)
{
ContentVO contentVO = ContentController.getContentController().getContentVOWithId(contentId);
ContentTypeDefinitionVO contentTypeDefinitionVO = ContentTypeDefinitionController.getController().getContentTypeDefinitionVOWithId(contentVO.getContentTypeDefinitionId());
StringBuffer sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?><article xmlns=\"x-schema:ArticleSchema.xml\"><attributes>");
List contentTypeAttributes = ContentTypeDefinitionController.getController().getContentTypeAttributes(contentTypeDefinitionVO, true);
Iterator contentTypeAttributesIterator = contentTypeAttributes.iterator();
while(contentTypeAttributesIterator.hasNext())
{
ContentTypeAttribute contentTypeAttribute = (ContentTypeAttribute)contentTypeAttributesIterator.next();
String initialValue = contentTypeAttribute.getContentTypeAttribute("initialData").getContentTypeAttributeParameterValue().getValue("label");
if(initialValue == null || initialValue.trim().equals(""))
initialValue = "State " + contentTypeAttribute.getName();
sb.append("<" + contentTypeAttribute.getName() + "><![CDATA[" + initialValue + "]]></" + contentTypeAttribute.getName() + ">");
}
sb.append("</attributes></article>");
ContentVersionVO contentVersionVO = new ContentVersionVO();
contentVersionVO.setVersionComment("Autocreated");
contentVersionVO.setVersionModifier(this.getInfoGluePrincipal().getName());
contentVersionVO.setVersionValue(sb.toString());
this.contentVersionVO = ContentVersionController.getContentVersionController().create(contentId, languageId, contentVersionVO, null);
}
AccessConstraintExceptionBuffer ceb = new AccessConstraintExceptionBuffer();
Integer protectedContentId = ContentControllerProxy.getController().getProtectedContentId(this.contentVersionVO.getContentId());
logger.info("protectedContentId:" + protectedContentId);
if(protectedContentId != null && !AccessRightController.getController().getIsPrincipalAuthorized(this.getInfoGluePrincipal(), "Content.Write", protectedContentId.toString()))
ceb.add(new AccessConstraintException("Content.contentId", "1001"));
ceb.throwIfNotEmpty();
String attributeValue = "";
if(this.contentVersionVO != null)
{
attributeValue = ContentVersionController.getContentVersionController().getAttributeValue(contentVersionVO, attributeName, false);
}
logger.info("attributeValue before parse:" + attributeValue);
attributeValue = PageEditorHelper.parseAttributeForInlineEditing(attributeValue, false, getDeliverContext(), contentId, languageId);
//logger.info("parseAttributeForInlineEditing done");
logger.info("attributeValue:" +attributeValue);
/*
logger.info("attributeValue:" +attributeValue);
for(int i=0; i<attributeValue.length(); i++)
logger.info("c3:" + (int)attributeValue.charAt(i) + "-" + Integer.toHexString((int)attributeValue.charAt(i)));
*/
this.getResponse().setContentType("text/plain; charset=utf-8");
this.getResponse().setCharacterEncoding("utf-8");
//this.getResponse().setContentType("text/plain");
this.getResponse().getWriter().println(attributeValue);
}
catch (Exception e)
{
e.printStackTrace();
throw e;
}
return NONE;
}
private String parseInlineAssetReferences(String attributeValue) throws Exception
{
Map<String,String> replacements = new HashMap<String,String>();
logger.info("********************\n\n");
Pattern pattern = Pattern.compile("\"DownloadAsset.action.*?\"");
Matcher matcher = pattern.matcher(attributeValue);
while ( matcher.find() )
{
String match = matcher.group();
logger.info("Found a inline asset: " + match);
String parsedContentId = match.substring(match.indexOf("contentId=") + 10, match.indexOf("&", 10));
logger.info("parsedContentId: " + parsedContentId);
int langStartIndex = match.indexOf("languageId=") + 11;
String parsedLanguageId = match.substring(langStartIndex, match.indexOf("&", langStartIndex));
logger.info("parsedLanguageId: " + parsedLanguageId);
int assetStartIndex = match.indexOf("assetKey=") + 9;
String parsedAssetKey = match.substring(assetStartIndex, match.indexOf("\"", assetStartIndex));
logger.info("parsedAssetKey: " + parsedAssetKey);
String url = "$templateLogic.getInlineAssetUrl(" + parsedContentId + ", \"" + parsedAssetKey + "\")";
logger.info("url:" + url);
//replacements.put(match.substring(1, match.length() - 1), url);
replacements.put(match, url);
}
logger.info("********************\n\n");
Iterator<String> replacementsIterator = replacements.keySet().iterator();
while(replacementsIterator.hasNext())
{
String patternToReplace = replacementsIterator.next();
String replacement = replacements.get(patternToReplace);
logger.info("Replacing " + patternToReplace + " with " + replacement);
patternToReplace = patternToReplace.replaceAll("\\?", "\\\\?");
logger.info("patternToReplace " + patternToReplace);
replacement = replacement.replaceAll("\\$", "\\\\\\$");
logger.info("replacement " + replacement);
replacement = replacement.replaceAll("\\.", "\\\\.");
logger.info("replacement " + replacement);
replacement = replacement.replaceAll("\\(", "\\\\(");
logger.info("replacement " + replacement);
replacement = replacement.replaceAll("\\)", "\\\\)");
logger.info("replacement " + replacement);
logger.info("patternToReplace: " + patternToReplace);
logger.info("replacement: " + replacement);
logger.info("attributeValue before " + attributeValue);
String delimeter = "";
if(patternToReplace.startsWith("\""))
delimeter = "\"";
if(patternToReplace.startsWith("\'"))
delimeter = "\'";
replacement = delimeter + replacement + delimeter;
logger.info("replacement: " + replacement);
attributeValue = attributeValue.replaceAll(patternToReplace, replacement);
logger.info("attributeValue after " + attributeValue);
}
return attributeValue;
}
/**
* This method sets a value to the xml that is the contentVersions Value.
*/
private void setAttributeValue(ContentVersionVO contentVersionVO, String attributeName, String attributeValue)
{
String value = "";
if(this.contentVersionVO != null)
{
try
{
logger.info("VersionValue:" + this.contentVersionVO.getVersionValue());
InputSource inputSource = new InputSource(new StringReader(this.contentVersionVO.getVersionValue()));
DOMParser parser = new DOMParser();
parser.parse(inputSource);
Document document = parser.getDocument();
NodeList nl = document.getDocumentElement().getChildNodes();
Node n = nl.item(0);
nl = n.getChildNodes();
for(int i=0; i<nl.getLength(); i++)
{
n = nl.item(i);
if(n.getNodeName().equalsIgnoreCase(attributeName))
{
logger.info("Setting attributeValue: " + attributeValue);
if(n.getFirstChild() != null && n.getFirstChild().getNodeValue() != null)
{
n.getFirstChild().setNodeValue(attributeValue);
break;
}
else
{
CDATASection cdata = document.createCDATASection(attributeValue);
n.appendChild(cdata);
break;
}
/*
Node valueNode = n.getFirstChild();
n.getFirstChild().setNodeValue(attributeValue);
break;
*/
}
}
contentVersionVO.setVersionValue(XMLHelper.serializeDom(document, new StringBuffer()).toString());
}
catch(Exception e)
{
logger.error("Problem updating version value:" + attributeName + "=" + attributeValue + " reason:" + e.getMessage(), e);
}
}
}
public void setContentVersionId(Integer contentVersionId)
{
this.contentVersionVO.setContentVersionId(contentVersionId);
}
public java.lang.Integer getContentVersionId()
{
return this.contentVersionVO.getContentVersionId();
}
public void setStateId(Integer stateId)
{
this.contentVersionVO.setStateId(stateId);
}
public java.lang.Integer getStateId()
{
return this.contentVersionVO.getStateId();
}
public void setContentId(Integer contentId)
{
this.contentId = contentId;
}
public java.lang.Integer getContentId()
{
return this.contentId;
}
public void setLanguageId(Integer languageId)
{
this.languageId = languageId;
}
public java.lang.Integer getLanguageId()
{
return this.languageId;
}
public java.lang.String getVersionValue()
{
return this.contentVersionVO.getVersionValue();
}
public void setVersionValue(java.lang.String versionValue)
{
this.contentVersionVO.setVersionValue(versionValue);
}
public String getAttributeName()
{
return attributeName;
}
public void setAttributeName(String attributeName)
{
this.attributeName = attributeName;
}
public String getDeliverContext()
{
return deliverContext;
}
public void setDeliverContext(String deliverContext)
{
this.deliverContext = deliverContext;
}
}