View Javadoc
1   /*
2    * Copyright (C) 2003-2009 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.cms.webdav;
18  
19  import java.io.InputStream;
20  import java.net.URI;
21  import java.net.URISyntaxException;
22  import java.util.GregorianCalendar;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Queue;
26  
27  import javax.jcr.Item;
28  import javax.jcr.NoSuchWorkspaceException;
29  import javax.jcr.Node;
30  import javax.jcr.NodeIterator;
31  import javax.jcr.PathNotFoundException;
32  import javax.jcr.RepositoryException;
33  import javax.jcr.Session;
34  import javax.ws.rs.DELETE;
35  import javax.ws.rs.GET;
36  import javax.ws.rs.HEAD;
37  import javax.ws.rs.HeaderParam;
38  import javax.ws.rs.PUT;
39  import javax.ws.rs.Path;
40  import javax.ws.rs.PathParam;
41  import javax.ws.rs.QueryParam;
42  import javax.ws.rs.core.Context;
43  import javax.ws.rs.core.MediaType;
44  import javax.ws.rs.core.MultivaluedMap;
45  import javax.ws.rs.core.Response;
46  import javax.ws.rs.core.UriInfo;
47  
48  import org.apache.commons.lang.StringUtils;
49  import org.exoplatform.common.http.HTTPStatus;
50  import org.exoplatform.common.util.HierarchicalProperty;
51  import org.exoplatform.commons.utils.MimeTypeResolver;
52  import org.exoplatform.container.xml.InitParams;
53  import org.exoplatform.ecm.utils.text.Text;
54  import org.exoplatform.services.cms.CmsService;
55  import org.exoplatform.services.cms.documents.AutoVersionService;
56  import org.exoplatform.services.cms.drives.DriveData;
57  import org.exoplatform.services.cms.drives.ManageDriveService;
58  import org.exoplatform.services.cms.impl.Utils;
59  import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService;
60  import org.exoplatform.services.cms.link.LinkUtils;
61  import org.exoplatform.services.cms.link.NodeFinder;
62  import org.exoplatform.services.jcr.RepositoryService;
63  import org.exoplatform.services.jcr.ext.app.ThreadLocalSessionProviderService;
64  import org.exoplatform.services.jcr.webdav.util.InitParamsDefaults;
65  import org.exoplatform.services.jcr.webdav.util.TextUtil;
66  import org.exoplatform.services.listener.ListenerService;
67  import org.exoplatform.services.log.ExoLogger;
68  import org.exoplatform.services.log.Log;
69  import org.exoplatform.services.rest.ExtHttpHeaders;
70  import org.exoplatform.services.rest.ext.webdav.method.ACL;
71  import org.exoplatform.services.rest.ext.webdav.method.CHECKIN;
72  import org.exoplatform.services.rest.ext.webdav.method.CHECKOUT;
73  import org.exoplatform.services.rest.ext.webdav.method.COPY;
74  import org.exoplatform.services.rest.ext.webdav.method.LOCK;
75  import org.exoplatform.services.rest.ext.webdav.method.MKCOL;
76  import org.exoplatform.services.rest.ext.webdav.method.MOVE;
77  import org.exoplatform.services.rest.ext.webdav.method.OPTIONS;
78  import org.exoplatform.services.rest.ext.webdav.method.ORDERPATCH;
79  import org.exoplatform.services.rest.ext.webdav.method.PROPFIND;
80  import org.exoplatform.services.rest.ext.webdav.method.PROPPATCH;
81  import org.exoplatform.services.rest.ext.webdav.method.REPORT;
82  import org.exoplatform.services.rest.ext.webdav.method.SEARCH;
83  import org.exoplatform.services.rest.ext.webdav.method.UNCHECKOUT;
84  import org.exoplatform.services.rest.ext.webdav.method.UNLOCK;
85  import org.exoplatform.services.rest.ext.webdav.method.VERSIONCONTROL;
86  import org.exoplatform.services.rest.impl.MultivaluedMapImpl;
87  import org.exoplatform.services.wcm.core.NodetypeConstant;
88  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
89  
90  /**
91   * This class is used to override the default WebDavServiceImpl in order to support symlinks
92   *
93   * Created by The eXo Platform SAS
94   * Author : eXoPlatform
95   *          nicolas.filotto@exoplatform.com
96   * 9 avr. 2009
97   */
98  @Path("/jcr/")
99  public class WebDavServiceImpl extends org.exoplatform.services.jcr.webdav.WebDavServiceImpl {
100 
101   /**
102    * Logger.
103    */
104   private static final Log LOG = ExoLogger.getLogger(WebDavServiceImpl.class.getName());
105 
106   private final String POST_UPLOAD_CONTENT_EVENT = "WebDavService.event.postUpload";
107 
108   private final String PERSONAL_DRIVE_PREFIX = "/Users/${userId}/Private";
109 
110   private final String GROUP_DRIVE_PREFIX = "/Groups${groupId}/Documents";
111 
112   private final String PERSONAL_GROUP_DRIVE_WORKSPACE = "collaboration";
113 
114 
115    private final NodeFinder nodeFinder;
116 
117   private final RepositoryService repositoryService;
118 
119   private ListenerService listenerService;
120 
121   private final MimeTypeResolver mimeTypeResolver;
122 
123    public WebDavServiceImpl(InitParams params,
124                             RepositoryService repositoryService,
125                             ThreadLocalSessionProviderService sessionProviderService,
126                             NodeFinder nodeFinder, AutoVersionService autoVersionService, ManageDriveService manageDriveService) throws Exception
127    {
128       super(params, repositoryService, sessionProviderService);
129       this.repositoryService = repositoryService;
130       this.nodeFinder = nodeFinder;
131       this.listenerService = WCMCoreUtils.getService(ListenerService.class);
132       this.mimeTypeResolver = new MimeTypeResolver();
133       this.mimeTypeResolver.setDefaultMimeType(InitParamsDefaults.FILE_MIME_TYPE);
134 
135       List<String> lstDriveAutoVersion = autoVersionService.getDriveAutoVersion();
136       MultivaluedMap<String, String> allowedAutoVersionPath = new MultivaluedMapImpl();
137       if (!lstDriveAutoVersion.isEmpty())
138       {
139          for (String driverName : lstDriveAutoVersion)
140          {
141             DriveData driveData = manageDriveService.getDriveByName(StringUtils.trim(driverName));
142             if (driveData != null)
143             {
144                String driveHome = driveData.getHomePath();
145                String workspace = driveData.getWorkspace();
146 
147                if (driveHome.startsWith(PERSONAL_DRIVE_PREFIX) && PERSONAL_GROUP_DRIVE_WORKSPACE.equals(workspace))
148                {
149                   allowedAutoVersionPath.add(driveData.getWorkspace(), "/Users");
150                }
151                else if (driveHome.startsWith(GROUP_DRIVE_PREFIX) && PERSONAL_GROUP_DRIVE_WORKSPACE.equals(workspace))
152                {
153                   allowedAutoVersionPath.add(driveData.getWorkspace(), "/Groups");
154                }
155                else
156                {
157                   allowedAutoVersionPath.add(driveData.getWorkspace(), driveHome);
158                }
159             }
160          }
161       }
162       webDavServiceInitParams.setAllowedAutoVersionPath(allowedAutoVersionPath);
163       webDavServiceInitParams.setEnableAutoVersion(true);
164    }
165 
166   private String getRealDestinationHeader(String baseURI, String repoName, String destinationHeader) {
167     String serverURI = baseURI + "/jcr/" + repoName;
168 
169     destinationHeader = TextUtil.unescape(destinationHeader, '%');
170 
171     if (!destinationHeader.startsWith(serverURI)) {
172       return null;
173     }
174 
175     String destPath = destinationHeader.substring(serverURI.length() + 1);
176 
177     try {
178       Item item = nodeFinder.getItem(workspaceName(destPath),
179                                      LinkUtils.getParentPath(path(destPath)),
180                                      true);
181       return item.getSession().getWorkspace().getName()
182           + LinkUtils.createPath(item.getPath(), LinkUtils.getItemName(path(destPath)));
183     } catch (RepositoryException e) {
184       if (LOG.isWarnEnabled()) {
185         LOG.warn("Cannot find the item at " + repoName + "/" + destPath, e);
186       }
187       return null;
188     }
189   }
190 
191   @CHECKIN
192   @Path("/{repoName}/{repoPath:.*}/")
193   public Response checkin(@PathParam("repoName") String repoName,
194                           @PathParam("repoPath") String repoPath,
195                           @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
196                           @HeaderParam(ExtHttpHeaders.IF) String ifHeader) {
197 
198     try {
199       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
200       repoPath = convertRepoPath(repoPath, true);
201     } catch (PathNotFoundException exc) {
202       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
203     } catch (NoSuchWorkspaceException exc) {
204       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
205     } catch (Exception e) {
206       if (LOG.isWarnEnabled()) {
207         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
208       }
209       return Response.serverError().build();
210     }
211     return super.checkin(repoName, repoPath, lockTokenHeader, ifHeader);
212   }
213 
214   @CHECKOUT
215   @Path("/{repoName}/{repoPath:.*}/")
216   public Response checkout(@PathParam("repoName") String repoName,
217                            @PathParam("repoPath") String repoPath,
218                            @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
219                            @HeaderParam(ExtHttpHeaders.IF) String ifHeader) {
220     try {
221       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
222       repoPath = convertRepoPath(repoPath, true);
223     } catch (PathNotFoundException exc) {
224       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
225     } catch (NoSuchWorkspaceException exc) {
226       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
227     } catch (Exception e) {
228       if (LOG.isWarnEnabled()) {
229         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
230       }
231       return Response.serverError().build();
232     }
233     return super.checkout(repoName, repoPath, lockTokenHeader, ifHeader);
234   }
235 
236   @COPY
237   @Path("/{repoName}/{repoPath:.*}/")
238   public Response copy(@PathParam("repoName") String repoName,
239                        @PathParam("repoPath") String repoPath,
240                        @HeaderParam(ExtHttpHeaders.DESTINATION) String destinationHeader,
241                        @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
242                        @HeaderParam(ExtHttpHeaders.IF) String ifHeader,
243                        @HeaderParam(ExtHttpHeaders.DEPTH) String depthHeader,
244                        @HeaderParam(ExtHttpHeaders.OVERWRITE) String overwriteHeader,
245                        @Context UriInfo uriInfo,
246                        HierarchicalProperty body) {
247 
248     try {
249       repoPath = convertRepoPath(repoPath, false);
250     } catch (PathNotFoundException exc) {
251       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
252     } catch (NoSuchWorkspaceException exc) {
253       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
254     } catch (Exception e) {
255       if (LOG.isWarnEnabled()) {
256         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
257       }
258       return Response.serverError().build();
259     }
260     String realDestinationHeader = getRealDestinationHeader(uriInfo.getPath(), repoName, destinationHeader);
261     if (realDestinationHeader != null) {
262       destinationHeader = realDestinationHeader;
263     }
264     return super.copy(repoName,
265                       repoPath,
266                       destinationHeader,
267                       lockTokenHeader,
268                       ifHeader,
269                       depthHeader,
270                       overwriteHeader,
271                       uriInfo,
272                       body);
273   }
274 
275   @GET
276   @Path("/{repoName}/{repoPath:.*}/")
277   public Response get(@PathParam("repoName") String repoName,
278                       @PathParam("repoPath") String repoPath,
279                       @HeaderParam(ExtHttpHeaders.RANGE) String rangeHeader,
280                       @HeaderParam(ExtHttpHeaders.IF_MODIFIED_SINCE) String ifModifiedSince,
281                       @HeaderParam(ExtHttpHeaders.IF_NONE_MATCH) String ifNoneMatch,
282                       @QueryParam("version") String version,
283                       @Context UriInfo uriInfo) {
284 
285     try {
286       repoPath = convertRepoPath(repoPath, true);
287     } catch (PathNotFoundException exc) {
288       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
289     } catch (NoSuchWorkspaceException exc) {
290       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
291     } catch (Exception e) {
292       if (LOG.isWarnEnabled()) {
293         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
294       }
295       return Response.serverError().build();
296     }
297     Response response = super.get(repoName, repoPath, rangeHeader, ifModifiedSince, ifNoneMatch, version, uriInfo);
298     if(HTTPStatus.OK == response.getStatus()) {
299       return Response.fromResponse(response)
300               .header("Access-Control-Allow-Origin", uriInfo.getRequestUri().getHost())
301               .header("Access-Control-Allow-Credentials", true)
302               .header("Access-Control-Allow-Methods", "ACL, CANCELUPLOAD, CHECKIN, CHECKOUT, COPY, DELETE, GET, HEAD, LOCK, MKCALENDAR, MKCOL, " +
303                       "MOVE, OPTIONS, POST, PROPFIND, PROPPATCH, PUT, REPORT, SEARCH, UNCHECKOUT, UNLOCK, UPDATE, VERSION-CONTROL")
304               .header("Access-Control-Allow-Headers", "Overwrite, Destination, Content-Type, Depth, User-Agent, Translate, Range, Content-Range," +
305                       " Timeout, X-File-Size, X-Requested-With, If-Modified-Since, X-File-Name, Cache-Control, Location, Lock-Token, If")
306               .header("Access-Control-Expose-Header", "DAV, content-length, Allow")
307               .header("Access-Control-Max-Age", 3600)
308               .build();
309     }
310     return response;
311   }
312 
313   @HEAD
314   @Path("/{repoName}/{repoPath:.*}/")
315   public Response head(@PathParam("repoName") String repoName,
316                        @PathParam("repoPath") String repoPath,
317                        @Context UriInfo uriInfo) {
318 
319     try {
320       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
321       repoPath = convertRepoPath(repoPath, true);
322     } catch (PathNotFoundException exc) {
323       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
324     } catch (NoSuchWorkspaceException exc) {
325       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
326     } catch (Exception e) {
327       if (LOG.isWarnEnabled()) {
328         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
329       }
330       return Response.serverError().build();
331     }
332     return super.head(repoName, repoPath, uriInfo);
333   }
334 
335   @LOCK
336   @Path("/{repoName}/{repoPath:.*}/")
337   public Response lock(@PathParam("repoName") String repoName,
338                        @PathParam("repoPath") String repoPath,
339                        @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
340                        @HeaderParam(ExtHttpHeaders.IF) String ifHeader,
341                        @HeaderParam(ExtHttpHeaders.DEPTH) String depthHeader,
342                        HierarchicalProperty body) {
343 
344     try {
345       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
346       repoPath = convertRepoPath(repoPath, true);
347     } catch (PathNotFoundException exc) {
348       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
349     } catch (NoSuchWorkspaceException exc) {
350       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
351     } catch (Exception e) {
352       if (LOG.isWarnEnabled()) {
353         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
354       }
355       return Response.serverError().build();
356     }
357     return super.lock(repoName, repoPath, lockTokenHeader, ifHeader, depthHeader, body);
358   }
359 
360   @UNLOCK
361   @Path("/{repoName}/{repoPath:.*}/")
362   public Response unlock(@PathParam("repoName") String repoName,
363                          @PathParam("repoPath") String repoPath,
364                          @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
365                          @HeaderParam(ExtHttpHeaders.IF) String ifHeader) {
366 
367     try {
368       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
369       repoPath = convertRepoPath(repoPath, true);
370     } catch (PathNotFoundException exc) {
371       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
372     } catch (NoSuchWorkspaceException exc) {
373       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
374     } catch (Exception e) {
375       if (LOG.isWarnEnabled()) {
376         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
377       }
378       return Response.serverError().build();
379     }
380     return super.unlock(repoName, repoPath, lockTokenHeader, ifHeader);
381   }
382 
383   @OPTIONS
384   @Path("/{repoName}/{path:.*}/")
385   public Response options(@PathParam("path") String path) {
386     return super.options(path);
387   }
388 
389   @ORDERPATCH
390   @Path("/{repoName}/{repoPath:.*}/")
391   public Response order(@PathParam("repoName") String repoName,
392                         @PathParam("repoPath") String repoPath,
393                         @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
394                         @HeaderParam(ExtHttpHeaders.IF) String ifHeader,
395                         @Context UriInfo uriInfo,
396                         HierarchicalProperty body) {
397 
398     try {
399       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
400       repoPath = convertRepoPath(repoPath, true);
401     } catch (PathNotFoundException exc) {
402       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
403     } catch (NoSuchWorkspaceException exc) {
404       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
405     } catch (Exception e) {
406       if (LOG.isWarnEnabled()) {
407         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
408       }
409       return Response.serverError().build();
410     }
411     return super.order(repoName, repoPath, lockTokenHeader, ifHeader, uriInfo, body);
412   }
413 
414   @PROPFIND
415   @Path("/{repoName}/{repoPath:.*}/")
416   public Response propfind(@PathParam("repoName") String repoName,
417                            @PathParam("repoPath") String repoPath,
418                            @HeaderParam(ExtHttpHeaders.DEPTH) String depthHeader,
419                            @Context UriInfo uriInfo,
420                            HierarchicalProperty body) {
421 
422     try {
423       repoPath = convertRepoPath(repoPath, true);
424     } catch (PathNotFoundException exc) {
425       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
426     } catch (NoSuchWorkspaceException exc) {
427       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
428     } catch (Exception e) {
429       if (LOG.isWarnEnabled()) {
430         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
431       }
432       return Response.serverError().build();
433     }
434     return super.propfind(repoName, repoPath, depthHeader, uriInfo, body);
435   }
436 
437   @PROPPATCH
438   @Path("/{repoName}/{repoPath:.*}/")
439   public Response proppatch(@PathParam("repoName") String repoName,
440                             @PathParam("repoPath") String repoPath,
441                             @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
442                             @HeaderParam(ExtHttpHeaders.IF) String ifHeader,
443                             @Context UriInfo uriInfo,
444                             HierarchicalProperty body) {
445 
446     try {
447       repoPath = convertRepoPath(repoPath, true);
448     } catch (PathNotFoundException exc) {
449       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
450     } catch (NoSuchWorkspaceException exc) {
451       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
452     } catch (Exception e) {
453       if (LOG.isWarnEnabled()) {
454         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
455       }
456       return Response.serverError().build();
457     }
458     return super.proppatch(repoName, repoPath, lockTokenHeader, ifHeader, uriInfo, body);
459   }
460 
461   @PUT
462   @Path("/{repoName}/{repoPath:.*}/")
463   public Response put(@PathParam("repoName") String repoName,
464                       @PathParam("repoPath") String repoPath,
465                       @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
466                       @HeaderParam(ExtHttpHeaders.IF) String ifHeader,
467                       @HeaderParam(ExtHttpHeaders.FILE_NODETYPE) String fileNodeTypeHeader,
468                       @HeaderParam(ExtHttpHeaders.CONTENT_NODETYPE) String nodeTypeHeader,
469                       @HeaderParam(ExtHttpHeaders.CONTENT_MIXINTYPES) String mixinTypes,
470                       @HeaderParam(ExtHttpHeaders.CONTENTTYPE) MediaType mediaType,
471                       @HeaderParam(ExtHttpHeaders.USER_AGENT) String userAgent,
472                       InputStream inputStream,
473                       @Context UriInfo uriInfo) {
474     Session session = null;
475     Item item = null;
476     boolean isCreating = false;
477     ActivityCommonService activityService = null;
478     try {
479       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
480       try {
481         item = nodeFinder.getItem(workspaceName(repoPath),
482                                   LinkUtils.getParentPath(path(normalizePath(repoPath))),
483                                   true);
484         repoPath = item.getSession().getWorkspace().getName()
485             + LinkUtils.createPath(item.getPath(), Text.escapeIllegalJcrChars(LinkUtils.getItemName(path(repoPath))));
486         session = item.getSession();
487       } catch (PathNotFoundException e) {
488         item = nodeFinder.getItem(workspaceName(repoPath),
489                                   LinkUtils.getParentPath(path(Text.escapeIllegalJcrChars(repoPath))),
490                                   true);
491         repoPath = item.getSession().getWorkspace().getName()
492             + LinkUtils.createPath(item.getPath(), Text.escapeIllegalJcrChars(LinkUtils.getItemName(path(repoPath))));
493         session = item.getSession();
494       }
495       activityService = WCMCoreUtils.getService(ActivityCommonService.class);
496       if (!session.itemExists(path(repoPath))) {
497         isCreating = true;
498       }
499     } catch (PathNotFoundException exc) {
500       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
501     } catch (NoSuchWorkspaceException exc) {
502       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
503     } catch (Exception e) {
504       if (LOG.isWarnEnabled()) {
505         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
506       }
507       return Response.serverError().build();
508     }
509 
510     Response res = super.put(repoName,
511                              repoPath,
512                              lockTokenHeader,
513                              ifHeader,
514                              null,
515                              nodeTypeHeader,
516                              mixinTypes,
517                              mediaType,
518                              userAgent,
519                              inputStream,
520                              uriInfo);
521     try {
522 
523       boolean pushAs = markTempFilesToHidden(repoPath);
524       Node currentNode = (Node) session.getItem(path(repoPath));
525       if (isCreating) {
526         if (userAgent!= null && userAgent.contains("Microsoft")) {
527           activityService.setCreating(currentNode, true);
528         }
529       }else {
530         activityService.setCreating(currentNode, false);
531       }
532 
533       try {
534         if(isCreating && pushAs)
535           listenerService.broadcast(ActivityCommonService.FILE_CREATED_ACTIVITY, null, currentNode);
536         
537         if (currentNode.isCheckedOut() && !activityService.isCreating(currentNode) && pushAs)
538           listenerService.broadcast(this.POST_UPLOAD_CONTENT_EVENT, this, currentNode);
539 
540         } catch (Exception e) {
541           if (LOG.isWarnEnabled()) {
542             LOG.warn("Cannot broadcast file create activity for the item at " + currentNode.getPath(), e);
543           }
544         }
545 
546     } catch (PathNotFoundException npfe) {
547       return Response.status(HTTPStatus.NOT_FOUND).entity(npfe.getMessage()).build();
548     } catch (RepositoryException re) {
549       return Response.status(HTTPStatus.NOT_FOUND).entity(re.getMessage()).build();
550     } catch (Exception e) {
551       return Response.serverError().build();
552     }
553 
554     return res;
555   }
556 
557   @REPORT
558   @Path("/{repoName}/{repoPath:.*}/")
559   public Response report(@PathParam("repoName") String repoName,
560                          @PathParam("repoPath") String repoPath,
561                          @HeaderParam(ExtHttpHeaders.DEPTH) String depthHeader,
562                          @Context UriInfo uriInfo,
563                          HierarchicalProperty body) {
564 
565     try {
566       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
567       repoPath = convertRepoPath(repoPath, true);
568     } catch (PathNotFoundException exc) {
569       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
570     } catch (NoSuchWorkspaceException exc) {
571       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
572     } catch (Exception e) {
573       if (LOG.isWarnEnabled()) {
574         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
575       }
576       return Response.serverError().build();
577     }
578     return super.report(repoName, repoPath, depthHeader, uriInfo, body);
579   }
580 
581   @SEARCH
582   @Path("/{repoName}/{repoPath:.*}/")
583   public Response search(@PathParam("repoName") String repoName,
584                          @PathParam("repoPath") String repoPath,
585                          @Context UriInfo uriInfo,
586                          HierarchicalProperty body) {
587 
588     try {
589       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
590       repoPath = convertRepoPath(repoPath, true);
591     } catch (PathNotFoundException exc) {
592       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
593     } catch (NoSuchWorkspaceException exc) {
594       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
595     } catch (Exception e) {
596       if (LOG.isWarnEnabled()) {
597         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
598       }
599       return Response.serverError().build();
600     }
601     return super.search(repoName, repoPath, uriInfo, body);
602   }
603 
604   @UNCHECKOUT
605   @Path("/{repoName}/{repoPath:.*}/")
606   public Response uncheckout(@PathParam("repoName") String repoName,
607                              @PathParam("repoPath") String repoPath,
608                              @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
609                              @HeaderParam(ExtHttpHeaders.IF) String ifHeader) {
610 
611     try {
612       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
613       repoPath = convertRepoPath(repoPath, true);
614     } catch (PathNotFoundException exc) {
615       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
616     } catch (NoSuchWorkspaceException exc) {
617       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
618     } catch (Exception e) {
619       if (LOG.isWarnEnabled()) {
620         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
621       }
622       return Response.serverError().build();
623     }
624     return super.uncheckout(repoName, repoPath, lockTokenHeader, ifHeader);
625   }
626 
627   @VERSIONCONTROL
628   @Path("/{repoName}/{repoPath:.*}/")
629   public Response versionControl(@PathParam("repoName") String repoName,
630                                  @PathParam("repoPath") String repoPath,
631                                  @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
632                                  @HeaderParam(ExtHttpHeaders.IF) String ifHeader) {
633 
634     try {
635       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
636       repoPath = convertRepoPath(repoPath, true);
637     } catch (PathNotFoundException exc) {
638       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
639     } catch (NoSuchWorkspaceException exc) {
640       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
641     } catch (Exception e) {
642       if (LOG.isWarnEnabled()) {
643         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
644       }
645       return Response.serverError().build();
646     }
647     return super.versionControl(repoName, repoPath, lockTokenHeader, ifHeader);
648   }
649 
650   @ACL
651   @Path("/{repoName}/{repoPath:.*}/")
652   public Response acl(@PathParam("repoName") String repoName,
653                       @PathParam("repoPath") String repoPath,
654                       @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
655                       @HeaderParam(ExtHttpHeaders.IF) String ifHeader,
656                       HierarchicalProperty body) {
657     try {
658       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
659       repoPath = convertRepoPath(repoPath, true);
660     } catch (PathNotFoundException exc) {
661       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
662     } catch (NoSuchWorkspaceException exc) {
663       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
664     } catch (Exception e) {
665       if (LOG.isWarnEnabled()) {
666         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
667       }
668       return Response.serverError().build();
669     }
670     return super.acl(repoName, repoPath, lockTokenHeader, ifHeader, body);
671   }
672 
673   @MOVE
674   @Path("/{repoName}/{repoPath:.*}/")
675   public Response move(@PathParam("repoName") String repoName,
676                        @PathParam("repoPath") String repoPath,
677                        @HeaderParam(ExtHttpHeaders.DESTINATION) String destinationHeader,
678                        @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
679                        @HeaderParam(ExtHttpHeaders.IF) String ifHeader,
680                        @HeaderParam(ExtHttpHeaders.DEPTH) String depthHeader,
681                        @HeaderParam(ExtHttpHeaders.OVERWRITE) String overwriteHeader,
682                        @Context UriInfo uriInfo,
683                        HierarchicalProperty body) {
684     try {
685       repoPath = convertRepoPath(repoPath, true);
686     } catch (PathNotFoundException exc) {
687       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
688     } catch (NoSuchWorkspaceException exc) {
689       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
690     } catch (Exception e) {
691       if (LOG.isWarnEnabled()) {
692         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
693       }
694       return Response.serverError().build();
695     }
696     Response response = super.move(repoName,
697                                    repoPath,
698                                    destinationHeader,
699                                    lockTokenHeader,
700                                    ifHeader,
701                                    depthHeader,
702                                    overwriteHeader,
703                                    uriInfo,
704                                    body);
705 
706     if (response.getStatus() == HTTPStatus.CREATED) {
707       updateProperties(destinationHeader, repoName);
708     }
709     markTempFilesToHidden(repoPath);
710     return response;
711   }
712 
713   /**
714    * update exo:name, exo:title and jcr:mimeType when rename a node
715    * 
716    * @param destinationHeader
717    * @param repoName
718    */
719   private void updateProperties(String destinationHeader, String repoName) {
720     try {
721       URI dest = buildURI(destinationHeader);
722       String destPath = dest.getPath();
723       int repoIndex = destPath.indexOf(repoName);
724       destPath = normalizePath(repoIndex == -1 ? destPath : destPath.substring(repoIndex + repoName.length() + 1));
725       String destNodePath = path(destPath);
726       Node destNode = (Node) nodeFinder.getItem(workspaceName(destPath), path(normalizePath(destNodePath)), true);
727       String nodeName = Text.escapeIllegalJcrChars(destNode.getName());
728       destNode.setProperty("exo:name", nodeName);
729       destNode.setProperty("exo:title", nodeName);
730       if (!Utils.isFolder(destNode)) {
731         Node content = destNode.getNode("jcr:content");
732         String mimeType = mimeTypeResolver.getMimeType(nodeName);
733         content.setProperty("jcr:mimeType", mimeType);
734         // Change publication status
735         ListenerService listenerService =  WCMCoreUtils.getService(ListenerService.class);
736         if (destNode.isNodeType("exo:datetime")) {
737           destNode.setProperty("exo:dateModified", new GregorianCalendar());
738         }
739         listenerService.broadcast(CmsService.POST_EDIT_CONTENT_EVENT, destNode.getParent(), destNode);
740       }
741       destNode.save();     
742     } catch (Exception e) {
743       if (LOG.isWarnEnabled()) {
744         LOG.warn("Cannot change property of destNode" + destinationHeader, e);
745       }
746     }
747   }
748 
749   /** 
750    * Build URI from string. 
751    */
752   private URI buildURI(String path) throws URISyntaxException {
753     try {
754       return new URI(path);
755     }
756     catch (URISyntaxException e) {
757       return new URI(TextUtil.escape(path, '%', true));
758     }
759   }
760 
761   /**
762    * {@inheritDoc}
763    */
764   @MKCOL
765   @Path("/{repoName}/{repoPath:.*}/")
766   public Response mkcol(@PathParam("repoName") String repoName,
767                         @PathParam("repoPath") String repoPath,
768                         @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
769                         @HeaderParam(ExtHttpHeaders.IF) String ifHeader,
770                         @HeaderParam(ExtHttpHeaders.CONTENT_NODETYPE) String nodeTypeHeader,
771                         @HeaderParam(ExtHttpHeaders.CONTENT_MIXINTYPES) String mixinTypesHeader,
772                         @Context UriInfo uriInfo) {
773     try {
774       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
775       Item item = nodeFinder.getItem(workspaceName(repoPath), LinkUtils.getParentPath(path(normalizePath(repoPath))), true);
776       repoPath =
777           item.getSession().getWorkspace().getName() + LinkUtils.createPath(item.getPath(), 
778                                                                             LinkUtils.getItemName(path(repoPath)));
779     } catch (PathNotFoundException exc) {
780       return Response.status(HTTPStatus.CONFLICT).entity(exc.getMessage()).build();
781     } catch (NoSuchWorkspaceException exc) {
782       return Response.status(HTTPStatus.CONFLICT).entity(exc.getMessage()).build();
783     } catch (Exception e) {
784       if (LOG.isWarnEnabled()) {
785         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
786       }
787       return Response.serverError().build();
788     }
789     return super.mkcol(repoName,
790                        repoPath,
791                        lockTokenHeader,
792                        ifHeader,
793                        nodeTypeHeader,
794                        mixinTypesHeader,
795                        uriInfo);
796   }
797 
798   @DELETE
799   @Path("/{repoName}/{repoPath:.*}/")
800   public Response delete(@PathParam("repoName") String repoName,
801                          @PathParam("repoPath") String repoPath,
802                          @HeaderParam(ExtHttpHeaders.LOCKTOKEN) String lockTokenHeader,
803                          @HeaderParam(ExtHttpHeaders.IF) String ifHeader) {
804     Item item = null;
805     try {
806       repoName = repositoryService.getCurrentRepository().getConfiguration().getName();
807       repoPath = convertRepoPath(repoPath, false);
808 
809       try {
810         item = nodeFinder.getItem(workspaceName(repoPath),
811                                   path(normalizePath(repoPath)),
812                                   true);        
813       } catch (PathNotFoundException e) {
814         item = nodeFinder.getItem(workspaceName(repoPath),
815                                   path(Text.escapeIllegalJcrChars(repoPath)),
816                                   true);        
817       }     
818 
819     } catch (PathNotFoundException exc) {
820       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
821     } catch (NoSuchWorkspaceException exc) {
822       return Response.status(HTTPStatus.NOT_FOUND).entity(exc.getMessage()).build();
823     } catch (Exception e) {
824       if (LOG.isWarnEnabled()) {
825         LOG.warn("Cannot find the item at " + repoName + "/" + repoPath, e);
826       }
827       return Response.serverError().build();
828     }    
829 
830     try {
831       //Broadcast the event when user move node to Trash
832       Node node = (Node)item;
833       ListenerService listenerService =  WCMCoreUtils.getService(ListenerService.class);
834       ActivityCommonService activityService = WCMCoreUtils.getService(ActivityCommonService.class);
835       Node parent = node.getParent();
836       if (node.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) {        
837         if (activityService.isBroadcastNTFileEvents(node)) {
838           listenerService.broadcast(ActivityCommonService.FILE_REMOVE_ACTIVITY, parent, node);
839         }
840       } else if(!WCMCoreUtils.isDocumentNodeType(node)){
841         Queue<Node> queue = new LinkedList<Node>();
842         queue.add(node);
843 
844         //Broadcast event to remove file activities
845         Node tempNode = null;
846         try {
847           while (!queue.isEmpty()) {
848             tempNode = queue.poll();
849             if (WCMCoreUtils.isDocumentNodeType(tempNode) 
850                 || tempNode.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) {
851               listenerService.broadcast(ActivityCommonService.FILE_REMOVE_ACTIVITY, tempNode.getParent(), tempNode);
852             } else {
853               for (NodeIterator iter = tempNode.getNodes(); iter.hasNext(); ) {
854                 Node childNode = iter.nextNode();
855                 if(WCMCoreUtils.isDocumentNodeType(childNode) || childNode.isNodeType(NodetypeConstant.NT_UNSTRUCTURED) 
856                     || childNode.isNodeType(NodetypeConstant.NT_FOLDER))
857                   queue.add(childNode);
858               }
859             }
860           }
861         } catch (Exception e) {
862           if (LOG.isWarnEnabled()) {
863             LOG.warn(e.getMessage());
864           }
865         }         
866       }
867       //Remove the symlinks of deleted node. 
868       Utils.removeSymlinks(node);
869     } catch(Exception ex) {
870       if (LOG.isWarnEnabled()) {
871         LOG.warn(ex.getMessage());
872       }
873     }    
874     return super.delete(repoName, repoPath, lockTokenHeader, ifHeader);
875   }
876 
877   private String convertRepoPath(String repoPath, boolean giveTarget) throws Exception{
878     try {
879       Item item = nodeFinder.getItem(workspaceName(repoPath), path(normalizePath(repoPath)), giveTarget);
880       return item.getSession().getWorkspace().getName() + item.getPath();
881     } catch (PathNotFoundException e) {
882       Item item = nodeFinder.getItem(workspaceName(repoPath), path(Text.escapeIllegalJcrChars(repoPath)), giveTarget);
883       return item.getSession().getWorkspace().getName() + item.getPath();
884     }
885   }
886 
887   /**
888    * hidden temporary files/folders
889    * @param repoPath
890    */
891   private boolean markTempFilesToHidden(String repoPath){
892     if(StringUtils.isBlank(repoPath)) return false;
893     String tempNodeFolder      = ".TemporaryItems";
894     String tempNodeFileChild = "._folders.501";
895     String tempNodeFile        = "._.TemporaryItems";
896     String txtTempRegex        = "/._";
897     try {
898       String txtTemp = repoPath.substring(repoPath.lastIndexOf("/"), repoPath.length());
899       boolean isTxtTemp = txtTemp.startsWith(txtTempRegex)?true:false;
900       if(repoPath.contains(tempNodeFile) || isTxtTemp){
901         Node _tempNodeFile = (Node)nodeFinder.getItem(workspaceName(repoPath), path(repoPath), true);
902         _tempNodeFile.remove();
903         _tempNodeFile.getSession().save();
904         return false;
905       }else if(repoPath.contains(tempNodeFolder)) {
906         String currentNodePath = repoPath.substring(0, repoPath.indexOf(tempNodeFolder));
907         Node currentNode = (Node)nodeFinder.getItem(workspaceName(repoPath), path(currentNodePath), true);
908         //make tmp folder to hidden
909         if(currentNode.hasNode(tempNodeFolder)){
910           Node _tmpFolderNode = currentNode.getNode(tempNodeFolder);
911           if(_tmpFolderNode.canAddMixin(NodetypeConstant.EXO_HIDDENABLE)) _tmpFolderNode.addMixin(NodetypeConstant.EXO_HIDDENABLE);
912           if (_tmpFolderNode.hasNode(tempNodeFileChild)){
913             Node _tempNodeFileChild = _tmpFolderNode.getNode(tempNodeFileChild);
914             _tempNodeFileChild.remove();
915           }
916           _tmpFolderNode.save();
917         }
918         return false;
919       }
920     }catch(RepositoryException ex){
921       if (LOG.isWarnEnabled()) {
922         LOG.warn("The hidden temp files has been ignored " + ex.getMessage());
923       }
924     }
925     return true;
926   }
927 
928 }