View Javadoc
1   /*
2    * Copyright (C) 2003-2011 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.wcm.ext.component.activity;
18  
19  import java.text.DateFormat;
20  import java.text.ParseException;
21  import java.text.SimpleDateFormat;
22  import java.time.LocalDateTime;
23  import java.time.format.DateTimeFormatter;
24  import java.time.format.FormatStyle;
25  import java.util.ArrayList;
26  import java.util.Date;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.LinkedHashMap;
30  import java.util.List;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.Set;
34  
35  import javax.imageio.ImageIO;
36  import javax.imageio.ImageReader;
37  import javax.imageio.stream.ImageInputStream;
38  import javax.jcr.AccessDeniedException;
39  import javax.jcr.Node;
40  import javax.jcr.PathNotFoundException;
41  import javax.jcr.RepositoryException;
42  import javax.jcr.ValueFormatException;
43  import javax.portlet.PortletRequest;
44  
45  import org.apache.commons.io.FileUtils;
46  import org.apache.commons.lang.StringUtils;
47  
48  import com.ibm.icu.util.Calendar;
49  
50  import org.apache.commons.lang3.StringEscapeUtils;
51  import org.exoplatform.commons.utils.ISO8601;
52  import org.exoplatform.container.ExoContainer;
53  import org.exoplatform.container.ExoContainerContext;
54  import org.exoplatform.container.PortalContainer;
55  import org.exoplatform.container.xml.PortalContainerInfo;
56  import org.exoplatform.download.DownloadService;
57  import org.exoplatform.ecm.webui.utils.Utils;
58  import org.exoplatform.portal.webui.util.Util;
59  import org.exoplatform.services.cms.documents.DocumentService;
60  import org.exoplatform.services.cms.documents.TrashService;
61  import org.exoplatform.services.cms.documents.VersionHistoryUtils;
62  import org.exoplatform.services.cms.drives.DriveData;
63  import org.exoplatform.services.cms.drives.impl.ManageDriveServiceImpl;
64  import org.exoplatform.services.jcr.access.PermissionType;
65  import org.exoplatform.services.jcr.core.ExtendedNode;
66  import org.exoplatform.services.jcr.core.ManageableRepository;
67  import org.exoplatform.services.log.ExoLogger;
68  import org.exoplatform.services.log.Log;
69  import org.exoplatform.services.organization.Group;
70  import org.exoplatform.services.organization.OrganizationService;
71  import org.exoplatform.services.organization.User;
72  import org.exoplatform.services.security.ConversationState;
73  import org.exoplatform.services.security.Identity;
74  import org.exoplatform.services.wcm.core.NodeLocation;
75  import org.exoplatform.services.wcm.core.NodetypeConstant;
76  import org.exoplatform.services.wcm.friendly.FriendlyService;
77  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
78  import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
79  import org.exoplatform.social.core.manager.IdentityManager;
80  import org.exoplatform.social.core.space.SpaceUtils;
81  import org.exoplatform.social.core.space.model.Space;
82  import org.exoplatform.social.core.space.spi.SpaceService;
83  import org.exoplatform.social.core.storage.SpaceStorageException;
84  import org.exoplatform.social.plugin.doc.UIDocActivity;
85  import org.exoplatform.social.webui.activity.BaseUIActivity;
86  import org.exoplatform.social.webui.activity.UIActivitiesContainer;
87  import org.exoplatform.social.webui.composer.PopupContainer;
88  import org.exoplatform.web.CacheUserProfileFilter;
89  import org.exoplatform.webui.application.WebuiRequestContext;
90  import org.exoplatform.webui.application.portlet.PortletRequestContext;
91  import org.exoplatform.webui.config.annotation.ComponentConfig;
92  import org.exoplatform.webui.config.annotation.ComponentConfigs;
93  import org.exoplatform.webui.config.annotation.EventConfig;
94  import org.exoplatform.webui.core.UIPopupContainer;
95  import org.exoplatform.webui.core.lifecycle.UIFormLifecycle;
96  import org.exoplatform.webui.event.Event;
97  import org.exoplatform.webui.event.EventListener;
98  import org.exoplatform.webui.ext.UIExtension;
99  import org.exoplatform.webui.ext.UIExtensionManager;
100 
101 
102 /**
103  * Created by The eXo Platform SAS Author : eXoPlatform exo@exoplatform.com Mar
104  * 15, 2011
105  */
106 @ComponentConfigs({
107         @ComponentConfig(lifecycle = UIFormLifecycle.class,
108                 template = "classpath:groovy/ecm/social-integration/plugin/space/FileUIActivity.gtmpl", events = {
109                 @EventConfig(listeners = FileUIActivity.ViewDocumentActionListener.class),
110                 @EventConfig(listeners = BaseUIActivity.LoadLikesActionListener.class),
111                 @EventConfig(listeners = BaseUIActivity.ToggleDisplayCommentFormActionListener.class),
112                 @EventConfig(listeners = BaseUIActivity.LikeActivityActionListener.class),
113                 @EventConfig(listeners = BaseUIActivity.SetCommentListStatusActionListener.class),
114                 @EventConfig(listeners = BaseUIActivity.PostCommentActionListener.class),
115                 @EventConfig(listeners = BaseUIActivity.DeleteActivityActionListener.class),
116                 @EventConfig(listeners = FileUIActivity.OpenFileActionListener.class),
117                 @EventConfig(listeners = BaseUIActivity.DeleteCommentActionListener.class),
118                 @EventConfig(listeners = BaseUIActivity.LikeCommentActionListener.class),
119                 @EventConfig(listeners = BaseUIActivity.EditActivityActionListener.class),
120                 @EventConfig(listeners = BaseUIActivity.EditCommentActionListener.class)
121         }),
122 })
123 public class FileUIActivity extends BaseUIActivity{
124 
125   public static final String[] EMPTY_ARRAY = new String[0];
126 
127   public static final String SEPARATOR_REGEX      = "\\|@\\|";
128 
129   private static final String NEW_DATE_FORMAT     = "hh:mm:ss MMM d, yyyy";
130   
131   private static final Log    LOG                 = ExoLogger.getLogger(FileUIActivity.class);
132 
133   public static final String  ID                  = "id";
134 
135   public static final String  CONTENT_LINK        = "contenLink";
136 
137   public static final String  MESSAGE             = "message";
138 
139   public static final String  ACTIVITY_STATUS     = "MESSAGE";
140 
141   public static final String  CONTENT_NAME        = "contentName";
142 
143   public static final String  IMAGE_PATH          = "imagePath";
144 
145   public static final String  MIME_TYPE           = "mimeType";
146 
147   public static final String  STATE               = "state";
148 
149   public static final String  AUTHOR              = "author";
150 
151   public static final String  DATE_CREATED        = "dateCreated";
152 
153   public static final String  LAST_MODIFIED       = "lastModified";
154 
155   public static final String  DOCUMENT_TYPE_LABEL = "docTypeLabel";
156 
157   public static final String  DOCUMENT_TITLE      = "docTitle";
158 
159   public static final String  DOCUMENT_VERSION    = "docVersion";
160 
161   public static final String  DOCUMENT_SUMMARY    = "docSummary";
162 
163   public static final String  IS_SYSTEM_COMMENT   = "isSystemComment";
164 
165   public static final String  SYSTEM_COMMENT      = "systemComment";
166 
167   private String              message;
168 
169   private LinkedHashMap<String, String>[] folderPathWithLinks;
170 
171   private String              activityStatus;
172 
173   public int                  filesCount          = 0;
174 
175   private String              activityTitle;
176 
177   private DateTimeFormatter   dateTimeFormatter;
178 
179   private DocumentService     documentService;
180 
181   private SpaceService        spaceService;
182 
183   private OrganizationService organizationService;
184 
185   private TrashService         trashService;
186 
187   List<ActivityFileAttachment> activityFileAttachments = new ArrayList<>();
188 
189   public FileUIActivity() throws Exception {
190     super();
191     if(WebuiRequestContext.getCurrentInstance() != null) {
192       addChild(UIPopupContainer.class, null, "UIDocViewerPopupContainer");
193     }
194   }
195 
196   @Override
197   protected void editActivity(String message) {
198     super.editActivity(message);
199     this.setMessage(message);
200     this.setActivityTitle(message.replace("</br></br>", ""));
201   }
202 
203   public String getActivityTitle() {
204     return activityTitle;
205   }
206 
207   public void setActivityTitle(String activityTitle) {
208     this.activityTitle = activityTitle;
209   }
210 
211   public String getContentLink(int i) {
212     if ((i + 1) > activityFileAttachments.size()) {
213       return null;
214     }
215     return activityFileAttachments.get(i).getContentLink();
216   }
217 
218   public void setContentLink(int i,String contentLink) {
219     if ((i + 1) > activityFileAttachments.size()) {
220       return;
221     }
222     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
223     activityFileAttachment.setContentLink(contentLink);
224   }
225 
226   public String getMessage() {
227     return message;
228   }
229 
230   public void setMessage(String message) {
231     this.message = message;
232   }
233 
234   public String getContentName(int i) {
235     if ((i + 1) > activityFileAttachments.size()) {
236       return null;
237     }
238     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
239     String contentName = null;
240     // Retrieve name from JCR Node instead of activity parameter
241     // To get real file name instead
242     try {
243       contentName = (activityFileAttachment == null
244           || activityFileAttachment.getContentNode() == null) ? null : activityFileAttachment.getContentNode().getName();
245     } catch (RepositoryException e) {
246       LOG.debug("Can't retrieve file name of attachment with path " + activityFileAttachment.getDocPath(), e);
247     }
248     if(StringUtils.isBlank(contentName)) {
249       contentName = activityFileAttachment.getContentName();
250     }
251     return contentName;
252   }
253 
254   public void setContentName(String contentName, int i) {
255     if ((i + 1) > activityFileAttachments.size()) {
256       return;
257     }
258     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
259     activityFileAttachment.setContentName(contentName);
260   }
261 
262   public String getImagePath(int i) {
263     if ((i + 1) > activityFileAttachments.size()) {
264       return null;
265     }
266     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
267     return activityFileAttachment.getImagePath();
268   }
269 
270   public void setImagePath(String imagePath, int i) {
271     if ((i + 1) > activityFileAttachments.size()) {
272       return;
273     }
274     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
275     activityFileAttachment.setImagePath(imagePath);
276   }
277 
278   public String getMimeType(int i) {
279     if ((i + 1) > activityFileAttachments.size()) {
280       return null;
281     }
282     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
283     return activityFileAttachment.getMimeType();
284   }
285 
286   public void setMimeType(String mimeType, int i) {
287     if ((i + 1) > activityFileAttachments.size()) {
288       return;
289     }
290     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
291     activityFileAttachment.setMimeType(mimeType);
292   }
293 
294   public String getNodeUUID(int i) {
295     if ((i + 1) > activityFileAttachments.size()) {
296       return null;
297     }
298     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
299     return activityFileAttachment.getNodeUUID();
300   }
301 
302   public void setNodeUUID(String nodeUUID, int i) {
303     if ((i + 1) > activityFileAttachments.size()) {
304       return;
305     }
306     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
307     activityFileAttachment.setNodeUUID(nodeUUID);
308   }
309 
310   public String getState(int i) {
311     if ((i + 1) > activityFileAttachments.size()) {
312       return null;
313     }
314     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
315     return activityFileAttachment.getState();
316   }
317 
318   public void setState(String state, int i) {
319     if ((i + 1) > activityFileAttachments.size()) {
320       return;
321     }
322     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
323     activityFileAttachment.setState(state);
324   }
325 
326   public String getAuthor(int i) {
327     if ((i + 1) > activityFileAttachments.size()) {
328       return null;
329     }
330     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
331     return activityFileAttachment.getAuthor();
332   }
333 
334   public void setAuthor(String author, int i) {
335     if ((i + 1) > activityFileAttachments.size()) {
336       return;
337     }
338     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
339     activityFileAttachment.setAuthor(author);
340   }
341 
342   public String getDocTypeName(int i) {
343     if ((i + 1) > activityFileAttachments.size()) {
344       return null;
345     }
346     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
347     return activityFileAttachment.getDocTypeName();
348   }
349 
350   public String getDocTitle(int i) {
351     if ((i + 1) > activityFileAttachments.size()) {
352       return null;
353     }
354     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
355     return activityFileAttachment.getDocTitle();
356   }
357 
358   public String getDocVersion(int i) {
359     if ((i + 1) > activityFileAttachments.size()) {
360       return null;
361     }
362     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
363     return activityFileAttachment.getDocVersion();
364   }
365 
366   public String getDocSummary(int i) {
367     if ((i + 1) > activityFileAttachments.size()) {
368       return null;
369     }
370     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
371     return activityFileAttachment.getDocSummary();
372   }
373 
374   public boolean isSymlink(int i) {
375     if ((i + 1) > activityFileAttachments.size()) {
376       return false;
377     }
378     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
379     return activityFileAttachment.isSymlink();
380   }
381 
382   public String getTitle(Node node) throws Exception {
383     return Utils.getTitle(node);
384   }
385   
386   private String convertDateFormat(String strDate, String strOldFormat, String strNewFormat) throws ParseException {
387     if (strDate == null || strDate.length() <= 0) {
388       return "";
389     }
390     Locale locale = Util.getPortalRequestContext().getLocale();
391     SimpleDateFormat sdfSource = new SimpleDateFormat(strOldFormat);
392     SimpleDateFormat sdfDestination = new SimpleDateFormat(strNewFormat, locale);
393     Date date = sdfSource.parse(strDate);
394     return sdfDestination.format(date);
395   }
396 
397   private String convertDateUsingFormat(Calendar date, String format) throws ParseException {
398     Locale locale = Util.getPortalRequestContext().getLocale();
399     DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale);
400     return dateFormat.format(date.getTime());
401   }
402 
403   public String getDateCreated(int i) throws ParseException {
404     if ((i + 1) > activityFileAttachments.size()) {
405       return null;
406     }
407     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
408     return convertDateFormat(activityFileAttachment.getDateCreated(), ISO8601.SIMPLE_DATETIME_FORMAT, NEW_DATE_FORMAT);
409   }  
410 
411   public void setDateCreated(String dateCreated, int i) {
412     if ((i + 1) > activityFileAttachments.size()) {
413       return;
414     }
415     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
416     activityFileAttachment.setDateCreated(dateCreated);
417   }
418 
419   public String getLastModified(int i) throws ParseException {
420     if ((i + 1) > activityFileAttachments.size()) {
421       return null;
422     }
423     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
424     return convertDateFormat(activityFileAttachment.getLastModified(), ISO8601.SIMPLE_DATETIME_FORMAT, NEW_DATE_FORMAT);
425   }
426 
427   public void setLastModified(String lastModified, int i) {
428     if ((i + 1) > activityFileAttachments.size()) {
429       return;
430     }
431     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
432     activityFileAttachment.setLastModified(lastModified);
433   }
434 
435   public Node getContentNode(int i) {
436     if ((i + 1) > activityFileAttachments.size()) {
437       return null;
438     }
439     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
440     Node tmpContentNode = activityFileAttachment.getContentNode();
441     try {
442       if (activityFileAttachment.getNodeLocation() != null && (tmpContentNode == null || !tmpContentNode.getSession().isLive())) {
443         tmpContentNode = NodeLocation.getNodeByLocation(activityFileAttachment.getNodeLocation());
444       }
445     } catch (RepositoryException e) {
446       if (activityFileAttachment.getNodeLocation() != null) {
447         tmpContentNode = NodeLocation.getNodeByLocation(activityFileAttachment.getNodeLocation());
448       }
449     }
450     activityFileAttachment.setContentNode(tmpContentNode);
451     return tmpContentNode;
452   }
453 
454   public void setContentNode(Node contentNode, int i) {
455     if ((i + 1) > activityFileAttachments.size()) {
456       return;
457     }
458     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
459     activityFileAttachment.setContentNode(contentNode);
460     activityFileAttachment.setNodeLocation(NodeLocation.getNodeLocationByNode(contentNode));
461   }
462 
463   public NodeLocation getNodeLocation(int i) {
464     if ((i + 1) > activityFileAttachments.size()) {
465       return null;
466     }
467     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
468     return activityFileAttachment.getNodeLocation();
469   }
470 
471   public void setNodeLocation(NodeLocation nodeLocation, int i) {
472     if ((i + 1) > activityFileAttachments.size()) {
473       return;
474     }
475     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
476     activityFileAttachment.setNodeLocation(nodeLocation);
477   }
478 
479   /**
480    * Gets the summary.
481    * @param node the node
482    * @return the summary of Node. Return empty string if catch an exception.
483    */
484   public String getSummary(Node node) {
485     return org.exoplatform.wcm.ext.component.activity.listener.Utils.getSummary(node);
486   }
487   
488   public String getDocumentSummary(Map<String, String> activityParams) {
489     return activityParams.get(FileUIActivity.DOCUMENT_SUMMARY);
490   }
491 
492   public String getUserFullName(String userId) {
493     if(StringUtils.isEmpty(userId)) {
494       return "";
495     }
496 
497     // if the requested user is the connected user, get the fullname from the ConversationState
498     ConversationState currentUserState = ConversationState.getCurrent();
499     Identity currentUserIdentity = currentUserState.getIdentity();
500     if(currentUserIdentity != null) {
501       String currentUser = currentUserIdentity.getUserId();
502       if (currentUser != null && currentUser.equals(userId)) {
503         User user = (User) currentUserState.getAttribute(CacheUserProfileFilter.USER_PROFILE);
504         if(user != null) {
505           return user.getDisplayName();
506         }
507       }
508     }
509 
510     // if the requested user if not the connected user, fetch it from the organization service
511     try {
512       User user = getOrganizationService().getUserHandler().findUserByName(userId);
513       if(user != null) {
514         return user.getDisplayName();
515       }
516     } catch (Exception e) {
517       LOG.error("Cannot get information of user " + userId + " : " + e.getMessage(), e);
518     }
519 
520     return "";
521   }
522   
523   protected String getSize(Node node) {
524     double size = 0;    
525     try {
526       if (node.hasNode(Utils.JCR_CONTENT)) {
527         Node contentNode = node.getNode(Utils.JCR_CONTENT);
528         if (contentNode.hasProperty(Utils.JCR_DATA)) {
529           size = contentNode.getProperty(Utils.JCR_DATA).getLength();
530         }
531         
532         return FileUtils.byteCountToDisplaySize((long)size);
533       }
534     } catch (PathNotFoundException e) {
535       return StringUtils.EMPTY;
536     } catch (ValueFormatException e) {
537       return StringUtils.EMPTY;
538     } catch (RepositoryException e) {
539       return StringUtils.EMPTY;
540     } catch(NullPointerException e) {
541     	return StringUtils.EMPTY;
542     }
543     return StringUtils.EMPTY;    
544   }
545   
546   protected double getFileSize(Node node) {
547     double fileSize = 0;    
548     try {
549       if(node.isNodeType(NodetypeConstant.EXO_SYMLINK)) {
550         node = Utils.getNodeSymLink(node);
551       }
552       if (node.hasNode(Utils.JCR_CONTENT)) {
553         Node contentNode = node.getNode(Utils.JCR_CONTENT);
554         if (contentNode.hasProperty(Utils.JCR_DATA)) {
555         	fileSize = contentNode.getProperty(Utils.JCR_DATA).getLength();
556         }
557       }
558     } catch(Exception ex) { fileSize = 0; }
559     return fileSize;    
560   }
561   
562   protected int getImageWidth(Node node, int i) {
563     if ((i + 1) > activityFileAttachments.size()) {
564       return 0;
565     }
566     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
567 
568   	int imageWidth = 0;
569   	try {
570       if(node.isNodeType(NodetypeConstant.EXO_SYMLINK)) {
571         node = Utils.getNodeSymLink(node);
572       }
573   		if(node.hasNode(NodetypeConstant.JCR_CONTENT)) node = node.getNode(NodetypeConstant.JCR_CONTENT);
574     	ImageReader reader = ImageIO.getImageReadersByMIMEType(activityFileAttachment.getMimeType()).next();
575     	ImageInputStream iis = ImageIO.createImageInputStream(node.getProperty("jcr:data").getStream());
576     	reader.setInput(iis, true);
577     	imageWidth = reader.getWidth(0);
578     	iis.close();
579     	reader.dispose();   	
580     } catch (Exception e) {
581         if(LOG.isTraceEnabled()) {
582           String nodePath = null;
583           try {
584             nodePath = node.getPath();
585           } catch(Exception exp) {
586             // Nothing to log
587           }
588           LOG.trace("Cannot get image from node " + nodePath, e);
589         }
590     }
591   	return imageWidth;
592   }
593   
594   protected int getImageHeight(Node node, int i) {
595     if ((i + 1) > activityFileAttachments.size()) {
596       return 0;
597     }
598     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
599 
600   	int imageHeight = 0;
601   	try {
602       if(node.isNodeType(NodetypeConstant.EXO_SYMLINK)) {
603         node = Utils.getNodeSymLink(node);
604       }
605   		if(node.hasNode(NodetypeConstant.JCR_CONTENT)) node = node.getNode(NodetypeConstant.JCR_CONTENT);
606     	ImageReader reader = ImageIO.getImageReadersByMIMEType(activityFileAttachment.getMimeType()).next();
607     	ImageInputStream iis = ImageIO.createImageInputStream(node.getProperty("jcr:data").getStream());
608     	reader.setInput(iis, true);
609     	imageHeight = reader.getHeight(0);
610     	iis.close();
611     	reader.dispose();   	
612     } catch (Exception e) {
613         LOG.info("Cannot get node");
614     }
615   	return imageHeight;
616   }
617 
618   protected String getDocUpdateDate(Node node) {
619     String docUpdatedDate = "";
620     try {
621       if(node != null && node.hasProperty("exo:lastModifiedDate")) {
622         String rawDocUpdatedDate = node.getProperty("exo:lastModifiedDate").getString();
623         LocalDateTime parsedDate = LocalDateTime.parse(rawDocUpdatedDate, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
624         docUpdatedDate = parsedDate.format(getDateTimeFormatter());
625       }
626     } catch (RepositoryException e) {
627       LOG.error("Cannot get document updated date : " + e.getMessage(), e);
628     }
629     return docUpdatedDate;
630   }
631 
632   /**
633    * Get a localized DateTimeFormatter
634    * @return A localized DateTimeFormatter
635    */
636   protected DateTimeFormatter getDateTimeFormatter() {
637     if(dateTimeFormatter == null) {
638       dateTimeFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
639       Locale locale = WebuiRequestContext.getCurrentInstance().getLocale();
640       if (locale != null) {
641         dateTimeFormatter = dateTimeFormatter.withLocale(locale);
642       }
643     }
644     return dateTimeFormatter;
645   }
646 
647   protected String getDocLastModifier(Node node) {
648     String docLastModifier = "";
649     try {
650       if (node.isNodeType("exo:symlink")){
651         String uuid = node.getProperty("exo:uuid").getString();
652         node = node.getSession().getNodeByUUID(uuid);
653       }
654       if(node != null && node.hasProperty("exo:lastModifier")) {
655         String docLastModifierUsername = node.getProperty("exo:lastModifier").getString();
656         docLastModifier = getUserFullName(docLastModifierUsername);
657       }
658     } catch (RepositoryException e) {
659       LOG.error("Cannot get document last modifier : " + e.getMessage(), e);
660     }
661     return docLastModifier;
662   }
663 
664   protected int getVersion(Node node) {
665     String currentVersion = null;
666     try {
667       if (node.isNodeType(VersionHistoryUtils.MIX_DISPLAY_VERSION_NAME) &&
668               node.hasProperty(VersionHistoryUtils.MAX_VERSION_PROPERTY)) {
669         //Get max version ID
670         int max = (int) node.getProperty(VersionHistoryUtils.MAX_VERSION_PROPERTY).getLong();
671         return max - 1;
672       }
673       currentVersion = node.getBaseVersion().getName();
674       if (currentVersion.contains("jcr:rootVersion")) currentVersion = "0";
675     }catch (Exception e) {
676       currentVersion ="0";
677     }
678     return Integer.parseInt(currentVersion);
679   }
680 
681   public String getUserProfileUri(String userId) {
682     ExoContainer container = ExoContainerContext.getCurrentContainer();
683     IdentityManager identityManager = (IdentityManager) container.getComponentInstanceOfType(IdentityManager.class);
684 
685     return identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, userId, true).getProfile().getUrl();
686   }
687 
688   public String getUserAvatarImageSource(String userId) {
689     return getOwnerIdentity().getProfile().getAvatarUrl();
690   }
691 
692   public String getSpaceAvatarImageSource(String spaceIdentityId) {
693     try {
694       String spaceId = getOwnerIdentity().getRemoteId();
695       SpaceService spaceService = getSpaceService();
696       Space space = spaceService.getSpaceById(spaceId);
697       if (space != null) {
698         return space.getAvatarUrl();
699       }
700     } catch (SpaceStorageException e) {
701       LOG.warn("Failed to getSpaceById: " + spaceIdentityId, e);
702     }
703     return null;
704   }
705 
706   private SpaceService getSpaceService() {
707     if (spaceService == null) {
708       spaceService = getApplicationComponent(SpaceService.class);
709     }
710     return spaceService;
711   }
712 
713   public String getActivityStatus() {
714     if (message == null) {
715       return activityStatus;
716     } else {
717       return message;
718     }
719   }
720 
721   public int getFilesCount() {
722     return filesCount;
723   }
724 
725   public void setUIActivityData(Map<String, String> activityParams) {
726     activityFileAttachments.clear();
727 
728     this.message =  activityParams.get(FileUIActivity.MESSAGE);
729     this.activityStatus =  activityParams.get(FileUIActivity.ACTIVITY_STATUS);
730 
731     String[] nodeUUIDs = getParameterValues(activityParams, FileUIActivity.ID);
732     this.filesCount = nodeUUIDs == null ? 0 : nodeUUIDs.length;
733 
734     String[] repositories = getParameterValues(activityParams,UIDocActivity.REPOSITORY);
735     String[] workspaces = getParameterValues(activityParams,UIDocActivity.WORKSPACE);
736     String[] contentLink = getParameterValues(activityParams,FileUIActivity.CONTENT_LINK);
737     String[] state = getParameterValues(activityParams, FileUIActivity.STATE);
738     String[] author = getParameterValues(activityParams, FileUIActivity.AUTHOR);
739     String[] dateCreated =  getParameterValues(activityParams, FileUIActivity.DATE_CREATED);
740     String[] lastModified =  getParameterValues(activityParams, FileUIActivity.LAST_MODIFIED);
741     String[] contentName =  getParameterValues(activityParams, FileUIActivity.CONTENT_NAME);
742     String[] mimeType =  getParameterValues(activityParams, FileUIActivity.MIME_TYPE);
743     String[] imagePath =  getParameterValues(activityParams, FileUIActivity.IMAGE_PATH);
744     String[] docTypeName =  getParameterValues(activityParams, FileUIActivity.DOCUMENT_TYPE_LABEL);
745     String[] docTitle =  getParameterValues(activityParams, FileUIActivity.DOCUMENT_TITLE);  
746     String[] docVersion =  getParameterValues(activityParams, FileUIActivity.DOCUMENT_VERSION);
747     String[] docSummary =  getParameterValues(activityParams, FileUIActivity.DOCUMENT_SUMMARY);
748     Boolean[] isSymlink = null;
749     String[] isSymlinkParams = getParameterValues(activityParams, UIDocActivity.IS_SYMLINK);
750     if(isSymlinkParams != null) {
751       isSymlink = new Boolean[isSymlinkParams.length];
752       for (int i = 0; i < isSymlinkParams.length; i++) {
753         isSymlink[i] = Boolean.parseBoolean(isSymlinkParams[i]);
754       }
755     }
756 
757     for (int i = 0; i < this.filesCount; i++) {
758       ActivityFileAttachment fileAttachment = new ActivityFileAttachment();
759       String repositoryName = (String) getValueFromArray(i, repositories);
760       String workspaceName = (String) getValueFromArray(i, workspaces);
761 
762       if(StringUtils.isBlank(repositoryName)) {
763         ManageableRepository repository = WCMCoreUtils.getRepository();
764         repositoryName = repository == null ? null : repository.getConfiguration().getName();
765       }
766 
767       if(StringUtils.isBlank(workspaceName)) {
768         ManageableRepository repository = WCMCoreUtils.getRepository();
769         workspaceName =  repository == null ? null : repository.getConfiguration().getDefaultWorkspaceName();
770       }
771 
772       fileAttachment.setNodeUUID(nodeUUIDs[i])
773                     .setRepository(repositoryName)
774                     .setWorkspace(workspaceName)
775                     .setContentLink((String) getValueFromArray(i, contentLink))
776                     .setContentName(getValueFromArray(i, contentName))
777                     .setState((String) getValueFromArray(i, state))
778                     .setAuthor(getValueFromArray(i, author))
779                     .setDateCreated(getValueFromArray(i, dateCreated))
780                     .setLastModified(getValueFromArray(i, lastModified))
781                     .setMimeType(getValueFromArray(i, mimeType))
782                     .setImagePath(getValueFromArray(i, imagePath))
783                     .setDocTypeName(getValueFromArray(i, docTypeName))
784                     .setDocTitle(getValueFromArray(i, docTitle))
785                     .setDocVersion(getValueFromArray(i, docVersion))
786                     .setDocSummary(getValueFromArray(i, docSummary))
787                     .setSymlink(getValueFromArray(i, isSymlink));
788 
789       Node contentNode = NodeLocation.getNodeByLocation(fileAttachment.getNodeLocation());
790       if (contentNode != null) {
791         try {
792           if (!getTrashService().isInTrash(contentNode)) {
793             activityFileAttachments.add(fileAttachment);
794           }
795         } catch (RepositoryException e) {
796           LOG.error("Error while testing if the content is in trash", e);
797         }
798       }
799     }
800     this.filesCount = this.activityFileAttachments.size();
801   }
802 
803   private <T> T getValueFromArray(int index, T... valuesArray) {
804     return (valuesArray == null || index > (valuesArray.length - 1)) ? null : valuesArray[index];
805   }
806 
807   private String[] getParameterValues(Map<String, String> activityParams, String paramName) {
808     String[] values = null;
809     String value = activityParams.get(paramName);
810     if(value == null) {
811       value = activityParams.get(paramName.toLowerCase());
812     }
813     if(value != null) {
814       values = value.split(SEPARATOR_REGEX);
815     }
816     if (LOG.isDebugEnabled()) {
817       if(this.filesCount != 0 && (values == null || values.length != this.filesCount)) {
818           LOG.debug("Parameter '{}' hasn't same length as other activity parmameters", paramName);
819       }
820     }
821     return values;
822   }
823 
824   /**
825    * Gets the webdav url.
826    * 
827    * @return the webdav url
828    * @throws Exception the exception
829    */
830   public String getWebdavURL(int i) throws Exception {
831     if ((i + 1) > activityFileAttachments.size()) {
832       return null;
833     }
834     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
835     if (activityFileAttachment.getWebdavURL() != null) {
836       return activityFileAttachment.getWebdavURL();
837     }
838 
839     PortletRequestContext portletRequestContext = WebuiRequestContext.getCurrentInstance();
840     PortletRequest portletRequest = portletRequestContext.getRequest();
841     String repository = activityFileAttachment.getRepository();
842     String workspace = activityFileAttachment.getWorkspace();
843     String baseURI = portletRequest.getScheme() + "://" + portletRequest.getServerName() + ":"
844         + String.format("%s", portletRequest.getServerPort());
845 
846     FriendlyService friendlyService = WCMCoreUtils.getService(FriendlyService.class);
847     String link = "#";
848 
849     String portalName = PortalContainer.getCurrentPortalContainerName();
850     String restContextName = PortalContainer.getCurrentRestContextName();
851     Node tmpContentNode = this.getContentNode(i);
852     if (tmpContentNode.isNodeType("nt:frozenNode")) {
853       String uuid = tmpContentNode.getProperty("jcr:frozenUuid").getString();
854       Node originalNode = tmpContentNode.getSession().getNodeByUUID(uuid);
855       link = baseURI + "/" + portalName + "/" + restContextName + "/jcr/" + repository + "/"
856           + workspace + originalNode.getPath() + "?version=" + this.getContentNode(i).getParent().getName();
857     } else {
858       link = baseURI + "/" + portalName + "/" + restContextName + "/jcr/" + repository + "/"
859           + workspace + tmpContentNode.getPath();
860     }
861 
862     activityFileAttachment.setWebdavURL(friendlyService.getFriendlyUri(link));
863     return activityFileAttachment.getWebdavURL();
864   }
865 
866   public String[] getSystemCommentBundle(Map<String, String> activityParams) {
867     return org.exoplatform.wcm.ext.component.activity.listener.Utils.getSystemCommentBundle(activityParams);
868   }
869 
870   public String[] getSystemCommentTitle(Map<String, String> activityParams) {
871     return org.exoplatform.wcm.ext.component.activity.listener.Utils.getSystemCommentTitle(activityParams);
872   }
873 
874   public DriveData getDocDrive(int i) {
875     if ((i + 1) > activityFileAttachments.size()) {
876       return null;
877     }
878     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
879     if (activityFileAttachment.getDocDrive() != null) {
880       return activityFileAttachment.getDocDrive();
881     }
882 
883     NodeLocation nodeLocation = activityFileAttachment.getNodeLocation();
884     if (nodeLocation != null) {
885       try {
886         String userId = ConversationState.getCurrent().getIdentity().getUserId();
887         activityFileAttachment.setDocDrive(documentService.getDriveOfNode(nodeLocation.getPath(), "placeholder_user_name", Utils.getMemberships()));
888       } catch (Exception e) {
889         LOG.error("Cannot get drive of node " + nodeLocation.getPath() + " : " + e.getMessage(), e);
890       }
891     }
892     return activityFileAttachment.getDocDrive();
893   }
894 
895   public String getDefaultIconClass(int i) {
896     String iconClass = "uiBgdFile";
897     String contentName = getContentName(i);
898     if (StringUtils.isNotBlank(contentName)) {
899       if (contentName.toLowerCase().contains(".pdf")) {
900         iconClass = "uiBgdFilePDF";
901       } else if (contentName.toLowerCase().contains(".doc")) {
902           iconClass = "uiBgdFileWord";
903       } else if (contentName.toLowerCase().contains(".xls")) {
904           iconClass = "uiBgdFileExcel";
905       } else if (contentName.toLowerCase().contains(".ppt")) {
906           iconClass = "uiBgdFilePPT";
907       }
908     }
909     return iconClass;
910   }
911 
912   public String getDocFileBreadCrumb(int i) {
913     LinkedHashMap<String, String> docFolderBreadCrumb = getDocFolderRelativePathWithLinks(i);
914     String breadCrumbContent = "";
915     if (docFolderBreadCrumb != null) {
916       int breadCrumbSize = docFolderBreadCrumb.size();
917       int folderIndex = 0;
918       for (String folderName : docFolderBreadCrumb.keySet()) {
919         String folderPath = docFolderBreadCrumb.get(folderName);
920         folderName = folderName.replaceAll("_" + (breadCrumbSize - folderIndex - 1) + "$", "");
921         if (folderIndex < (breadCrumbSize - 1)) {
922           if (folderIndex > 0) {
923             breadCrumbContent += ",";
924           }
925           breadCrumbContent += "'" + folderName.replace("'", "\\'") + "': '" + folderPath + "'";
926           breadCrumbContent = breadCrumbContent.replace("%27", "\\'");
927         }
928         folderIndex++;
929       }
930     }
931     return breadCrumbContent;
932   }
933   
934   public String getDocFilePath(int i) {
935     LinkedHashMap<String, String> folderRelativePathWithLinks = getDocFolderRelativePathWithLinks(i);
936     if(folderRelativePathWithLinks != null && !folderRelativePathWithLinks.isEmpty()) {
937       String[] nodeNames = folderRelativePathWithLinks.values().toArray(EMPTY_ARRAY);
938       return nodeNames[nodeNames.length - 1];
939     }
940     return null;
941   }
942 
943   public LinkedHashMap<String, String> getDocFolderRelativePathWithLinks(int i) {
944     if ((i + 1) > activityFileAttachments.size()) {
945       return null;
946     }
947     if(folderPathWithLinks == null) {
948       folderPathWithLinks = new LinkedHashMap[filesCount];
949     }
950     if(folderPathWithLinks[i] == null) {
951       folderPathWithLinks[i] = new LinkedHashMap<>();
952       LinkedHashMap<String, String> reversedFolderPathWithLinks = new LinkedHashMap<>();
953 
954       DriveData drive = getDocDrive(i);
955       if (drive != null) {
956         try {
957           Map<String, String> parameters = drive.getParameters();
958           String driveName = drive.getName();
959           if (parameters != null) {
960             if (parameters.containsKey("groupId")) {
961               String groupId = parameters.get("groupId");
962               if (StringUtils.isNotBlank(groupId)) {
963                 try {
964                   groupId = groupId.replaceAll("\\.", "/");
965                   if (groupId.startsWith(SpaceUtils.SPACE_GROUP)) {
966                     SpaceService spaceService = getSpaceService();
967                     Space space = spaceService.getSpaceByGroupId(groupId);
968                     if (space != null) {
969                       driveName = space.getDisplayName();
970                     }
971                   } else {
972                     Group group = getOrganizationService().getGroupHandler().findGroupById(groupId);
973                     driveName = group == null ? driveName : group.getLabel();
974                   }
975                 } catch (Exception e) {
976                   LOG.warn("Can't get drive name for group with id '" + groupId + "'", e);
977                 }
978               }
979             } else if (parameters.containsKey("userId")) {
980               String userId = parameters.get("userId");
981               if (StringUtils.isNotBlank(userId)) {
982                 try {
983                   userId = userId.indexOf("/") >= 0 ? userId.substring(userId.lastIndexOf("/") + 1) : userId;
984                   User user = getOrganizationService().getUserHandler().findUserByName(userId);
985                   if (user != null) {
986                     driveName = user.getDisplayName();
987                   }
988                 } catch (Exception e) {
989                   LOG.warn("Can't get drive name for user with id '" + userId + "'", e);
990                 }
991               }
992             }
993           }
994           String driveHomePath = drive.getResolvedHomePath();
995 
996           // if the drive is the Personal Documents drive, we must handle the special case of the Public symlink
997           String drivePublicFolderHomePath = null;
998           if (ManageDriveServiceImpl.PERSONAL_DRIVE_NAME.equals(drive.getName())) {
999             drivePublicFolderHomePath = driveHomePath.replace("/" + ManageDriveServiceImpl.PERSONAL_DRIVE_PRIVATE_FOLDER_NAME, "/" + ManageDriveServiceImpl.PERSONAL_DRIVE_PUBLIC_FOLDER_NAME);
1000           }
1001 
1002           // calculate the relative path to the drive by browsing up the content node path
1003           Node parentContentNode = getContentNode(i);
1004           while (parentContentNode != null) {
1005             String parentPath = parentContentNode.getPath();
1006             // exit condition is check here instead of in the while condition to avoid
1007             // retrieving the path several times and because there is some logic to handle
1008             if (!parentPath.contains(driveHomePath)) {
1009               // The parent path is outside drive
1010               break;
1011             } else if (!driveHomePath.equals("/") && parentPath.equals("/")) {
1012               // we are at the root of the workspace
1013               break;
1014             } else if (drivePublicFolderHomePath != null && parentPath.equals(drivePublicFolderHomePath)) {
1015               // this is a special case : the root of the Public folder of the Personal Documents drive
1016               // in this case we add the Public folder in the path
1017               reversedFolderPathWithLinks.put(ManageDriveServiceImpl.PERSONAL_DRIVE_PUBLIC_FOLDER_NAME, getDocOpenUri(parentPath, i));
1018               break;
1019             }
1020 
1021             String nodeName;
1022             // title is used if it exists, otherwise the name is used
1023             if (parentPath.equals(driveHomePath)) {
1024               nodeName = driveName;
1025             } else if (parentContentNode.hasProperty("exo:title")) {
1026               nodeName = parentContentNode.getProperty("exo:title").getString();
1027             } else {
1028               nodeName = parentContentNode.getName();
1029             }
1030             reversedFolderPathWithLinks.put(nodeName + "_" + reversedFolderPathWithLinks.size(), getDocOpenUri(parentPath, i));
1031 
1032             if (parentPath.equals("/")) {
1033               break;
1034             } else {
1035               parentContentNode = parentContentNode.getParent();
1036             }
1037           }
1038         } catch (AccessDeniedException e) {
1039           LOG.debug(e.getMessage());
1040         } catch (RepositoryException re) {
1041           ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
1042           LOG.error("Cannot retrieve path of doc " + activityFileAttachment.getDocPath() + " : " + re.getMessage(), re);
1043         }
1044       }
1045 
1046       if(reversedFolderPathWithLinks.size() > 1) {
1047         List<Map.Entry<String, String>> entries = new ArrayList<>(reversedFolderPathWithLinks.entrySet());
1048         for(int j = entries.size()-1; j >= 0; j--) {
1049           Map.Entry<String, String> entry = entries.get(j);
1050           folderPathWithLinks[i].put(StringEscapeUtils.escapeHtml4(entry.getKey()), entry.getValue());
1051         }
1052       } else {
1053         folderPathWithLinks[i] = reversedFolderPathWithLinks;
1054       }
1055     }
1056 
1057     return folderPathWithLinks[i];
1058   }
1059 
1060   private OrganizationService getOrganizationService() {
1061     if (organizationService == null) {
1062       organizationService = getApplicationComponent(OrganizationService.class);
1063     }
1064     return organizationService;
1065   }
1066 
1067   private DocumentService getDocumentService() {
1068     if (documentService == null) {
1069       documentService = getApplicationComponent(DocumentService.class);
1070     }
1071     return documentService;
1072   }
1073   
1074   private TrashService getTrashService() {
1075     if (trashService == null) {
1076       trashService = getApplicationComponent(TrashService.class);
1077     }
1078     return trashService;
1079   }
1080 
1081   public String getDocFolderRelativePath(int i) {
1082     StringBuilder folderRelativePath = new StringBuilder();
1083 
1084     Set<String> relativePaths = getDocFolderRelativePathWithLinks(i).keySet();
1085     int pathSize = relativePaths.size();
1086     Iterator<String> relativePathIterator = relativePaths.iterator();
1087     int folderIndex = 0;
1088     while (relativePathIterator.hasNext()) {
1089       String folderName = relativePathIterator.next();
1090 
1091       // Delete file from parent Path
1092       if(relativePathIterator.hasNext()) {
1093         folderName = folderName.replaceAll("_" + (pathSize - folderIndex -1) + "$", "");
1094         folderRelativePath.append(folderName).append("/");
1095       }
1096       folderIndex++;
1097     }
1098 
1099     if(folderRelativePath.length() > 1) {
1100       // remove the last /
1101       folderRelativePath.deleteCharAt(folderRelativePath.length() - 1);
1102     }
1103 
1104     return folderRelativePath.toString();
1105   }
1106 
1107   public String getCurrentDocOpenUri(int i) {
1108     if ((i + 1) > activityFileAttachments.size()) {
1109       return null;
1110     }
1111     ActivityFileAttachment activityFileAttachment = activityFileAttachments.get(i);
1112 
1113     String uri = "";
1114     if(activityFileAttachment.getNodeLocation() != null) {
1115       uri = getDocOpenUri(activityFileAttachment.getDocPath(), i);
1116     }
1117 
1118     return uri;
1119   }
1120 
1121   public String getDocOpenUri(String nodePath, int i) {
1122     String uri = "";
1123 
1124     if(nodePath != null) {
1125       try {
1126         if (nodePath.endsWith("/")) {
1127           nodePath = nodePath.replaceAll("/$", "");
1128         }
1129         uri = getDocumentService().getLinkInDocumentsApp(nodePath, getDocDrive(i));
1130       } catch(Exception e) {
1131         LOG.error("Cannot get document open URI of node " + nodePath + " : " + e.getMessage(), e);
1132         uri = "";
1133       }
1134     }
1135 
1136     return uri;
1137   }
1138 
1139   public String getEditLink(int i) {
1140     try {
1141       return org.exoplatform.wcm.webui.Utils.getEditLink(getContentNode(i), true, false);
1142     }catch (Exception e) {
1143       return "";
1144     }
1145   }
1146   
1147   public String getActivityEditLink(int i) {
1148   	try {
1149       return org.exoplatform.wcm.webui.Utils.getActivityEditLink(getContentNode(i));
1150     }catch (Exception e) {
1151       return "";
1152     }
1153   }
1154 
1155   protected String getCssClassIconFile(String fileName, String fileType, int i) {
1156     try {
1157       return org.exoplatform.ecm.webui.utils.Utils.getNodeTypeIcon(this.getContentNode(i), "uiBgd64x64");
1158     } catch (RepositoryException e) {
1159       return "uiBgd64x64FileDefault";
1160     }
1161   }
1162 
1163   protected String getContainerName() {
1164     //get portal name
1165     ExoContainer container = ExoContainerContext.getCurrentContainer();
1166     PortalContainerInfo containerInfo = (PortalContainerInfo) container.getComponentInstanceOfType(PortalContainerInfo.class);
1167     return containerInfo.getContainerName();  
1168   }
1169 
1170   public String getDownloadAllLink() {
1171     try {
1172       if (activityFileAttachments.isEmpty()) {
1173         return null;
1174       }
1175       if(activityFileAttachments.size() == 1) {
1176         return getDownloadLink(0);
1177       }
1178 
1179       // Get binary data from node
1180       DownloadService dservice = WCMCoreUtils.getService(DownloadService.class);
1181 
1182       NodeLocation[] nodeLocations = new NodeLocation[activityFileAttachments.size()];
1183 
1184       for (int i = 0; i < activityFileAttachments.size(); i++) {
1185         nodeLocations[i] = activityFileAttachments.get(i).getNodeLocation();
1186       }
1187 
1188       // Make download stream
1189       ActivityFilesDownloadResource dresource = new ActivityFilesDownloadResource(nodeLocations);
1190       String fileName = "activity_" + getActivity().getId() + "_";
1191       Long postedTime = getActivity().getPostedTime();
1192       if(postedTime != null) {
1193         Calendar postedDate = Calendar.getInstance();
1194         fileName += convertDateUsingFormat(postedDate, ISO8601.COMPLETE_DATE_FORMAT).replaceAll("/", "-");
1195       }
1196       dresource.setDownloadName(fileName + ".zip");
1197       return dservice.getDownloadLink(dservice.addDownloadResource(dresource)) ;
1198     }catch (Exception e) {
1199       return "";
1200     }
1201   }
1202 
1203   public String getDownloadLink(int i) {
1204     try {
1205       Node contentNode = getContentNode(i);
1206       if(contentNode.isNodeType(NodetypeConstant.EXO_SYMLINK)) {
1207         contentNode = Utils.getNodeSymLink(contentNode);
1208       }
1209       return org.exoplatform.wcm.webui.Utils.getDownloadLink(contentNode);
1210     }catch (Exception e) {
1211       return "";
1212     }
1213   }
1214 
1215   /**
1216    * <h2>Check if file node is supported by preview on activity stream
1217    * A preview from the activity stream is available for the following contents:
1218    * </h2>
1219    * <ul>
1220    * <li>pdf and office file</li>
1221    * <li>media (audio, video, image)</li>
1222    * </ul>
1223    * @param data Content node
1224    * @return true: support; false: not support
1225    * @throws Exception
1226    */
1227   public boolean isFileSupportPreview(Node data) throws Exception {
1228     if (data != null && data.isNodeType(Utils.NT_FILE)) {
1229       UIExtensionManager manager = getApplicationComponent(UIExtensionManager.class);
1230       List<UIExtension> extensions = manager.getUIExtensions(Utils.FILE_VIEWER_EXTENSION_TYPE);
1231 
1232       Map<String, Object> context = new HashMap<String, Object>();
1233       context.put(Utils.MIME_TYPE, data.getNode(Utils.JCR_CONTENT).getProperty(Utils.JCR_MIMETYPE).getString());
1234 
1235       for (UIExtension extension : extensions) {
1236         if (manager.accept(Utils.FILE_VIEWER_EXTENSION_TYPE, extension.getName(), context) && !"Text".equals(extension.getName())) {
1237           return true;
1238         }
1239       }
1240     }
1241 
1242     return false;
1243   }
1244 
1245   public static class ViewDocumentActionListener extends EventListener<FileUIActivity> {
1246     @Override
1247     public void execute(Event<FileUIActivity> event) throws Exception {
1248       FileUIActivity fileUIActivity = event.getSource();
1249       String index = event.getRequestContext().getRequestParameter(OBJECTID);
1250       int i = Integer.parseInt(index);
1251       UIActivitiesContainer uiActivitiesContainer = fileUIActivity.getAncestorOfType(UIActivitiesContainer.class);
1252       PopupContainer uiPopupContainer = uiActivitiesContainer.getPopupContainer();
1253 
1254       UIDocumentPreview uiDocumentPreview = uiPopupContainer.createUIComponent(UIDocumentPreview.class, null,
1255               "UIDocumentPreview");
1256       uiDocumentPreview.setBaseUIActivity(fileUIActivity);
1257       if ((i + 1) > fileUIActivity.activityFileAttachments.size()) {
1258         return;
1259       }
1260       ActivityFileAttachment activityFileAttachment = fileUIActivity.activityFileAttachments.get(i);
1261       uiDocumentPreview.setContentInfo(activityFileAttachment.getDocPath(), activityFileAttachment.getRepository(), activityFileAttachment.getWorkspace(),
1262               fileUIActivity.getContentNode(i));
1263 
1264       uiPopupContainer.activate(uiDocumentPreview, 0, 0, true);
1265       event.getRequestContext().addUIComponentToUpdateByAjax(uiPopupContainer);
1266     }
1267   }
1268   
1269   public static class DownloadDocumentActionListener extends EventListener<FileUIActivity> {
1270     @Override
1271     public void execute(Event<FileUIActivity> event) throws Exception {
1272     	FileUIActivity uiComp = event.getSource();
1273     	String index = event.getRequestContext().getRequestParameter(OBJECTID);
1274     	int i = Integer.parseInt(index);
1275       String downloadLink = null;
1276       if (getRealNode(uiComp.getContentNode(i)).getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) {
1277         downloadLink = Utils.getDownloadRestServiceLink(uiComp.getContentNode(i));
1278       }
1279       event.getRequestContext().getJavascriptManager().addJavascript("ajaxRedirect('" + downloadLink + "');");
1280     }
1281     
1282     private Node getRealNode(Node node) throws Exception {
1283       // TODO: Need to add to check symlink node
1284       if (node.isNodeType("nt:frozenNode")) {
1285         String uuid = node.getProperty("jcr:frozenUuid").getString();
1286         return node.getSession().getNodeByUUID(uuid);
1287       }
1288       return node;
1289     }
1290   }
1291 
1292   public static class OpenFileActionListener extends EventListener<FileUIActivity> {
1293     public void execute(Event<FileUIActivity> event) throws Exception {
1294       FileUIActivity fileUIActivity = event.getSource();
1295       String index = event.getRequestContext().getRequestParameter(OBJECTID);
1296       int i = 0;
1297       if (!StringUtils.isBlank(index)) {
1298         i = Integer.parseInt(index);
1299       }
1300 
1301       Node currentNode = fileUIActivity.getContentNode(i);
1302 
1303       FileUIActivity docActivity = event.getSource();
1304       UIActivitiesContainer activitiesContainer = docActivity.getAncestorOfType(UIActivitiesContainer.class);
1305       PopupContainer popupContainer = activitiesContainer.getPopupContainer();
1306 
1307       org.exoplatform.ecm.webui.utils.Utils.openDocumentInDesktop(currentNode, popupContainer, event);
1308     }
1309   }
1310 
1311   /**
1312    * <h2>Check file node can edit on activity stream</h2>
1313    * The file only can edit when user have modify permission on parent folder
1314    * @param data File node
1315    * @return true: can edit; false: cannot edit
1316    */
1317   public boolean canEditDocument(Node data){
1318     try {
1319       ((ExtendedNode)data.getParent()).checkPermission(PermissionType.ADD_NODE);
1320       return true;
1321     } catch(Exception e) {
1322       return false;
1323     }
1324   }
1325 
1326 }