View Javadoc
1   package org.exoplatform.wcm.connector.collaboration;
2   
3   import java.io.FileNotFoundException;
4   import java.security.AccessControlException;
5   import java.util.Locale;
6   import java.util.ResourceBundle;
7   
8   import javax.jcr.AccessDeniedException;
9   import javax.jcr.Node;
10  import javax.jcr.RepositoryException;
11  import javax.jcr.Session;
12  import javax.jcr.Value;
13  import javax.jcr.lock.LockException;
14  import javax.ws.rs.FormParam;
15  import javax.ws.rs.POST;
16  import javax.ws.rs.Path;
17  import javax.ws.rs.QueryParam;
18  import javax.ws.rs.core.CacheControl;
19  import javax.ws.rs.core.MediaType;
20  import javax.ws.rs.core.Response;
21  import javax.xml.parsers.DocumentBuilderFactory;
22  import javax.xml.transform.dom.DOMSource;
23  
24  import org.exoplatform.container.xml.PortalContainerInfo;
25  import org.exoplatform.ecm.utils.text.Text;
26  import org.exoplatform.services.jcr.RepositoryService;
27  import org.exoplatform.services.jcr.access.PermissionType;
28  import org.exoplatform.services.jcr.core.ExtendedNode;
29  import org.exoplatform.services.jcr.core.ManageableRepository;
30  import org.exoplatform.services.jcr.ext.common.SessionProvider;
31  import org.exoplatform.services.listener.ListenerService;
32  import org.exoplatform.services.log.ExoLogger;
33  import org.exoplatform.services.log.Log;
34  import org.exoplatform.services.resources.ResourceBundleService;
35  import org.exoplatform.services.rest.resource.ResourceContainer;
36  import org.exoplatform.services.security.ConversationState;
37  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
38  import org.w3c.dom.Document;
39  import org.w3c.dom.Element;
40  
41  
42  
43  /**
44   * Created by The eXo Platform SEA
45   * Author : Ha Quang Tan
46   * tan.haquang@exoplatform.com
47   * Mar 23, 2011
48   */
49  @Path("/contents/editing/")
50  public class InlineEditingService implements ResourceContainer{
51    private static final Log LOG = ExoLogger.getLogger(InlineEditingService.class.getName());
52  
53    final static public String EXO_TITLE               = "exo:title";
54  
55    final static public String EXO_SUMMARY             = "exo:summary";
56  
57    final static public String EXO_TEXT                = "exo:text";
58  
59    final static public String EXO_RSS_ENABLE          = "exo:rss-enable";
60  
61    public final static String POST_EDIT_CONTENT_EVENT = "CmsService.event.postEdit";
62  
63    private final String       localeFile              = "locale.portlet.i18n.WebUIDms";
64    /**
65     * SERVICE: Edit title of document.
66     *
67     * @param newTitle the new title of document
68     * @param repositoryName the repository name
69     * @param workspaceName the workspace name
70     * @param nodeUIID the UIID of node
71     * @param siteName the site name
72     *
73     * @return the response
74     */
75    @POST
76    @Path("/title/")
77    public Response editTitle(@FormParam("newValue") String newTitle,
78                              @QueryParam("repositoryName") String repositoryName,
79                              @QueryParam("workspaceName") String workspaceName,
80                              @QueryParam("nodeUIID") String  nodeUIID,
81                              @QueryParam("siteName") String  siteName,
82                              @QueryParam("language") String  language){
83      return modifyProperty(EXO_TITLE, newTitle, repositoryName, workspaceName, nodeUIID, siteName, language);
84    }
85  
86    /**
87     * SERVICE: Edit summary of document.
88     *
89     * @param newSummary the new summary of document
90     * @param repositoryName the repository name
91     * @param workspaceName the workspace name
92     * @param nodeUIID the UIID of node
93     * @param siteName the site name
94     *
95     * @return the response
96     */
97    @POST
98    @Path("/summary/")
99    public Response editSummary(@FormParam("newValue") String newSummary,
100                               @QueryParam("repositoryName") String repositoryName,
101                               @QueryParam("workspaceName") String workspaceName,
102                               @QueryParam("nodeUIID") String  nodeUIID,
103                               @QueryParam("siteName") String  siteName,
104                               @QueryParam("language") String  language){
105     return modifyProperty(EXO_SUMMARY, newSummary, repositoryName, workspaceName, nodeUIID, siteName, language);
106   }
107   /**
108    * SERVICE: Edit summary of document.
109    *
110    * @param newValue the new summary of document
111    * @param repositoryName the repository name
112    * @param workspaceName the workspace name
113    * @param nodeUIID the UIID of node
114    * @param siteName the site name
115    *
116    * @return the response
117    */
118   @POST
119   @Path("/text/")
120   public Response editText( @FormParam("newValue") String newValue,
121                             @QueryParam("repositoryName") String repositoryName,
122                             @QueryParam("workspaceName") String workspaceName,
123                             @QueryParam("nodeUIID") String  nodeUIID,
124                             @QueryParam("siteName") String  siteName,
125                             @QueryParam("language") String  language){
126     return modifyProperty(EXO_TEXT, newValue, repositoryName, workspaceName, nodeUIID, siteName, language);
127   }
128 
129   /**
130    * SERVICE: Edit value of any property
131    *
132    * @param propertyName
133    * @param newValue
134    * @param repositoryName the repository name
135    * @param workspaceName the workspace name
136    * @param nodeUIID the UIID of node
137    * @param siteName the site name
138    * @param language
139    * @return the response
140    */
141   @POST
142   @Path("/property/")
143   public Response editProperty( @QueryParam("propertyName") String propertyName,
144                                 @FormParam("newValue") String newValue,
145                                 @QueryParam("repositoryName") String repositoryName,
146                                 @QueryParam("workspaceName") String workspaceName,
147                                 @QueryParam("nodeUIID") String  nodeUIID,
148                                 @QueryParam("siteName") String  siteName,
149                                 @QueryParam("language") String  language){
150     String decodedPropertyName =  Text.unescapeIllegalJcrChars(propertyName);
151     return modifyProperty(decodedPropertyName, newValue, repositoryName, workspaceName, nodeUIID, siteName, language);
152   }
153 
154   /**
155    * Edit generic property of document.
156    * @param propertyName property that need to edit
157    * @param newValue the new 'requested property' of document
158    * @param repositoryName the repository name
159    * @param workspaceName the workspace name
160    * @param nodeUIID the UIID of node
161    * @param siteName the site name
162    *
163    * @return the response
164    */
165   public Response modifyProperty(String propertyName, String newValue, String repositoryName, String workspaceName,
166       String nodeUIID,String siteName, String language){
167     ResourceBundle resourceBundle = null;
168     String messageKey = "";
169     String message = "";
170     Document document = null;
171     Element localeMsg = null;
172     try {
173       Locale locale = new Locale(language);
174       ResourceBundleService resourceBundleService = WCMCoreUtils.getService(ResourceBundleService.class);
175       resourceBundle = resourceBundleService.getResourceBundle(localeFile, locale);
176     } catch(Exception ex) {
177       if (LOG.isErrorEnabled()) {
178         LOG.error("Error when perform create ResourceBundle: ", ex);
179       }
180     }
181     try {
182       document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
183     } catch(Exception ex) {
184       if (LOG.isErrorEnabled()) {
185         LOG.error("Error when perform create Document object: ", ex);
186       }
187     }
188     CacheControl cacheControl = new CacheControl();
189     cacheControl.setNoCache(true);
190     cacheControl.setNoStore(true);
191     try {
192       SessionProvider sessionProvider = WCMCoreUtils.getUserSessionProvider();
193       RepositoryService repositoryService = WCMCoreUtils.getService(RepositoryService.class);
194       ManageableRepository manageableRepository = repositoryService.getCurrentRepository();
195       Session session = sessionProvider.getSession(workspaceName, manageableRepository);
196       try {
197         localeMsg = document.createElement("bundle");
198         Node node = session.getNodeByUUID(nodeUIID);
199         node = (Node)session.getItem(node.getPath());
200         if(canSetProperty(node)) {
201           if (!sameValue(newValue, node, propertyName)) {
202             if (newValue.length() > 0) {
203               newValue = Text.unescapeIllegalJcrChars(newValue.trim());
204               PortalContainerInfo containerInfo = WCMCoreUtils.getService(PortalContainerInfo.class);
205               String containerName = containerInfo.getContainerName();
206               ListenerService listenerService = WCMCoreUtils.getService(ListenerService.class, containerName);
207               if (propertyName.equals(EXO_TITLE)) {
208                 if (!node.hasProperty(EXO_TITLE))
209                   node.addMixin(EXO_RSS_ENABLE);
210               }
211               if (!propertyName.contains("/")) {
212                 if (node.hasProperty(propertyName) && node.getProperty(propertyName).getDefinition().isMultiple()) {
213                   Value[] currentValue = node.getProperty(propertyName).getValues();
214                   if (currentValue==null) currentValue = new Value[1];
215                   currentValue[0] = session.getValueFactory().createValue(newValue);
216                   node.setProperty(propertyName, currentValue);
217                 }else {
218                   node.setProperty(propertyName, newValue);
219                 }
220               } else {
221                 int iSlash = propertyName.lastIndexOf("/");
222                 String subnodePath = propertyName.substring(0, iSlash);
223                 String subnodeProperty = propertyName.substring(iSlash+1);
224                 Node subnode = node.getNode(subnodePath);
225                 if (subnode.hasProperty(subnodeProperty) && subnode.getProperty(subnodeProperty).getDefinition().isMultiple()) {
226                   Value[] currentValue = subnode.getProperty(subnodeProperty).getValues();
227                   if (currentValue==null) currentValue = new Value[1];
228                   currentValue[0] = session.getValueFactory().createValue(newValue);
229                   subnode.setProperty(subnodeProperty, currentValue);
230                 } else {
231                   subnode.setProperty(subnodeProperty, newValue);
232                 }
233               }
234               ConversationState conversationState = ConversationState.getCurrent();
235               conversationState.setAttribute("siteName", siteName);
236               listenerService.broadcast(POST_EDIT_CONTENT_EVENT, null, node);
237               node.save();
238             }
239           }
240         } else {
241           messageKey = "AccessDeniedException.msg";
242           message = resourceBundle.getString(messageKey);
243           localeMsg.setAttribute("message", message);
244           document.appendChild(localeMsg);
245           return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
246         }
247       } catch (AccessDeniedException ace) {
248         if (LOG.isErrorEnabled()) {
249           LOG.error("AccessDeniedException: ", ace);
250         }
251         messageKey = "AccessDeniedException.msg";
252         message = resourceBundle.getString(messageKey);
253         localeMsg.setAttribute("message", message);
254         document.appendChild(localeMsg);
255         return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
256       } catch (FileNotFoundException fie) {
257         if (LOG.isErrorEnabled()) {
258           LOG.error("FileNotFoundException: ", fie);
259         }
260         messageKey = "ItemNotFoundException.msg";
261         message = resourceBundle.getString(messageKey);
262         localeMsg.setAttribute("message", message);
263         document.appendChild(localeMsg);
264         return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
265       }  catch (LockException lockex) {
266         if (LOG.isErrorEnabled()) {
267           LOG.error("LockException", lockex);
268         }
269         messageKey = "LockException.msg";
270         message = resourceBundle.getString(messageKey);
271         localeMsg.setAttribute("message", message);
272         document.appendChild(localeMsg);
273         return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
274       }
275     } catch (Exception e) {
276       if (LOG.isErrorEnabled()) {
277         LOG.error("Error when perform edit title: ", e);
278       }
279       messageKey = "UIPresentation.label.Exception";
280       message = resourceBundle.getString(messageKey);
281       localeMsg.setAttribute("message", message);
282       document.appendChild(localeMsg);
283       return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
284     }
285     localeMsg.setAttribute("message", "OK");
286     document.appendChild(localeMsg);
287     return Response.ok(new DOMSource(document), MediaType.TEXT_XML).cacheControl(cacheControl).build();
288   }
289   /**
290    * Compare new value with current value property
291    *
292    * @param newValue the new value of property
293    * @param node the document node
294    *
295    * @return the result of compare
296    *
297    * @throws Exception the exception
298    */
299   private boolean sameValue(String newValue, Node node, String propertyName) throws Exception {
300     if (!node.hasProperty(propertyName))
301       return (newValue == null || newValue.length() == 0);
302     if (node.getProperty(propertyName).getDefinition().isMultiple()){
303       try {
304         return node.getProperty(propertyName).getValues()[0].getString().equals(newValue);
305       }catch (Exception e) {
306         return false;
307       }
308     }
309     return node.getProperty(propertyName).getString().equals(newValue);
310   }
311 
312   /**
313    * Can set property.
314    *
315    * @param node the node
316    * @return true, if successful
317    * @throws RepositoryException the repository exception
318    */
319   public static boolean canSetProperty(Node node) throws RepositoryException {
320     return checkPermission(node,PermissionType.SET_PROPERTY);
321   }
322 
323   private static boolean checkPermission(Node node,String permissionType) throws RepositoryException {
324     try {
325       ((ExtendedNode)node).checkPermission(permissionType);
326       return true;
327     } catch(AccessControlException e) {
328       return false;
329     }
330   }
331 }