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.listener;
18  
19  import org.apache.commons.lang.StringUtils;
20  import org.exoplatform.commons.utils.ActivityTypeUtils;
21  import org.exoplatform.commons.utils.CommonsUtils;
22  import org.exoplatform.commons.utils.ISO8601;
23  import org.exoplatform.container.ExoContainer;
24  import org.exoplatform.container.ExoContainerContext;
25  import org.exoplatform.container.PortalContainer;
26  import org.exoplatform.container.xml.PortalContainerInfo;
27  import org.exoplatform.services.cms.BasePath;
28  import org.exoplatform.services.cms.documents.DocumentService;
29  import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService;
30  import org.exoplatform.services.cms.link.LinkManager;
31  import org.exoplatform.services.cms.templates.TemplateService;
32  import org.exoplatform.services.context.DocumentContext;
33  import org.exoplatform.services.jcr.core.ExtendedNode;
34  import org.exoplatform.services.jcr.core.ManageableRepository;
35  import org.exoplatform.services.jcr.ext.common.SessionProvider;
36  import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;
37  import org.exoplatform.services.jcr.impl.core.NodeImpl;
38  import org.exoplatform.services.log.ExoLogger;
39  import org.exoplatform.services.log.Log;
40  import org.exoplatform.services.resources.ResourceBundleService;
41  import org.exoplatform.services.security.ConversationState;
42  import org.exoplatform.services.wcm.core.NodeLocation;
43  import org.exoplatform.services.wcm.core.NodetypeConstant;
44  import org.exoplatform.services.wcm.core.WebSchemaConfigService;
45  import org.exoplatform.services.wcm.friendly.FriendlyService;
46  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
47  import org.exoplatform.services.wcm.webcontent.WebContentSchemaHandler;
48  import org.exoplatform.social.core.activity.model.ExoSocialActivity;
49  import org.exoplatform.social.core.activity.model.ExoSocialActivityImpl;
50  import org.exoplatform.social.core.identity.model.Identity;
51  import org.exoplatform.social.core.identity.model.Profile;
52  import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
53  import org.exoplatform.social.core.identity.provider.SpaceIdentityProvider;
54  import org.exoplatform.social.core.manager.ActivityManager;
55  import org.exoplatform.social.core.manager.IdentityManager;
56  import org.exoplatform.social.core.service.LinkProvider;
57  import org.exoplatform.social.core.space.spi.SpaceService;
58  import org.exoplatform.social.core.space.model.Space;
59  import org.exoplatform.social.core.space.SpaceUtils;
60  import org.exoplatform.wcm.ext.component.activity.ContentUIActivity;
61  import org.exoplatform.wcm.ext.component.activity.FileUIActivity;
62  import org.exoplatform.webui.application.WebuiRequestContext;
63  import org.exoplatform.webui.cssfile.CssClassIconFile;
64  import org.exoplatform.webui.cssfile.CssClassManager;
65  import org.exoplatform.webui.cssfile.CssClassUtils;
66  
67  import javax.jcr.Node;
68  import javax.jcr.PathNotFoundException;
69  import javax.jcr.RepositoryException;
70  import java.io.InputStream;
71  import java.net.URLEncoder;
72  import java.text.DateFormat;
73  import java.text.SimpleDateFormat;
74  import java.util.Calendar;
75  import java.util.HashMap;
76  import java.util.List;
77  import java.util.Locale;
78  import java.util.Map;
79  import java.util.MissingResourceException;
80  import java.util.ResourceBundle;
81  
82  
83  
84  /**
85   * Created by The eXo Platform SAS Author : eXoPlatform exo@exoplatform.com Mar
86   * 18, 2011
87   */
88  public class Utils {
89  
90    private static final Log    LOG                 = ExoLogger.getLogger(Utils.class);
91  
92    /** The Constant Activity Type */
93    public static final String CONTENT_SPACES        = "contents:spaces";
94    public static final String FILE_SPACES           = "files:spaces";
95    public  static final String SHARE_FILE           = "sharefiles:spaces";
96    public  static final String SHARE_CONTENT        = "sharecontents:spaces";
97  
98    /** the publication:currentState property name */
99    private static final String CURRENT_STATE_PROP  = "publication:currentState";
100 
101   public static final String EXO_RESOURCES_URI            = "/eXoSkin/skin/images/themes/default/Icons/TypeIcons/EmailNotificationIcons/";
102   public static final String ICON_FILE_EXTENSION          = ".png";
103   public static final String DEFAULT_AVATAR          = "/eXoSkin/skin/images/themes/default/social/skin/ShareImages/UserAvtDefault.png";
104 
105   private static String MIX_COMMENT                = "exo:activityComment";
106   private static String MIX_COMMENT_ID             = "exo:activityCommentID";
107   private static int    MAX_SUMMARY_LINES_COUNT    = 4;
108   private static int    MAX_SUMMARY_CHAR_COUNT     = 430;
109   private static String activityType;
110 
111 
112   public static String getActivityType() {
113     return activityType;
114   }
115 
116   public static void setActivityType(String activityType) {
117     Utils.activityType = activityType;
118   }
119 
120   /**
121    * Populate activity data with the data from Node
122    * 
123    * @param node the node
124    * @param activityOwnerId the owner id of the activity
125    * @param activityMsgBundleKey the message bundle key of the activity
126    * @return Map the mapped data
127    */
128   public static Map<String, String> populateActivityData(Node node,
129           String activityOwnerId,
130           String activityMsgBundleKey) throws Exception {
131 	  return populateActivityData(node, activityOwnerId, activityMsgBundleKey, false, null, null);
132   }
133   public static Map<String, String> populateActivityData(Node node,
134                                                          String activityOwnerId, String activityMsgBundleKey, 
135                                                          boolean isSystemComment, String systemComment, String perm) throws Exception {
136     /** The date formatter. */
137     DateFormat dateFormatter = null;
138     dateFormatter = new SimpleDateFormat(ISO8601.SIMPLE_DATETIME_FORMAT);
139     LinkManager linkManager = WCMCoreUtils.getService(LinkManager.class);
140 
141     if(node.canAddMixin(NodetypeConstant.MIX_REFERENCEABLE)){
142       node.addMixin(NodetypeConstant.MIX_REFERENCEABLE);
143       node.save();
144     }
145     // get activity data
146     String repository = ((ManageableRepository) node.getSession().getRepository()).getConfiguration()
147                                                                                   .getName();
148     String workspace = node.getSession().getWorkspace().getName();
149     
150     String illustrationImg;
151     try{
152       illustrationImg = Utils.getIllustrativeImage(node);
153     }catch(Exception ex){
154       illustrationImg="";
155     }
156     String strDateCreated = "";
157     if (node.hasProperty(NodetypeConstant.EXO_DATE_CREATED)) {
158       Calendar dateCreated = node.getProperty(NodetypeConstant.EXO_DATE_CREATED).getDate();
159       strDateCreated = dateFormatter.format(dateCreated.getTime());
160     }
161     String strLastModified = "";
162     if (node.hasNode(NodetypeConstant.JCR_CONTENT)) {
163       Node contentNode = node.getNode(NodetypeConstant.JCR_CONTENT);
164       if (contentNode.hasProperty(NodetypeConstant.JCR_LAST_MODIFIED)) {
165         Calendar lastModified = contentNode.getProperty(NodetypeConstant.JCR_LAST_MODIFIED)
166                                            .getDate();
167         strLastModified = dateFormatter.format(lastModified.getTime());
168       }
169     }
170 
171     activityOwnerId = activityOwnerId != null ? activityOwnerId : "";
172 
173     // populate data to map object
174     Map<String, String> activityParams = new HashMap<String, String>();
175     activityParams.put(ContentUIActivity.NODE_UUID, node.getUUID());
176     activityParams.put(ContentUIActivity.CONTENT_NAME, node.getName());
177     activityParams.put(ContentUIActivity.AUTHOR, activityOwnerId);
178     activityParams.put(ContentUIActivity.DATE_CREATED, strDateCreated);
179     activityParams.put(ContentUIActivity.LAST_MODIFIED, strLastModified);
180     activityParams.put(ContentUIActivity.CONTENT_LINK, getContentLink(node));
181     activityParams.put(ContentUIActivity.ID,
182                        node.isNodeType(NodetypeConstant.MIX_REFERENCEABLE) ? node.getUUID() : "");
183     activityParams.put(ContentUIActivity.REPOSITORY, repository);
184     activityParams.put(ContentUIActivity.WORKSPACE, workspace);
185     activityParams.put(ContentUIActivity.MESSAGE, activityMsgBundleKey);
186     activityParams.put(ContentUIActivity.MIME_TYPE, getMimeType(linkManager.isLink(node)?linkManager.getTarget(node, true):node));
187     activityParams.put(ContentUIActivity.IMAGE_PATH, illustrationImg);
188     activityParams.put(ContentUIActivity.IMAGE_PATH, illustrationImg);
189     if (isSystemComment) {
190       activityParams.put(ContentUIActivity.IS_SYSTEM_COMMENT, String.valueOf(isSystemComment));
191     	activityParams.put(ContentUIActivity.SYSTEM_COMMENT, systemComment);
192     }else{
193       activityParams.put(ContentUIActivity.IS_SYSTEM_COMMENT, String.valueOf(false));
194       activityParams.put(ContentUIActivity.SYSTEM_COMMENT, "");
195     }
196     activityParams.put(ContentUIActivity.PERMISSION, perm);
197     activityParams.put(ContentUIActivity.COMMENT, systemComment);
198     activityParams.put(ContentUIActivity.THUMBNAIL, getThumbnailUrl(node, repository, workspace) != null ? getThumbnailUrl(node, repository, workspace) : getDefaultThumbnailUrl(node));
199     activityParams.put(ContentUIActivity.NODE_PATH, node.getPath());
200     return activityParams;
201   }
202 
203   private static String getDefaultThumbnailUrl(Node node) throws RepositoryException {
204     LinkManager linkManager = WCMCoreUtils.getService(LinkManager.class);
205     String cssClass = CssClassUtils.getCSSClassByFileNameAndFileType(
206         node.getName(), getMimeType(linkManager.isLink(node)?linkManager.getTarget(node, true):node), CssClassManager.ICON_SIZE.ICON_64);
207 
208     if (cssClass.indexOf(CssClassIconFile.DEFAULT_CSS) > 0) {
209       return CommonsUtils.getCurrentDomain() + EXO_RESOURCES_URI  + "uiIcon64x64Templatent_file.png";
210     }
211     return CommonsUtils.getCurrentDomain() + EXO_RESOURCES_URI + cssClass.split(" ")[0] + ICON_FILE_EXTENSION;
212   }
213 
214   private static String getThumbnailUrl(Node node, String repository, String workspace) {
215     try {
216       LinkManager linkManager = WCMCoreUtils.getService(LinkManager.class);
217       String mimeType = getMimeType(linkManager.isLink(node)?linkManager.getTarget(node, true):node);
218       ExoContainer container = ExoContainerContext.getCurrentContainer();
219       PortalContainerInfo containerInfo = (PortalContainerInfo) container.getComponentInstanceOfType(PortalContainerInfo.class);
220       String portalName = containerInfo.getContainerName();
221 
222       String restContextName = org.exoplatform.ecm.webui.utils.Utils.getRestContextName(portalName);
223       String preferenceWS = node.getSession().getWorkspace().getName();
224       String encodedPath = URLEncoder.encode(node.getPath(), "utf-8");
225       encodedPath = encodedPath.replaceAll ("%2F", "/");
226 
227       if (mimeType.startsWith("image")) {
228 
229         return CommonsUtils.getCurrentDomain() + "/" + portalName + "/" + restContextName + "/thumbnailImage/custom/300x300/" +
230             repository + "/" + preferenceWS + encodedPath;
231       }
232       else if (mimeType.indexOf("icon") >=0) {
233         return getWebdavURL(node, repository, workspace);
234       }
235       else if (org.exoplatform.services.cms.impl.Utils.isSupportThumbnailView(mimeType)) {
236         return CommonsUtils.getCurrentDomain() + "/" + portalName + "/" + restContextName + "/thumbnailImage/big/" + repository + "/" + preferenceWS + encodedPath;
237       } else {
238         return null;
239       }
240 
241     }
242     catch (Exception e) {
243       LOG.debug("Cannot get thumbnail url");
244     }
245     return StringUtils.EMPTY;
246   }
247 
248   private static String getWebdavURL(Node contentNode, String repository, String workspace) throws Exception {
249     FriendlyService friendlyService = WCMCoreUtils.getService(FriendlyService.class);
250     String link = "#";
251 
252     String portalName = PortalContainer.getCurrentPortalContainerName();
253     String restContextName = PortalContainer.getCurrentRestContextName();
254     if (contentNode.isNodeType("nt:frozenNode")) {
255       String uuid = contentNode.getProperty("jcr:frozenUuid").getString();
256       Node originalNode = contentNode.getSession().getNodeByUUID(uuid);
257       link = CommonsUtils.getCurrentDomain() + "/" + portalName + "/" + restContextName + "/jcr/" + repository + "/"
258           + workspace + originalNode.getPath() + "?version=" + contentNode.getParent().getName();
259     } else {
260       link = CommonsUtils.getCurrentDomain() + "/" + portalName + "/" + restContextName + "/jcr/" + repository + "/"
261           + workspace + contentNode.getPath();
262     }
263 
264     return friendlyService.getFriendlyUri(link);
265   }
266 
267   /**
268    * see the postActivity(Node node, String activityMsgBundleKey, Boolean isSystemComment, String systemComment, String perm)
269    */
270   public static void postActivity(Node node, String activityMsgBundleKey) throws Exception {
271     postActivity(node, activityMsgBundleKey, false, false, null, null);
272   }
273 
274   public static ExoSocialActivity createShareActivity(Node node, String activityMsgBundleKey, String activityType, String comments, String perm) throws Exception{
275     setActivityType(activityType);
276     if(SHARE_FILE.equals(activityType)){
277       return postFileActivity(node,activityMsgBundleKey,false,false,comments, perm);
278     }else if(SHARE_CONTENT.equals(activityType)){
279       return postActivity(node,activityMsgBundleKey,false,false,comments, perm);
280     }else{
281       setActivityType(null);
282       return postFileActivity(node,activityMsgBundleKey,false,false,comments, perm);
283     }
284   }
285   /**
286    * see the postFileActivity(Node node, String activityMsgBundleKey, Boolean isSystemComment, String systemComment, String perm)
287    */
288   public static void postFileActivity(Node node, String activityMsgBundleKey) throws Exception {
289     postFileActivity(node, activityMsgBundleKey, false, false, null, null);
290   }
291 
292   /**
293    * 
294    * @param node : activity raised from this source
295    * @param activityMsgBundleKey
296    * @param needUpdate
297    * @param isSystemComment
298    * @param systemComment the new value of System Posted activity,
299    *        if (isSystemComment) systemComment can not be set to null, set to empty string instead of.
300    * @param perm the permission accorded for sharing file/content
301    * @throws Exception
302    */
303   public static ExoSocialActivity postActivity(Node node, String activityMsgBundleKey, boolean needUpdate, 
304                                   boolean isSystemComment, String systemComment, String perm) throws Exception {
305     Object isSkipRaiseAct = DocumentContext.getCurrent()
306                                            .getAttributes()
307                                            .get(DocumentContext.IS_SKIP_RAISE_ACT);
308     if (isSkipRaiseAct != null && Boolean.valueOf(isSkipRaiseAct.toString())) {
309       return null;
310     }
311 //    if (!isSupportedContent(node)) {
312 //      return null;
313 //    }
314 
315     // get services
316     ExoContainer container = ExoContainerContext.getCurrentContainer();
317     ActivityManager activityManager = (ActivityManager) container.getComponentInstanceOfType(ActivityManager.class);
318     IdentityManager identityManager = (IdentityManager) container.getComponentInstanceOfType(IdentityManager.class);
319     ActivityCommonService activityCommonService =
320             (ActivityCommonService)container.getComponentInstanceOfType(ActivityCommonService.class);
321 
322     SpaceService spaceService = WCMCoreUtils.getService(SpaceService.class);
323 
324     // refine to get the valid node
325     refineNode(node);
326 
327     // get owner
328     String activityOwnerId = getActivityOwnerId(node);
329     String nodeActivityID = StringUtils.EMPTY;
330     ExoSocialActivity exa =null;
331     if (node.isNodeType(ActivityTypeUtils.EXO_ACTIVITY_INFO)) {
332       try {
333         nodeActivityID = node.getProperty(ActivityTypeUtils.EXO_ACTIVITY_ID).getString();
334         exa =  activityManager.getActivity(nodeActivityID);
335       }catch (Exception e){
336           LOG.info("No activity is deleted, return no related activity");
337       }
338     }
339     ExoSocialActivity activity = null ;
340     String commentID;
341     boolean commentFlag = false;
342     if (node.isNodeType(MIX_COMMENT) && node.hasProperty(MIX_COMMENT_ID) && activityCommonService.isEditing(node))
343     {
344       commentID = node.getProperty(MIX_COMMENT_ID).getString();
345       if (StringUtils.isNotBlank(commentID)) activity = activityManager.getActivity(commentID);
346       commentFlag = (activity != null);
347     }
348     if (activity==null) {
349       String _activityType = StringUtils.isNotEmpty(activityType)?activityType:CONTENT_SPACES;
350       activity = createActivity(identityManager, activityOwnerId,
351                                 node, activityMsgBundleKey, _activityType, isSystemComment, systemComment, perm);
352       setActivityType(null);
353     }
354     
355     if (exa!=null) {
356       if (commentFlag) {
357         Map<String, String> paramsMap = activity.getTemplateParams();
358         String paramMessage = paramsMap.get(ContentUIActivity.MESSAGE);
359         String paramContent = paramsMap.get(ContentUIActivity.SYSTEM_COMMENT);
360         if (!StringUtils.isEmpty(paramMessage)) {
361           paramMessage += ActivityCommonService.VALUE_SEPERATOR + activityMsgBundleKey;
362           if (StringUtils.isEmpty(systemComment)) {
363             paramContent += ActivityCommonService.VALUE_SEPERATOR + " ";
364           }else {
365             paramContent += ActivityCommonService.VALUE_SEPERATOR + systemComment;
366           }
367         } else {
368           paramMessage = activityMsgBundleKey;
369           paramContent = systemComment;
370         }
371         paramsMap.put(ContentUIActivity.MESSAGE, paramMessage);
372         paramsMap.put(ContentUIActivity.SYSTEM_COMMENT, paramContent);
373         activity.setTemplateParams(paramsMap);
374         updateNotifyMessages(activity, activityMsgBundleKey, systemComment);
375         activityManager.updateActivity(activity);
376       } else {
377         updateNotifyMessages(activity, activity.getTemplateParams().get(ContentUIActivity.MESSAGE), activity.getTemplateParams().get(ContentUIActivity.SYSTEM_COMMENT));
378         activityManager.saveComment(exa, activity);
379         if (activityCommonService.isEditing(node)) {
380           commentID = activity.getId();
381           if (node.canAddMixin(MIX_COMMENT)) node.addMixin(MIX_COMMENT);
382           if (node.isNodeType(MIX_COMMENT)) node.setProperty(MIX_COMMENT_ID, commentID);
383         }
384       }
385       if (needUpdate) {
386         updateMainActivity(activityManager, node, exa);
387       }
388       return activity;
389     }else {
390       String spaceGroupName = getSpaceName(node);
391       Space space = spaceService.getSpaceByGroupId(SpaceUtils.SPACE_GROUP + "/" + spaceGroupName);
392       if (spaceGroupName != null && spaceGroupName.length() > 0
393           && space != null) {
394         // post activity to space stream
395         Identity spaceIdentity = identityManager.getOrCreateIdentity(SpaceIdentityProvider.NAME,
396             space.getPrettyName(),
397             true);
398         activityManager.saveActivityNoReturn(spaceIdentity, activity);
399       } else if (activityOwnerId != null && activityOwnerId.length() > 0) {
400         // post activity to user status stream
401         Identity ownerIdentity = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME,
402             activityOwnerId,
403             true);
404         activityManager.saveActivityNoReturn(ownerIdentity, activity);
405       } else {
406         return null;
407       }
408       String activityId = activity.getId();
409       if (!StringUtils.isEmpty(activityId)) {
410         ActivityTypeUtils.attachActivityId(node, activityId);
411       }
412       updateMainActivity(activityManager, node, activity);
413 
414       if (node.isNodeType(ActivityTypeUtils.EXO_ACTIVITY_INFO)) {
415         try {
416           nodeActivityID = node.getProperty(ActivityTypeUtils.EXO_ACTIVITY_ID).getString();
417           exa = activityManager.getActivity(nodeActivityID);
418         } catch (Exception e) {
419           LOG.info("No activity is deleted, return no related activity");
420         }
421         if (exa != null && !commentFlag && isSystemComment) {
422           activityManager.saveComment(exa, activity);
423           if (activityCommonService.isEditing(node)) {
424             commentID = activity.getId();
425             if (node.canAddMixin(MIX_COMMENT)) node.addMixin(MIX_COMMENT);
426             if (node.isNodeType(MIX_COMMENT)) node.setProperty(MIX_COMMENT_ID, commentID);
427           }
428         }
429       }
430 
431       return activity;
432     }
433   }
434   
435   /**
436    * 
437    * @param node : activity raised from this source
438    * @param activityMsgBundleKey
439    * @param isSystemComment
440    * @param systemComment the new value of System Posted activity, 
441    *        if (isSystemComment) systemComment can not be set to null, set to empty string instead of.
442    * @throws Exception
443    */
444   public static ExoSocialActivity postFileActivity(Node node, String activityMsgBundleKey, boolean needUpdate, 
445                                   boolean isSystemComment, String systemComment, String perm) throws Exception {
446     Object isSkipRaiseAct = DocumentContext.getCurrent()
447                                            .getAttributes()
448                                            .get(DocumentContext.IS_SKIP_RAISE_ACT);
449     if (isSkipRaiseAct != null && Boolean.valueOf(isSkipRaiseAct.toString())) {
450       return null;
451     }
452 //    if (!isSupportedContent(node)) {
453 //      return null;
454 //    }
455 
456     // get services
457     ExoContainer container = ExoContainerContext.getCurrentContainer();
458     ActivityManager activityManager = (ActivityManager) container.getComponentInstanceOfType(ActivityManager.class);
459     IdentityManager identityManager = (IdentityManager) container.getComponentInstanceOfType(IdentityManager.class);
460     ActivityCommonService activityCommonService =
461             (ActivityCommonService)container.getComponentInstanceOfType(ActivityCommonService.class);
462 
463     SpaceService spaceService = WCMCoreUtils.getService(SpaceService.class);
464 
465     // refine to get the valid node
466     refineNode(node);
467 
468     // get owner
469     String activityOwnerId = getActivityOwnerId(node);
470     String nodeActivityID = StringUtils.EMPTY;
471     ExoSocialActivity exa =null;    
472     if (node.isNodeType(ActivityTypeUtils.EXO_ACTIVITY_INFO)) {
473       try {
474         nodeActivityID = node.getProperty(ActivityTypeUtils.EXO_ACTIVITY_ID).getString();
475         exa =  activityManager.getActivity(nodeActivityID);
476       }catch (Exception e){
477           LOG.info("No activity is deleted, return no related activity");
478       }
479     }
480     ExoSocialActivity activity = null ;
481     String commentID;
482     boolean commentFlag = false;
483     if (node.isNodeType(MIX_COMMENT) && activityCommonService.isEditing(node)) {
484       if (node.hasProperty(MIX_COMMENT_ID)) {
485         commentID = node.getProperty(MIX_COMMENT_ID).getString();
486         if (StringUtils.isNotBlank(commentID)) activity = activityManager.getActivity(commentID);
487         commentFlag = (activity != null);
488       }
489     }
490     if (activity==null) {
491       String _activityType = StringUtils.isNotEmpty(activityType)?activityType:FILE_SPACES;
492       activity = createActivity(identityManager, activityOwnerId,
493                                 node, activityMsgBundleKey, _activityType, isSystemComment, systemComment, perm);
494       setActivityType(null);
495     }
496     
497     if (exa!=null) {
498       if (commentFlag) {
499         Map<String, String> paramsMap = activity.getTemplateParams();
500         String paramMessage = paramsMap.get(ContentUIActivity.MESSAGE);
501         String paramContent = paramsMap.get(ContentUIActivity.SYSTEM_COMMENT);
502         if (!StringUtils.isEmpty(paramMessage)) {
503           paramMessage += ActivityCommonService.VALUE_SEPERATOR + activityMsgBundleKey;
504           if (StringUtils.isEmpty(systemComment)) {
505             paramContent += ActivityCommonService.VALUE_SEPERATOR + " ";
506           }else {
507             paramContent += ActivityCommonService.VALUE_SEPERATOR + systemComment;
508           }
509         } else {
510           paramMessage = activityMsgBundleKey;
511           paramContent = systemComment;
512         }              
513         paramsMap.put(ContentUIActivity.MESSAGE, paramMessage);
514         paramsMap.put(ContentUIActivity.SYSTEM_COMMENT, paramContent);
515         activity.setTemplateParams(paramsMap);
516         updateNotifyMessages(activity, activityMsgBundleKey, systemComment);
517         activityManager.updateActivity(activity);
518       } else {
519         updateNotifyMessages(activity, activity.getTemplateParams().get(ContentUIActivity.MESSAGE), activity.getTemplateParams().get(ContentUIActivity.SYSTEM_COMMENT));
520         activityManager.saveComment(exa, activity);
521         if (activityCommonService.isEditing(node)) {
522           commentID = activity.getId();
523           if (node.canAddMixin(MIX_COMMENT)) node.addMixin(MIX_COMMENT);
524           if (node.isNodeType(MIX_COMMENT)) node.setProperty(MIX_COMMENT_ID, commentID);
525         }
526       }      
527       return activity;
528     }else {
529       String spaceGroupName = getSpaceName(node);
530       Space space = spaceService.getSpaceByGroupId(SpaceUtils.SPACE_GROUP + "/" + spaceGroupName);
531       if (spaceGroupName != null && spaceGroupName.length() > 0
532           && space != null) {
533         // post activity to space stream
534         Identity spaceIdentity = identityManager.getOrCreateIdentity(SpaceIdentityProvider.NAME,
535             space.getPrettyName(),
536             true);
537         activityManager.saveActivityNoReturn(spaceIdentity, activity);
538       } else if (activityOwnerId != null && activityOwnerId.length() > 0) {
539         if (!isPublic(node)) {
540           // only post activity to user status stream if that upload is public
541           return null;
542         }
543         // post activity to user status stream
544         Identity ownerIdentity = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME,
545             activityOwnerId,
546             true);
547         activityManager.saveActivityNoReturn(ownerIdentity, activity);
548       } else {
549         return null;
550       }
551       String activityId = activity != null ? activity.getId() : null;
552       if (!StringUtils.isEmpty(activityId)) {
553         ActivityTypeUtils.attachActivityId(node, activityId);
554       }
555 
556       if (node.isNodeType(ActivityTypeUtils.EXO_ACTIVITY_INFO)) {
557         try {
558           nodeActivityID = node.getProperty(ActivityTypeUtils.EXO_ACTIVITY_ID).getString();
559           exa = activityManager.getActivity(nodeActivityID);
560         } catch (Exception e) {
561           LOG.info("No activity is deleted, return no related activity");
562         }
563         if (exa != null && !commentFlag && isSystemComment) {
564           activity.setId(null);
565           activityManager.saveComment(exa, activity);
566           if (activityCommonService.isEditing(node)) {
567             commentID = activity.getId();
568             if (node.canAddMixin(MIX_COMMENT)) node.addMixin(MIX_COMMENT);
569             if (node.isNodeType(MIX_COMMENT)) node.setProperty(MIX_COMMENT_ID, commentID);
570           }
571         }
572       }
573 
574       return activity;
575     }
576   }
577   
578   public static void updateNotifyMessages(ExoSocialActivity activity, String activityMsgBundleKey, String systemComment)
579       throws Exception {     
580     Locale locale = new Locale("en");
581     ResourceBundleService resourceBundleService = WCMCoreUtils.getService(ResourceBundleService.class);
582     ResourceBundle res = resourceBundleService.getResourceBundle("locale.extension.SocialIntegration", locale);
583     StringBuffer sb = new StringBuffer();
584     String[] keys = activityMsgBundleKey.split(ActivityCommonService.VALUE_SEPERATOR);
585     String[] values = systemComment.split(ActivityCommonService.VALUE_SEPERATOR);
586     String message;
587     for (String key : keys) {
588       try {
589         message = res.getString(key);
590       } catch(MissingResourceException mre) {
591         message = key;
592       }
593       if(values.length > 0) {
594         for(int i = 0; i < values.length; i++) {
595           message = message.replace("{"+i+"}", values[i]);
596         }
597       }
598       sb.append(message).append("\n");
599     }
600     activity.setTitle(sb.toString());
601   }
602   
603   
604   private static void updateMainActivity(ActivityManager activityManager, Node contentNode, ExoSocialActivity activity) {
605     Map<String, String> activityParams = activity.getTemplateParams();
606     String state;
607     String nodeTitle;
608     String nodeType = null;
609     String documentTypeLabel;
610     String currentVersion = null;
611     TemplateService templateService = WCMCoreUtils.getService(TemplateService.class);
612     try {
613       nodeType = contentNode.getPrimaryNodeType().getName();
614       documentTypeLabel = templateService.getTemplateLabel(nodeType);
615     }catch (Exception e) {
616       documentTypeLabel = "";
617     }
618     try {
619       nodeTitle = org.exoplatform.ecm.webui.utils.Utils.getTitle(contentNode);
620     } catch (Exception e1) {
621       nodeTitle ="";
622     }
623     try {
624       state = contentNode.hasProperty(CURRENT_STATE_PROP) ? contentNode.getProperty(CURRENT_STATE_PROP)
625           .getValue()
626           .getString() : "";
627     } catch (Exception e) {
628       state="";
629     }
630     try {
631       currentVersion = contentNode.getBaseVersion().getName();
632       
633       //TODO Must improve this hardcode later, need specification
634       if (currentVersion.contains("jcr:rootVersion")) currentVersion = "0";
635     }catch (Exception e) {
636       currentVersion ="";
637     }
638     activityParams.put(ContentUIActivity.STATE, state);
639     activityParams.put(ContentUIActivity.DOCUMENT_TYPE_LABEL, documentTypeLabel);
640     activityParams.put(ContentUIActivity.DOCUMENT_TITLE, nodeTitle);
641     activityParams.put(ContentUIActivity.DOCUMENT_VERSION, currentVersion);
642     String summary = getSummary(contentNode);
643     summary =getFirstSummaryLines(summary, MAX_SUMMARY_LINES_COUNT);
644     activityParams.put(ContentUIActivity.DOCUMENT_SUMMARY, summary);
645     activity.setTemplateParams(activityParams);
646     activityManager.updateActivity(activity);
647   }
648   /**
649    * check the nodes that we support to post activities
650    * 
651    * @param node for checking
652    * @return result of checking
653    * @throws RepositoryException
654    */
655   private static boolean isSupportedContent(Node node) throws Exception {
656     if (getActivityOwnerId(node) != null && getActivityOwnerId(node).length() > 0) {
657       NodeHierarchyCreator nodeHierarchyCreator = (NodeHierarchyCreator) ExoContainerContext.getCurrentContainer()
658                                                                                             .getComponentInstanceOfType(NodeHierarchyCreator.class);
659       SessionProvider sessionProvider = WCMCoreUtils.getUserSessionProvider();
660       if(sessionProvider == null){
661     	  sessionProvider = WCMCoreUtils.getSystemSessionProvider();
662       }
663       Node userNode = nodeHierarchyCreator.getUserNode(sessionProvider, getActivityOwnerId(node));
664       if (userNode != null && node.getPath().startsWith(userNode.getPath() + "/Private/")) {
665         return false;
666       }
667     }
668 
669     return true;
670   }
671 
672   /**
673    * refine node for validation
674    * 
675    * @param currentNode
676    * @throws Exception
677    */
678   private static void refineNode(Node currentNode) throws Exception {
679     if (currentNode instanceof NodeImpl && !((NodeImpl) currentNode).isValid()) {
680       ExoContainer container = ExoContainerContext.getCurrentContainer();
681       LinkManager linkManager = (LinkManager) container.getComponentInstanceOfType(LinkManager.class);
682       if (linkManager.isLink(currentNode)) {
683         try {
684           currentNode = linkManager.getTarget(currentNode, false);
685         } catch (RepositoryException ex) {
686           currentNode = linkManager.getTarget(currentNode, true);
687         }
688       }
689     }
690   }
691 
692   /**
693    * get activity owner
694    * 
695    * @return activity owner
696    */
697   private static String getActivityOwnerId(Node node) {
698     String activityOwnerId = "";
699     ConversationState conversationState = ConversationState.getCurrent();
700     if (conversationState != null) {
701       activityOwnerId = conversationState.getIdentity().getUserId();
702     }else{
703       try {
704         activityOwnerId = node.getProperty("publication:lastUser").getString();
705       } catch (Exception e) {
706         LOG.info("No lastUser publication");
707       }	
708     }
709     return activityOwnerId;
710   }
711   
712   /**
713    * get the space name of node
714    * 
715    * @param node
716    * @return the group name
717    * @throws Exception
718    */
719   private static String getSpaceName(Node node) throws Exception {
720     NodeHierarchyCreator nodeHierarchyCreator = (NodeHierarchyCreator) ExoContainerContext.getCurrentContainer()
721                                                                                           .getComponentInstanceOfType(NodeHierarchyCreator.class);
722     String groupPath = nodeHierarchyCreator.getJcrPath(BasePath.CMS_GROUPS_PATH);
723     String spacesFolder = groupPath + "/spaces/";
724     String spaceName = "";
725     String nodePath = node.getPath();
726     if (nodePath.startsWith(spacesFolder)) {
727       spaceName = nodePath.substring(spacesFolder.length());
728       spaceName = spaceName.substring(0, spaceName.indexOf("/"));
729     }
730 
731     return spaceName;
732   }
733 
734   private static boolean isPublic(Node node) {
735     if (node instanceof ExtendedNode) {
736       ExtendedNode n = (ExtendedNode)node;
737       try {
738         List<String> permissions =n.getACL().getPermissions("any");
739         if(permissions != null && permissions.size() > 0) {
740           for (String p : permissions) {
741             if ("read".equalsIgnoreCase(p)) {
742               return true;
743             }
744           }
745         }
746       } catch (RepositoryException ex) {
747         return false;
748       }
749     }
750     return false;
751   }
752 
753   /**
754    * Generate the viewer link to site explorer by node
755    * 
756    * @param node the node
757    * @return String the viewer link
758    * @throws RepositoryException
759    */
760   public static String getContentLink(Node node) throws Exception {
761     DocumentService documentService = CommonsUtils.getService(DocumentService.class);
762     return documentService.getShortLinkInDocumentsApp(node.getSession().getWorkspace().getName(), ((NodeImpl)node).getInternalIdentifier());
763   }
764 
765   /**
766    * Create ExoSocialActivity
767    * 
768    * @param identityManager the identity Manager
769    * @param activityOwnerId the remote user name
770    * @param node the node
771    * @param activityMsgBundleKey the message bundle key
772    * @param activityType the activity type
773    * @return the ExoSocialActivity
774    * @throws Exception the activity storage exception
775    */
776   public static ExoSocialActivity createActivity(IdentityManager identityManager,
777                                                  String activityOwnerId, Node node,
778                                                  String activityMsgBundleKey, String activityType) throws Exception {
779 	  return createActivity(identityManager, activityOwnerId, node, activityMsgBundleKey, activityType, false, null, null);
780   }
781   public static ExoSocialActivity createActivity(IdentityManager identityManager,
782                                                  String activityOwnerId,
783                                                  Node node, String activityMsgBundleKey, String activityType,
784                                                  boolean isSystemComment,  String systemComment, String perm) throws Exception {
785 		// Populate activity data
786 	Map<String, String> activityParams = populateActivityData(node, activityOwnerId, activityMsgBundleKey, isSystemComment, systemComment, perm);
787 	
788     String title = node.hasProperty(NodetypeConstant.EXO_TITLE) ? node.getProperty(NodetypeConstant.EXO_TITLE)
789                                                                       .getString()
790                                                                : org.exoplatform.ecm.webui.utils.Utils.getTitle(node);
791     ExoSocialActivity activity = new ExoSocialActivityImpl();
792     String userId = "";
793     if(ConversationState.getCurrent() != null)
794     {
795       userId = ConversationState.getCurrent().getIdentity().getUserId();
796     }else{
797       userId = activityOwnerId;
798     }
799     Identity identity = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, 
800       userId, false);
801     activity.setUserId(identity.getId());
802     activity.setType(activityType);
803     activity.setUrl(node.getPath());
804     if(StringUtils.isNotEmpty(activityMsgBundleKey) && StringUtils.isNotEmpty(systemComment)) {
805       updateNotifyMessages(activity, activityMsgBundleKey, systemComment);
806     } else if(StringUtils.isNotEmpty(systemComment)){
807         activity.setTitle(systemComment);
808     } else {
809         activity.setTitle(title);
810     }
811     activity.setTemplateParams(activityParams);
812     return activity;
813   }
814   
815   public static void deleteFileActivity(Node node) throws RepositoryException {
816     // get services
817     ExoContainer container = ExoContainerContext.getCurrentContainer();
818     ActivityManager activityManager = (ActivityManager) container.getComponentInstanceOfType(ActivityManager.class);
819     
820     // get owner
821     String nodeActivityID = StringUtils.EMPTY;
822     if (node.isNodeType(ActivityTypeUtils.EXO_ACTIVITY_INFO)) {
823       try {
824         nodeActivityID = node.getProperty(ActivityTypeUtils.EXO_ACTIVITY_ID).getString();
825         if(activityManager.getActivity(nodeActivityID) != null) {
826           activityManager.deleteActivity(nodeActivityID);
827         }
828       } catch (Exception e) {
829         LOG.info("No activity is deleted, return no related activity");
830       }
831     }    
832   }
833 
834   /**
835    * Gets the illustrative image.
836    * 
837    * @param node the node
838    * @return the illustrative image
839    */
840   public static String getIllustrativeImage(Node node) {
841     WebSchemaConfigService schemaConfigService = WCMCoreUtils.getService(WebSchemaConfigService.class);
842     WebContentSchemaHandler contentSchemaHandler = schemaConfigService.getWebSchemaHandlerByType(WebContentSchemaHandler.class);
843     Node illustrativeImage = null;
844     String uri = "";
845     try {
846       illustrativeImage = contentSchemaHandler.getIllustrationImage(node);
847       uri = generateThumbnailImageURI(illustrativeImage);
848     } catch (PathNotFoundException ex) {
849       return uri;
850     } catch (Exception e) { // WebContentSchemaHandler
851       LOG.warn(e.getMessage(), e);
852     }
853     return uri;
854   }
855 
856   /**
857    * Generate the Thumbnail Image URI.
858    * 
859    * @param file the node
860    * @return the Thumbnail uri with medium size
861    * @throws Exception the exception
862    */
863   public static String generateThumbnailImageURI(Node file) throws Exception {
864     StringBuilder builder = new StringBuilder();
865     NodeLocation fielLocation = NodeLocation.getNodeLocationByNode(file);
866     String repository = fielLocation.getRepository();
867     String workspaceName = fielLocation.getWorkspace();
868     String nodeIdentifiler = file.getPath().replaceFirst("/", "");
869     String portalName = PortalContainer.getCurrentPortalContainerName();
870     String restContextName = PortalContainer.getCurrentRestContextName();
871     InputStream stream = file.getNode(NodetypeConstant.JCR_CONTENT)
872                              .getProperty(NodetypeConstant.JCR_DATA)
873                              .getStream();
874     if (stream.available() == 0)
875       return null;
876     stream.close();
877     builder.append("/")
878            .append(portalName)
879            .append("/")
880            .append(restContextName)
881            .append("/")
882            .append("thumbnailImage/medium/")
883            .append(repository)
884            .append("/")
885            .append(workspaceName)
886            .append("/")
887            .append(nodeIdentifiler);
888     return builder.toString();
889   }
890 
891   /**
892    * Get the MimeType
893    * 
894    * @param node the node
895    * @return the MimeType
896    */
897   public static String getMimeType(Node node) {
898     try {
899       if (node.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) {
900         if (node.hasNode(NodetypeConstant.JCR_CONTENT))
901           return node.getNode(NodetypeConstant.JCR_CONTENT)
902                      .getProperty(NodetypeConstant.JCR_MIME_TYPE)
903                      .getString();
904       }
905     } catch (RepositoryException e) {
906       LOG.error(e.getMessage(), e);
907     }
908     return "";
909   }
910   
911   public static String getSummary(Node node) {
912     String desc = "";
913     try {
914       if (node != null) {
915         if (node.hasProperty("exo:summary")) {
916           desc = node.getProperty("exo:summary").getValue().getString();
917         } else if (node.hasNode("jcr:content")) {
918           Node content = node.getNode("jcr:content");
919           if (content.hasProperty("dc:description") && content.getProperty("dc:description").getValues().length > 0) {
920             desc = content.getProperty("dc:description").getValues()[0].getString();
921           }
922         }
923       }
924     } catch (RepositoryException re) {
925       if (LOG.isWarnEnabled())
926         LOG.warn("RepositoryException: ", re);
927     }
928     return desc;
929   }
930   public static String getFirstSummaryLines(String source) {
931     return getFirstSummaryLines(source, MAX_SUMMARY_LINES_COUNT);
932   }
933   
934   
935   private static String convertActivityContent(String source){
936     String result =  source;
937     result = result.replaceAll("(?i)<head>.*</head>", "");
938     result = result.replaceAll("(?i)<script.*>.*</script>", "");
939     result = result.replaceAll("(?i)<style.*>.*</style>", "");
940     result = result.replaceAll("<([a-zA-Z\"]+) *[^/]*?>", "");
941     result = result.replaceAll("</p>", "<br>");
942     result = result.replaceAll("</([a-zA-Z]+) *[^/]*?>", "");
943     result = result.replaceAll("([\r\n\t])+", "");
944     result = result.replaceAll("^(<br>)", "");
945     result = result.replaceAll("(<br>[ \r\t\n]+<br>)", "\n");
946     result = result.replaceAll("(<br>)+", "\n");
947     return result;
948   }
949   
950   /**
951    * 
952    * @param source
953    * @param linesCount
954    * @return first {@code linesCount} without HTML tag
955    */
956   public static String getFirstSummaryLines(String source, int linesCount) {
957     String result =  convertActivityContent(source);
958     int i = 0;
959     int index = -1;
960     while (true) {
961       index = result.indexOf("\n", index+1);
962       if (index<0) break;
963       i++;
964       if (i>=linesCount) break;
965     }
966     if (index <0) {
967       if (result.length()>MAX_SUMMARY_CHAR_COUNT)
968       return  result.substring(0, MAX_SUMMARY_CHAR_COUNT-1) + "...";
969       return result;
970     }
971     if (index>MAX_SUMMARY_CHAR_COUNT) index = MAX_SUMMARY_CHAR_COUNT-1;
972     result = result.substring(0, index) + "\n...";
973     return result;
974   }
975 
976   public static String[] getSystemCommentTitle(Map<String, String> activityParams) {
977     String[] result;
978     if (activityParams == null) return null;
979     String commentValue = activityParams.get(FileUIActivity.SYSTEM_COMMENT);
980     if (!StringUtils.isEmpty(commentValue)) {
981       if (commentValue.indexOf(ActivityCommonService.VALUE_SEPERATOR) >= 0) {
982         result = commentValue.split(ActivityCommonService.VALUE_SEPERATOR);
983         return result;
984       } else {
985         return new String[]{commentValue};
986       }
987     }
988     return null;
989   }
990 
991   public static String[] getSystemCommentBundle(Map<String, String> activityParams) {
992     String[] result;
993     if (activityParams == null) return null;
994     String tmp = activityParams.get(FileUIActivity.IS_SYSTEM_COMMENT);
995     String commentMessage;
996     if (tmp == null) return null;
997     try {
998       if (Boolean.parseBoolean(tmp)) {
999         commentMessage = activityParams.get(FileUIActivity.MESSAGE);
1000         if (!StringUtils.isEmpty(commentMessage)) {
1001           if (commentMessage.indexOf(ActivityCommonService.VALUE_SEPERATOR) >= 0) {
1002             result = commentMessage.split(ActivityCommonService.VALUE_SEPERATOR);
1003             return result;
1004           } else {
1005             return new String[]{commentMessage};
1006           }
1007         }
1008       }
1009     } catch (Exception e) {
1010       return null;
1011     }
1012     return null;
1013   }
1014 
1015   public static String getBundleValue(String key) {
1016     try {
1017       WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
1018       ResourceBundle res = context.getApplicationResourceBundle();
1019       String value = res.getString(key);
1020       return value;
1021     } catch (MissingResourceException e) {
1022       return key;
1023     }
1024   }
1025 
1026   public static String processMentions(String comment) {
1027     String excerpts[] = comment.split("@");
1028     comment = excerpts[0];
1029     String mentioned = "";
1030     for (int i=1; i<excerpts.length; i++) {
1031       String name = excerpts[i].split(" ")[0];
1032       Identity identity = org.exoplatform.social.notification.Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, name, true);
1033       if (identity != null) {
1034         mentioned = addMentioned(name, identity.getProfile().getFullName());
1035       }
1036       if (mentioned.isEmpty()) {
1037         if (excerpts[i].isEmpty()) comment = comment + " ";
1038         else comment = comment + excerpts[i] + " ";
1039       } else {
1040         comment = comment + mentioned + excerpts[i].substring(name.length(),excerpts[i].length());
1041         mentioned = "";
1042       }
1043     }
1044     return comment;
1045   }
1046 
1047   private static String addMentioned(String mention, String fullname) {
1048     String profileURL = CommonsUtils.getCurrentDomain() + LinkProvider.getProfileUri(mention);
1049     return "<a href=" + profileURL + " type=\"mentionedUser\" rel=\"nofollow\">" + fullname + "</a>";
1050   }
1051 
1052   public static void setAvatarUrl(Node commentNode) throws RepositoryException {
1053     String name = commentNode.getProperty("exo:commentor").getString();;
1054     Identity identity = org.exoplatform.social.notification.Utils.getIdentityManager().getOrCreateIdentity(OrganizationIdentityProvider.NAME, name, true);
1055     Profile profile = identity.getProfile();
1056     if (profile.getAvatarUrl() != null ) {
1057       commentNode.setProperty("exo:commentorAvatar", profile.getAvatarUrl());
1058     } else {
1059       commentNode.setProperty("exo:commentorAvatar", DEFAULT_AVATAR);
1060     } ;
1061   }
1062 }