View Javadoc
1   package org.exoplatform.wcm.connector.collaboration;
2   
3   import org.apache.commons.lang.StringUtils;
4   import org.apache.tika.io.IOUtils;
5   import org.exoplatform.ecm.utils.text.Text;
6   import org.exoplatform.services.cms.documents.DocumentTypeService;
7   import org.exoplatform.services.cms.documents.impl.DocumentType;
8   import org.exoplatform.services.cms.link.LinkManager;
9   import org.exoplatform.services.cms.link.NodeFinder;
10  import org.exoplatform.services.log.ExoLogger;
11  import org.exoplatform.services.log.Log;
12  import org.exoplatform.services.resources.ResourceBundleService;
13  import org.exoplatform.services.rest.resource.ResourceContainer;
14  import org.exoplatform.services.wcm.core.NodetypeConstant;
15  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
16  import org.json.JSONObject;
17  import org.picocontainer.Startable;
18  
19  import javax.annotation.security.RolesAllowed;
20  import javax.jcr.Node;
21  import javax.jcr.PathNotFoundException;
22  import javax.jcr.RepositoryException;
23  import javax.jcr.Session;
24  import javax.servlet.http.HttpServletRequest;
25  import javax.ws.rs.*;
26  import javax.ws.rs.core.*;
27  import java.net.URLDecoder;
28  import java.util.Locale;
29  import java.util.MissingResourceException;
30  import java.util.ResourceBundle;
31  
32  
33  /**
34   * Created by The eXo Platform SAS
35   * Author : eXoPlatform
36   *          toannh@exoplatform.com
37   * Dec 09, 2014
38   * Provider all rest methods of Open Document feature.
39   */
40  
41  @Path("/office/")
42  @RolesAllowed("users")
43  public class OpenInOfficeConnector implements ResourceContainer, Startable {
44  
45    private Log log = ExoLogger.getExoLogger(OpenInOfficeConnector.class);
46    private final String OPEN_DOCUMENT_ON_DESKTOP_ICO              = "uiIconOpenOnDesktop";
47    private final String CONNECTOR_BUNDLE_LOCATION                 = "locale.wcm.resources.WCMResourceBundleConnector";
48    private final String OPEN_DOCUMENT_ON_DESKTOP_RESOURCE_KEY = "OpenInOfficeConnector.label.exo.remote-edit.desktop";
49    private final String OPEN_DOCUMENT_IN_DESKTOP_APP_RESOURCE_KEY = "OpenInOfficeConnector.label.exo.remote-edit.desktop-app";
50    private final String OPEN_DOCUMENT_DEFAULT_TITLE               = "Open on Desktop";
51    private final int CACHED_TIME = 60*24*30*12;
52  
53    private static final String VERSION_MIXIN ="mix:versionable";
54  
55    private NodeFinder nodeFinder;
56    private LinkManager linkManager;
57    private ResourceBundleService resourceBundleService;
58    private DocumentTypeService documentTypeService;
59  
60    public OpenInOfficeConnector(NodeFinder nodeFinder,
61                                 LinkManager linkManager,
62                                 ResourceBundleService resourceBundleService,
63                                 DocumentTypeService documentTypeService){
64      this.nodeFinder = nodeFinder;
65      this.linkManager = linkManager;
66      this.resourceBundleService = resourceBundleService;
67      this.documentTypeService = documentTypeService;
68    }
69    /**
70     * Return a JsonObject's current file to update display titles
71     * @param request
72     * @param objId
73     * @return
74     * @throws Exception
75     */
76    @GET
77    @Path("/updateDocumentTitle")
78    public Response updateDocumentTitle(
79            @Context Request request,
80            @QueryParam("objId") String objId,
81            @QueryParam("lang") String language) throws Exception {
82  
83      //find from cached
84      int indexColon = objId.indexOf(":/");
85      if(indexColon < 0) {
86        return Response.status(Response.Status.BAD_REQUEST)
87                .entity("The objId param must start by the workspace name, followed by ':' and the node path").build();
88      }
89      String workspace = objId.substring(0, indexColon);
90      String filePath = objId.substring(indexColon + 1);
91  
92      String extension = filePath.substring(filePath.lastIndexOf(".") + 1, filePath.length());
93      if(extension.contains("[")) extension=extension.substring(0, extension.indexOf("["));
94      EntityTag etag = new EntityTag(Integer.toString((extension+"_"+language).hashCode()));
95      Response.ResponseBuilder builder = request.evaluatePreconditions(etag);
96      if(builder!=null) return builder.build();
97  
98      //query form configuration values params
99  
100     CacheControl cc = new CacheControl();
101     cc.setMaxAge(CACHED_TIME);
102 
103     ResourceBundle resourceBundle = resourceBundleService.getResourceBundle(CONNECTOR_BUNDLE_LOCATION, new Locale(language));
104     String title = resourceBundle!=null?resourceBundle.getString(OPEN_DOCUMENT_ON_DESKTOP_RESOURCE_KEY):OPEN_DOCUMENT_DEFAULT_TITLE;
105     String ico = OPEN_DOCUMENT_ON_DESKTOP_ICO;
106 
107     DocumentType documentType = documentTypeService.getDocumentType(extension);
108 
109     if(documentType !=null && resourceBundle !=null ){
110       try {
111         if(!StringUtils.isEmpty(resourceBundle.getString(documentType.getResourceBundleKey())))
112           title = resourceBundle.getString(documentType.getResourceBundleKey());
113       }catch(MissingResourceException ex){
114         String _openonDesktop = resourceBundle.getString(OPEN_DOCUMENT_IN_DESKTOP_APP_RESOURCE_KEY);
115         if(_openonDesktop!=null && _openonDesktop.contains("{0}")) {
116           title = _openonDesktop.replace("{0}", documentType.getResourceBundleKey());
117         }else{
118           title = OPEN_DOCUMENT_DEFAULT_TITLE;
119         }
120       }
121       if(!StringUtils.isEmpty(documentType.getIconClass())) ico=documentType.getIconClass();
122     }
123     Node node;
124     String nodePath = filePath;
125     boolean isFile=false;
126     try{
127       try {
128         node = (Node)nodeFinder.getItem(workspace, filePath);
129       } catch (PathNotFoundException e) {
130         node = (Node)nodeFinder.getItem(workspace, Text.unescapeIllegalJcrChars(filePath));
131       }
132       if (linkManager.isLink(node)) node = linkManager.getTarget(node);
133       nodePath = node.getPath();
134       isFile = node.isNodeType(NodetypeConstant.NT_FILE);
135     }catch(RepositoryException ex){
136       if(log.isErrorEnabled()){log.error("Exception when get node with path: "+filePath, ex);}
137     }
138 
139     boolean isMsoffice = false;
140     if (documentType.getResourceBundleKey() != OPEN_DOCUMENT_ON_DESKTOP_RESOURCE_KEY) {
141       isMsoffice = true;
142     }
143     JSONObject rs = new JSONObject();
144     rs.put("ico", ico);
145     rs.put("title", title);
146     rs.put("repository", WCMCoreUtils.getRepository().getConfiguration().getName());
147     rs.put("workspace", workspace);
148     rs.put("filePath", nodePath);
149     rs.put("isFile", isFile);
150     rs.put("isMsoffice", isMsoffice);
151 
152     builder = Response.ok(rs.toString(), MediaType.APPLICATION_JSON);
153     builder.tag(etag);
154     builder.cacheControl(cc);
155     return builder.build();
156   }
157 
158   /**
159    * Get Title, css class of document by document's name
160    * @param fileName
161    * @return
162    */
163   public String[] getDocumentInfos(String fileName){
164     String title = OPEN_DOCUMENT_ON_DESKTOP_RESOURCE_KEY;
165     String icon = OPEN_DOCUMENT_ON_DESKTOP_ICO;
166 
167     String _extension = "";
168     if(fileName.lastIndexOf(".") > 0 ) {
169       _extension = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1, fileName.length());
170     }
171     if(StringUtils.isBlank(_extension)) return new String[]{title, icon};
172 
173     DocumentType documentType = documentTypeService.getDocumentType(_extension);
174     if(documentType !=null){
175       if(!StringUtils.isEmpty(documentType.getResourceBundleKey())) title=documentType.getResourceBundleKey();
176       if(!StringUtils.isEmpty(documentType.getIconClass())) icon=documentType.getIconClass();
177     }
178     return new String[]{title, icon};
179   }
180 
181   /**
182    * Return a JsonObject's check a version when file has been opened successfully by desktop application
183    * @param request
184    * @param filePath
185    * @return
186    * @throws Exception
187    */
188   @GET
189   @Path("/checkout")
190   public Response checkout(@Context Request request,
191                            @QueryParam("filePath") String filePath,
192                            @QueryParam("workspace") String workspace
193   ) throws Exception {
194     Session session = WCMCoreUtils.getSystemSessionProvider().
195         getSession(workspace, WCMCoreUtils.getRepository());
196     Node node = (Node)session.getItem(filePath);
197 
198     if(node.canAddMixin(VERSION_MIXIN)){
199       node.addMixin(VERSION_MIXIN);
200       node.save();
201       node.checkin();
202       node.checkout();
203     }
204 
205     if(!node.isCheckedOut()) node.checkout();
206 
207     return Response.ok(String.valueOf(node.isCheckedOut()), MediaType.TEXT_PLAIN).build();
208   }
209 
210   /**
211    * Return a a input stream internet shortcut to open file with desktop application
212    * @param httpServletRequest
213    * @param filePath
214    * @return
215    * @throws Exception
216    */
217   @GET
218   @Path("/{linkFilePath}/")
219   @Produces("application/internet-shortcut")
220   public Response createShortcut(@Context HttpServletRequest httpServletRequest,
221                            @PathParam("linkFilePath") String linkFilePath,
222                            @QueryParam("filePath") String filePath,
223                            @QueryParam("workspace") String workspace
224   ) throws Exception {
225     Session session = WCMCoreUtils.getSystemSessionProvider().getSession(workspace, WCMCoreUtils.getRepository());
226     Node node = (Node)session.getItem(filePath);
227     String repo = WCMCoreUtils.getRepository().getConfiguration().getName();
228 
229     String obsPath = httpServletRequest.getScheme()+ "://" + httpServletRequest.getServerName() + ":"
230             +httpServletRequest.getServerPort() + "/"
231             + WCMCoreUtils.getRestContextName()+ "/private/jcr/" + repo + "/" + workspace + node.getPath();
232 
233     String shortCutContent = "[InternetShortcut]\n";
234     shortCutContent+="URL="+obsPath+"\n";
235     return Response.ok(IOUtils.toInputStream(shortCutContent), MediaType.APPLICATION_OCTET_STREAM)
236             .header("Content-Disposition","attachment; filename="+node.getName()+".url")
237             .header("Content-type", "application/internet-shortcut")
238             .build();
239   }
240 
241   @Override
242   public void start() {
243   }
244 
245   @Override
246   public void stop() { }
247 }