package org.exoplatform.wcm.connector.collaboration;
import java.io.FileNotFoundException;
import java.security.AccessControlException;
import java.util.Locale;
import java.util.ResourceBundle;
import javax.jcr.AccessDeniedException;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import javax.jcr.lock.LockException;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import org.exoplatform.container.xml.PortalContainerInfo;
import org.exoplatform.ecm.utils.text.Text;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.access.PermissionType;
import org.exoplatform.services.jcr.core.ExtendedNode;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.common.SessionProvider;
import org.exoplatform.services.listener.ListenerService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.resources.ResourceBundleService;
import org.exoplatform.services.rest.resource.ResourceContainer;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Created by The eXo Platform SEA
* Author : Ha Quang Tan
* tan.haquang@exoplatform.com
* Mar 23, 2011
*/
@Path("/contents/editing/")
public class InlineEditingService implements ResourceContainer{
private static final Log LOG = ExoLogger.getLogger(InlineEditingService.class.getName());
final static public String EXO_TITLE = "exo:title";
final static public String EXO_SUMMARY = "exo:summary";
final static public String EXO_TEXT = "exo:text";
final static public String EXO_RSS_ENABLE = "exo:rss-enable";
public final static String POST_EDIT_CONTENT_EVENT = "CmsService.event.postEdit";
private final String localeFile = "locale.portlet.i18n.WebUIDms";
/**
* SERVICE: Edit title of document.
*
* @param newTitle the new title of document
* @param repositoryName the repository name
* @param workspaceName the workspace name
* @param nodeUIID the UIID of node
* @param siteName the site name
*
* @return the response
*/
@POST
@Path("/title/")
public Response editTitle(@FormParam("newValue") String newTitle,
@QueryParam("repositoryName") String repositoryName,
@QueryParam("workspaceName") String workspaceName,
@QueryParam("nodeUIID") String nodeUIID,
@QueryParam("siteName") String siteName,
@QueryParam("language") String language){
return modifyProperty(EXO_TITLE, newTitle, repositoryName, workspaceName, nodeUIID, siteName, language);
}
/**
* SERVICE: Edit summary of document.
*
* @param newSummary the new summary of document
* @param repositoryName the repository name
* @param workspaceName the workspace name
* @param nodeUIID the UIID of node
* @param siteName the site name
*
* @return the response
*/
@POST
@Path("/summary/")
public Response editSummary(@FormParam("newValue") String newSummary,
@QueryParam("repositoryName") String repositoryName,
@QueryParam("workspaceName") String workspaceName,
@QueryParam("nodeUIID") String nodeUIID,
@QueryParam("siteName") String siteName,
@QueryParam("language") String language){
return modifyProperty(EXO_SUMMARY, newSummary, repositoryName, workspaceName, nodeUIID, siteName, language);
}
/**
* SERVICE: Edit summary of document.
*
* @param newValue the new summary of document
* @param repositoryName the repository name
* @param workspaceName the workspace name
* @param nodeUIID the UIID of node
* @param siteName the site name
*
* @return the response
*/
@POST
@Path("/text/")
public Response editText( @FormParam("newValue") String newValue,
@QueryParam("repositoryName") String repositoryName,
@QueryParam("workspaceName") String workspaceName,
@QueryParam("nodeUIID") String nodeUIID,
@QueryParam("siteName") String siteName,
@QueryParam("language") String language){
return modifyProperty(EXO_TEXT, newValue, repositoryName, workspaceName, nodeUIID, siteName, language);
}
/**
* SERVICE: Edit value of any property
*
* @param propertyName
* @param newValue
* @param repositoryName the repository name
* @param workspaceName the workspace name
* @param nodeUIID the UIID of node
* @param siteName the site name
* @param language
* @return the response
*/
@POST
@Path("/property/")
public Response editProperty( @QueryParam("propertyName") String propertyName,
@FormParam("newValue") String newValue,
@QueryParam("repositoryName") String repositoryName,
@QueryParam("workspaceName") String workspaceName,
@QueryParam("nodeUIID") String nodeUIID,
@QueryParam("siteName") String siteName,
@QueryParam("language") String language){
String decodedPropertyName = Text.unescapeIllegalJcrChars(propertyName);
return modifyProperty(decodedPropertyName, newValue, repositoryName, workspaceName, nodeUIID, siteName, language);
}
/**
* Edit generic property of document.
* @param propertyName property that need to edit
* @param newValue the new 'requested property' of document
* @param repositoryName the repository name
* @param workspaceName the workspace name
* @param nodeUIID the UIID of node
* @param siteName the site name
*
* @return the response
*/
public Response modifyProperty(String propertyName, String newValue, String repositoryName, String workspaceName,
String nodeUIID,String siteName, String language){
ResourceBundle resourceBundle = null;
String messageKey = "";
String message = "";
Document document = null;
Element localeMsg = null;
try {
Locale locale = new Locale(language);
ResourceBundleService resourceBundleService = WCMCoreUtils.getService(ResourceBundleService.class);
resourceBundle = resourceBundleService.getResourceBundle(localeFile, locale);
} catch(Exception ex) {
if (LOG.isErrorEnabled()) {
LOG.error("Error when perform create ResourceBundle: ", ex);
}
}
try {
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
} catch(Exception ex) {
if (LOG.isErrorEnabled()) {
LOG.error("Error when perform create Document object: ", ex);
}
}
CacheControl cacheControl = new CacheControl();
cacheControl.setNoCache(true);
cacheControl.setNoStore(true);
try {
SessionProvider sessionProvider = WCMCoreUtils.getUserSessionProvider();
RepositoryService repositoryService = WCMCoreUtils.getService(RepositoryService.class);
ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
Session session = sessionProvider.getSession(workspaceName, manageableRepository);
try {
localeMsg = document.createElement("bundle");
Node node = session.getNodeByUUID(nodeUIID);
node = (Node)session.getItem(node.getPath());
if(canSetProperty(node)) {
if (!sameValue(newValue, node, propertyName)) {
if (newValue.length() > 0) {
newValue = Text.unescapeIllegalJcrChars(newValue.trim());
PortalContainerInfo containerInfo = WCMCoreUtils.getService(PortalContainerInfo.class);
String containerName = containerInfo.getContainerName();
ListenerService listenerService = WCMCoreUtils.getService(ListenerService.class, containerName);
if (propertyName.equals(EXO_TITLE)) {
if (!node.hasProperty(EXO_TITLE))
node.addMixin(EXO_RSS_ENABLE);
}
if (!propertyName.contains("/")) {
if (node.hasProperty(propertyName) && node.getProperty(propertyName).getDefinition().isMultiple()) {
Value[] currentValue = node.getProperty(propertyName).getValues();
if (currentValue==null) currentValue = new Value[1];
currentValue[0] = session.getValueFactory().createValue(newValue);
node.setProperty(propertyName, currentValue);
}else {
node.setProperty(propertyName, newValue);
}
} else {
int iSlash = propertyName.lastIndexOf("/");
String subnodePath = propertyName.substring(0, iSlash);
String subnodeProperty = propertyName.substring(iSlash+1);
Node subnode = node.getNode(subnodePath);
if (subnode.hasProperty(subnodeProperty) && subnode.getProperty(subnodeProperty).getDefinition().isMultiple()) {
Value[] currentValue = subnode.getProperty(subnodeProperty).getValues();
if (currentValue==null) currentValue = new Value[1];
currentValue[0] = session.getValueFactory().createValue(newValue);
subnode.setProperty(subnodeProperty, currentValue);
} else {
subnode.setProperty(subnodeProperty, newValue);
}
}
ConversationState conversationState = ConversationState.getCurrent();
conversationState.setAttribute("siteName", siteName);
listenerService.broadcast(POST_EDIT_CONTENT_EVENT, null, node);
session.save();
}
}
} else {
messageKey = "AccessDeniedException.msg";
message = resourceBundle.getString(messageKey);
localeMsg.setAttribute("message", message);
document.appendChild(localeMsg);
return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
}
} catch (AccessDeniedException ace) {
if (LOG.isErrorEnabled()) {
LOG.error("AccessDeniedException: ", ace);
}
messageKey = "AccessDeniedException.msg";
message = resourceBundle.getString(messageKey);
localeMsg.setAttribute("message", message);
document.appendChild(localeMsg);
return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
} catch (FileNotFoundException fie) {
if (LOG.isErrorEnabled()) {
LOG.error("FileNotFoundException: ", fie);
}
messageKey = "ItemNotFoundException.msg";
message = resourceBundle.getString(messageKey);
localeMsg.setAttribute("message", message);
document.appendChild(localeMsg);
return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
} catch (LockException lockex) {
if (LOG.isErrorEnabled()) {
LOG.error("LockException", lockex);
}
messageKey = "LockException.msg";
message = resourceBundle.getString(messageKey);
localeMsg.setAttribute("message", message);
document.appendChild(localeMsg);
return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
}
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Error when perform edit title: ", e);
}
messageKey = "UIPresentation.label.Exception";
message = resourceBundle.getString(messageKey);
localeMsg.setAttribute("message", message);
document.appendChild(localeMsg);
return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
}
localeMsg.setAttribute("message", "OK");
document.appendChild(localeMsg);
return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
}
/**
* Compare new value with current value property
*
* @param newValue the new value of property
* @param node the document node
*
* @return the result of compare
*
* @throws Exception the exception
*/
private boolean sameValue(String newValue, Node node, String propertyName) throws Exception {
if (!node.hasProperty(propertyName))
return (newValue == null || newValue.length() == 0);
if (node.getProperty(propertyName).getDefinition().isMultiple()){
try {
return node.getProperty(propertyName).getValues()[0].getString().equals(newValue);
}catch (Exception e) {
return false;
}
}
return node.getProperty(propertyName).getString().equals(newValue);
}
/**
* Can set property.
*
* @param node the node
* @return true, if successful
* @throws RepositoryException the repository exception
*/
public static boolean canSetProperty(Node node) throws RepositoryException {
return checkPermission(node,PermissionType.SET_PROPERTY);
}
private static boolean checkPermission(Node node,String permissionType) throws RepositoryException {
try {
((ExtendedNode)node).checkPermission(permissionType);
return true;
} catch(AccessControlException e) {
return false;
}
}
}