1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
70
71
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
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
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
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
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
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
270
271 private class DateComparator implements Comparator<Node> {
272
273
274
275
276
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
291
292
293
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 }