View Javadoc
1   /*
2    * Copyright (C) 2003-2007 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.cms.comments.impl;
18  
19  import java.util.ArrayList;
20  import java.util.Calendar;
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.Date;
24  import java.util.GregorianCalendar;
25  import java.util.List;
26  
27  import javax.jcr.Node;
28  import javax.jcr.NodeIterator;
29  import javax.jcr.Session;
30  
31  import org.exoplatform.commons.utils.ActivityTypeUtils;
32  import org.exoplatform.services.cache.CacheService;
33  import org.exoplatform.services.cache.ExoCache;
34  import org.exoplatform.services.cms.comments.CommentsService;
35  import org.exoplatform.services.cms.i18n.MultiLanguageService;
36  import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService;
37  import org.exoplatform.services.jcr.core.ManageableRepository;
38  import org.exoplatform.services.listener.ListenerService;
39  import org.exoplatform.services.log.ExoLogger;
40  import org.exoplatform.services.log.Log;
41  import org.exoplatform.services.organization.OrganizationService;
42  import org.exoplatform.services.organization.User;
43  import org.exoplatform.services.wcm.core.NodetypeConstant;
44  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
45  
46  public class CommentsServiceImpl implements CommentsService {
47  
48    private static final Log LOG = ExoLogger.getLogger(CommentsServiceImpl.class.getName());
49    private static final String CACHE_NAME = "ecms.CommentsService" ;
50  
51    private final static String COMMENTS = "comments" ;
52    private final static String COMMENTABLE = "mix:commentable" ;
53    private final static String EXO_COMMENTS = "exo:comments" ;
54    private final static String NT_UNSTRUCTURE = "nt:unstructured" ;
55    private final static String MESSAGE = "exo:commentContent" ;
56    private final static String COMMENTOR = "exo:commentor" ;
57    private final static String COMMENTOR_FULLNAME = "exo:commentorFullName" ;
58    private final static String COMMENTOR_EMAIL = "exo:commentorEmail" ;
59    private final static String COMMENTOR_SITE = "exo:commentorSite" ;
60    private final static String CREATED_DATE = "exo:commentDate" ;
61    private static final String LANGUAGES = "languages" ;
62    private static final String ANONYMOUS = "anonymous" ;
63  
64    private ExoCache<String, List<Node>> commentsCache_ ;
65    private MultiLanguageService         multiLangService_ ;
66    private ListenerService              listenerService;
67    private ActivityCommonService        activityService;
68    /**
69     * Constructor Method
70     * @param cacheService        CacheService Object
71     * @param multiLangService    MultiLanguageService Object
72     */
73    public CommentsServiceImpl(CacheService cacheService,
74                               MultiLanguageService multiLangService) throws Exception {
75      commentsCache_ = cacheService.getCacheInstance(CACHE_NAME) ;
76      multiLangService_ = multiLangService ;
77      activityService = WCMCoreUtils.getService(ActivityCommonService.class);
78    }
79  
80    /**
81     * {@inheritDoc}
82     */
83    public void addComment(Node node, String commentor,String email, String site, String comment,String language)
84        throws Exception {
85      if (listenerService==null) {
86        listenerService = WCMCoreUtils.getService(ListenerService.class);
87      }
88      Session session = node.getSession();
89      try {
90        Node document = (Node)session.getItem(node.getPath());
91        if(!document.isNodeType(COMMENTABLE)) {
92          if(document.canAddMixin(COMMENTABLE)) document.addMixin(COMMENTABLE) ;
93          else throw new Exception("This node does not support comments.") ;
94        }
95        Node multiLanguages =null, languageNode= null, commentNode = null ;
96  
97        if(!document.hasNode(LANGUAGES) || language.equals(multiLangService_.getDefault(document))) {
98          if(document.hasNode(COMMENTS)) commentNode = document.getNode(COMMENTS) ;
99          else {
100           commentNode = document.addNode(COMMENTS,NT_UNSTRUCTURE) ;          
101           commentNode.addMixin("exo:hiddenable");
102         }
103       } else {
104         multiLanguages = document.getNode(LANGUAGES) ;
105         if(multiLanguages.hasNode(language)) {
106           languageNode = multiLanguages.getNode(language) ;
107         } else {
108           languageNode = multiLanguages.addNode(language) ;
109         }
110         if(languageNode.hasNode(COMMENTS)) {
111           commentNode = languageNode.getNode(COMMENTS) ;
112         } else{
113           commentNode = languageNode.addNode(COMMENTS,NT_UNSTRUCTURE) ;
114           commentNode.addMixin("exo:hiddenable");
115         }
116       }
117 
118       if(commentor == null || commentor.length() == 0) {
119         commentor = ANONYMOUS ;
120       }      
121 
122       Calendar commentDate = new GregorianCalendar() ;
123       String name = Long.toString(commentDate.getTimeInMillis()) ;
124       Node newComment = commentNode.addNode(name,EXO_COMMENTS) ;
125       newComment.setProperty(COMMENTOR,commentor) ;
126 
127       OrganizationService organizationService = WCMCoreUtils.getService(OrganizationService.class);
128       User user = organizationService.getUserHandler().findUserByName(commentor);
129      
130       if(user == null)
131         newComment.setProperty(COMMENTOR_FULLNAME,"ANONYMOUS") ;
132       else {
133         String fullName = user.getDisplayName();
134         if(fullName == null) fullName = user.getUserName();
135         newComment.setProperty(COMMENTOR_FULLNAME,fullName) ; 
136       }
137       newComment.setProperty(CREATED_DATE,commentDate) ;
138       newComment.setProperty(MESSAGE,comment) ;
139       if(email!=null && email.length()>0) {
140         newComment.setProperty(COMMENTOR_EMAIL,email) ;
141       }
142       if(site !=null && site.length()>0) {
143         newComment.setProperty(COMMENTOR_SITE,site) ;
144       }
145       document.save();
146       if (listenerService!=null) {
147         try {
148           if (activityService.isAcceptedNode(document) 
149               || (document.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE) 
150                   && activityService.isBroadcastNTFileEvents(document))) {
151             listenerService.broadcast(ActivityCommonService.COMMENT_ADDED_ACTIVITY, document, newComment);
152           }
153         } catch (Exception e) {
154           if (LOG.isErrorEnabled()) {
155             LOG.error("Can not notify CommentAddedActivity because of: " + e.getMessage());
156           }
157         }
158       }
159       commentsCache_.remove(commentNode.getPath()) ;
160     } catch(Exception e) {
161       if (LOG.isErrorEnabled()) {
162         LOG.error("Unexpected problem happen when try to add comment", e);
163       }
164     }
165 
166   }
167 
168   /**
169    * {@inheritDoc}
170    */
171   public void updateComment(Node commentNode, String newComment) throws Exception {
172     Calendar commentDate = new GregorianCalendar() ;
173     commentNode.setProperty(CREATED_DATE, commentDate);
174     commentNode.setProperty(MESSAGE, newComment);
175     commentNode.save();
176     Node documentNode = commentNode.getParent().getParent();
177     if (listenerService!=null && activityService!=null) {
178       try {
179         if (activityService.isAcceptedNode(documentNode) || 
180             (documentNode.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE) && 
181                 activityService.isBroadcastNTFileEvents(documentNode))) {
182           listenerService.broadcast(ActivityCommonService.COMMENT_UPDATED_ACTIVITY, documentNode, commentNode);
183         }
184       } catch (Exception e) {
185         if (LOG.isErrorEnabled()) {
186           LOG.error("Can not notify CommentModifiedActivity because of: " + e.getMessage());
187         }
188       }
189     }
190   }
191 
192   /**
193    * {@inheritDoc}
194    */
195   public void deleteComment(Node commentNode) throws Exception {
196     Node document = commentNode.getParent();
197     String activityID;
198     try {
199       activityID = ActivityTypeUtils.getActivityId(commentNode);
200     }catch (Exception e) {
201       activityID = null;
202     }
203     commentNode.remove();
204     document.save();    
205     if (listenerService!=null && activityID !=null && activityService !=null) {
206       Node parentNode = document.getParent();
207       try {
208         if (activityService.isAcceptedNode(parentNode) || 
209             (parentNode.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE) && 
210                 activityService.isBroadcastNTFileEvents(parentNode))) {
211           listenerService.broadcast(ActivityCommonService.COMMENT_REMOVED_ACTIVITY, parentNode, activityID);
212         }
213       } catch (Exception e) {
214         if (LOG.isErrorEnabled()) {
215           LOG.error("Can not notify CommentRemovedActivity because of: " + e.getMessage());
216         }
217       }
218     }
219   }
220 
221   /**
222    * {@inheritDoc}
223    */
224   @SuppressWarnings("unchecked")
225   public List<Node> getComments(Node document,String language) throws Exception {
226     Node commentsNode = null ;
227     Node languagesNode = null ;
228     Node languageNode = null ;
229     if(!isSupportedLocalize(document,language)) {
230       if(document.hasProperty("exo:language")) language = document.getProperty("exo:language").getString() ;
231     }
232     if(document.hasNode(LANGUAGES)) {
233       languagesNode = document.getNode(LANGUAGES) ;
234       if(languagesNode.hasNode(language)) {
235         languageNode = languagesNode.getNode(language) ;
236         if(languageNode.hasNode(COMMENTS)) commentsNode = languageNode.getNode(COMMENTS) ;
237       } else if(language.equals(multiLangService_.getDefault(document))) {
238         languageNode = document ;
239       }
240     } else {
241       languageNode = document ;
242     }
243     if(!languageNode.hasNode(COMMENTS)) return new ArrayList<Node>() ;
244     Session session = document.getSession();
245     //TODO check if really need delegate to system session
246     Session systemSession = WCMCoreUtils.getSystemSessionProvider().getSession(session.getWorkspace().getName(),
247                                                                                WCMCoreUtils.getRepository()) ;
248     List<Node> list = new ArrayList<Node>() ;
249     try {
250       commentsNode = (Node)systemSession.getItem(languageNode.getPath() + "/" + COMMENTS) ;
251       String cacheKey = document.getPath().concat(commentsNode.getPath());
252       Object comments = commentsCache_.get(cacheKey) ;
253       if(comments !=null) return (List<Node>)comments ;
254       for(NodeIterator iter = commentsNode.getNodes(); iter.hasNext();) {
255         list.add(iter.nextNode()) ;
256       }
257       Collections.sort(list,new DateComparator()) ;
258       commentsCache_.put(commentsNode.getPath(),list) ;
259     } catch(Exception e) {
260       if (LOG.isErrorEnabled()) {
261         LOG.error("Unexpected problem happen when try to get comments", e);
262       }
263     }
264     return list;
265   }
266 
267 
268   /**
269    * This Class implements Comparator<Node> to compare the created date of nodes.
270    */
271   private class DateComparator implements Comparator<Node> {
272 
273     /**
274      * Compare the created date of nodes
275      * @param node1     node is used to compare
276      * @param node2     node is used to compare
277      */
278     public int compare(Node node1, Node node2) {
279       try{
280         Date date1 = node1.getProperty(CREATED_DATE).getDate().getTime() ;
281         Date date2 = node2.getProperty(CREATED_DATE).getDate().getTime() ;
282         return date2.compareTo(date1) ;
283       }catch (Exception e) {
284         return 0;
285       }
286     }
287   }
288 
289   /**
290    * Check language of comment is supported in a document
291    * @param  document    The document node is commented
292    * @param  language    The language of comment node
293    * @throws Exception
294    */
295   private boolean isSupportedLocalize(Node document,String language)throws Exception {
296     List<String> locales= multiLangService_.getSupportedLanguages(document) ;
297     if(Collections.frequency(locales,language) >0) return true ;
298     return false ;
299   }
300 
301 }