1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.messageboards.service.impl;
24  
25  import com.liferay.portal.PortalException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.search.Hits;
28  import com.liferay.portal.kernel.util.GetterUtil;
29  import com.liferay.portal.kernel.util.Validator;
30  import com.liferay.portal.lucene.LuceneFields;
31  import com.liferay.portal.lucene.LuceneUtil;
32  import com.liferay.portal.model.User;
33  import com.liferay.portal.model.impl.CompanyImpl;
34  import com.liferay.portal.model.impl.ResourceImpl;
35  import com.liferay.portal.util.PortalUtil;
36  import com.liferay.portlet.messageboards.CategoryNameException;
37  import com.liferay.portlet.messageboards.model.MBCategory;
38  import com.liferay.portlet.messageboards.model.MBMessage;
39  import com.liferay.portlet.messageboards.model.MBThread;
40  import com.liferay.portlet.messageboards.model.impl.MBCategoryImpl;
41  import com.liferay.portlet.messageboards.service.base.MBCategoryLocalServiceBaseImpl;
42  import com.liferay.portlet.messageboards.util.Indexer;
43  import com.liferay.util.lucene.HitsImpl;
44  
45  import java.io.IOException;
46  
47  import java.util.ArrayList;
48  import java.util.Date;
49  import java.util.Iterator;
50  import java.util.List;
51  
52  import org.apache.commons.logging.Log;
53  import org.apache.commons.logging.LogFactory;
54  import org.apache.lucene.document.Document;
55  import org.apache.lucene.index.IndexWriter;
56  import org.apache.lucene.index.Term;
57  import org.apache.lucene.queryParser.ParseException;
58  import org.apache.lucene.search.BooleanClause;
59  import org.apache.lucene.search.BooleanQuery;
60  import org.apache.lucene.search.Searcher;
61  import org.apache.lucene.search.TermQuery;
62  
63  /**
64   * <a href="MBCategoryLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
65   *
66   * @author Brian Wing Shun Chan
67   *
68   */
69  public class MBCategoryLocalServiceImpl extends MBCategoryLocalServiceBaseImpl {
70  
71      public MBCategory addCategory(
72              long userId, long plid, long parentCategoryId, String name,
73              String description, boolean addCommunityPermissions,
74              boolean addGuestPermissions)
75          throws PortalException, SystemException {
76  
77          return addCategory(
78              null, userId, plid, parentCategoryId, name, description,
79              Boolean.valueOf(addCommunityPermissions),
80              Boolean.valueOf(addGuestPermissions), null, null);
81      }
82  
83      public MBCategory addCategory(
84              String uuid, long userId, long plid, long parentCategoryId,
85              String name, String description, boolean addCommunityPermissions,
86              boolean addGuestPermissions)
87          throws PortalException, SystemException {
88  
89          return addCategory(
90              uuid, userId, plid, parentCategoryId, name, description,
91              Boolean.valueOf(addCommunityPermissions),
92              Boolean.valueOf(addGuestPermissions), null, null);
93      }
94  
95      public MBCategory addCategory(
96              long userId, long plid, long parentCategoryId, String name,
97              String description, String[] communityPermissions,
98              String[] guestPermissions)
99          throws PortalException, SystemException {
100 
101         return addCategory(
102             null, userId, plid, parentCategoryId, name, description, null, null,
103             communityPermissions, guestPermissions);
104     }
105 
106     public MBCategory addCategory(
107             String uuid, long userId, long plid, long parentCategoryId,
108             String name, String description, Boolean addCommunityPermissions,
109             Boolean addGuestPermissions, String[] communityPermissions,
110             String[] guestPermissions)
111         throws PortalException, SystemException {
112 
113         // Category
114 
115         User user = userPersistence.findByPrimaryKey(userId);
116         long groupId = PortalUtil.getPortletGroupId(plid);
117         parentCategoryId = getParentCategoryId(groupId, parentCategoryId);
118         Date now = new Date();
119 
120         validate(name);
121 
122         long categoryId = counterLocalService.increment();
123 
124         MBCategory category = mbCategoryPersistence.create(categoryId);
125 
126         category.setUuid(uuid);
127         category.setGroupId(groupId);
128         category.setCompanyId(user.getCompanyId());
129         category.setUserId(user.getUserId());
130         category.setUserName(user.getFullName());
131         category.setCreateDate(now);
132         category.setModifiedDate(now);
133         category.setParentCategoryId(parentCategoryId);
134         category.setName(name);
135         category.setDescription(description);
136 
137         mbCategoryPersistence.update(category);
138 
139         // Resources
140 
141         if ((addCommunityPermissions != null) &&
142             (addGuestPermissions != null)) {
143 
144             addCategoryResources(
145                 category, addCommunityPermissions.booleanValue(),
146                 addGuestPermissions.booleanValue());
147         }
148         else {
149             addCategoryResources(
150                 category, communityPermissions, guestPermissions);
151         }
152 
153         return category;
154     }
155 
156     public void addCategoryResources(
157             long categoryId, boolean addCommunityPermissions,
158             boolean addGuestPermissions)
159         throws PortalException, SystemException {
160 
161         MBCategory category = mbCategoryPersistence.findByPrimaryKey(
162             categoryId);
163 
164         addCategoryResources(
165             category, addCommunityPermissions, addGuestPermissions);
166     }
167 
168     public void addCategoryResources(
169             MBCategory category, boolean addCommunityPermissions,
170             boolean addGuestPermissions)
171         throws PortalException, SystemException {
172 
173         resourceLocalService.addResources(
174             category.getCompanyId(), category.getGroupId(),
175             category.getUserId(), MBCategory.class.getName(),
176             category.getCategoryId(), false, addCommunityPermissions,
177             addGuestPermissions);
178     }
179 
180     public void addCategoryResources(
181             long categoryId, String[] communityPermissions,
182             String[] guestPermissions)
183         throws PortalException, SystemException {
184 
185         MBCategory category = mbCategoryPersistence.findByPrimaryKey(
186             categoryId);
187 
188         addCategoryResources(category, communityPermissions, guestPermissions);
189     }
190 
191     public void addCategoryResources(
192             MBCategory category, String[] communityPermissions,
193             String[] guestPermissions)
194         throws PortalException, SystemException {
195 
196         resourceLocalService.addModelResources(
197             category.getCompanyId(), category.getGroupId(),
198             category.getUserId(), MBCategory.class.getName(),
199             category.getCategoryId(), communityPermissions, guestPermissions);
200     }
201 
202     public void deleteCategories(long groupId)
203         throws PortalException, SystemException {
204 
205         Iterator itr = mbCategoryPersistence.findByG_P(
206             groupId, MBCategoryImpl.DEFAULT_PARENT_CATEGORY_ID).iterator();
207 
208         while (itr.hasNext()) {
209             MBCategory category = (MBCategory)itr.next();
210 
211             deleteCategory(category);
212         }
213     }
214 
215     public void deleteCategory(long categoryId)
216         throws PortalException, SystemException {
217 
218         MBCategory category = mbCategoryPersistence.findByPrimaryKey(
219             categoryId);
220 
221         deleteCategory(category);
222     }
223 
224     public void deleteCategory(MBCategory category)
225         throws PortalException, SystemException {
226 
227         // Categories
228 
229         Iterator itr = mbCategoryPersistence.findByG_P(
230             category.getGroupId(), category.getCategoryId()).iterator();
231 
232         while (itr.hasNext()) {
233             MBCategory curCategory = (MBCategory)itr.next();
234 
235             deleteCategory(curCategory);
236         }
237 
238         // Lucene
239 
240         try {
241             Indexer.deleteMessages(
242                 category.getCompanyId(), category.getCategoryId());
243         }
244         catch (IOException ioe) {
245             _log.error("Deleting index " + category.getCategoryId(), ioe);
246         }
247         catch (ParseException pe) {
248             _log.error("Deleting index " + category.getCategoryId(), pe);
249         }
250 
251         // Threads
252 
253         mbThreadLocalService.deleteThreads(category.getCategoryId());
254 
255         // Resources
256 
257         resourceLocalService.deleteResource(
258             category.getCompanyId(), MBCategory.class.getName(),
259             ResourceImpl.SCOPE_INDIVIDUAL, category.getCategoryId());
260 
261         // Category
262 
263         mbCategoryPersistence.remove(category.getCategoryId());
264     }
265 
266     public List getCategories(
267             long groupId, long parentCategoryId, int begin, int end)
268         throws SystemException {
269 
270         return mbCategoryPersistence.findByG_P(
271             groupId, parentCategoryId, begin, end);
272     }
273 
274     public int getCategoriesCount(long groupId) throws SystemException {
275         return mbCategoryPersistence.countByGroupId(groupId);
276     }
277 
278     public int getCategoriesCount(long groupId, long parentCategoryId)
279         throws SystemException {
280 
281         return mbCategoryPersistence.countByG_P(groupId, parentCategoryId);
282     }
283 
284     public MBCategory getCategory(long categoryId)
285         throws PortalException, SystemException {
286 
287         return mbCategoryPersistence.findByPrimaryKey(categoryId);
288     }
289 
290     public void getSubcategoryIds(
291             List categoryIds, long groupId, long categoryId)
292         throws SystemException {
293 
294         Iterator itr = mbCategoryPersistence.findByG_P(
295             groupId, categoryId).iterator();
296 
297         while (itr.hasNext()) {
298             MBCategory category = (MBCategory)itr.next();
299 
300             categoryIds.add(new Long(category.getCategoryId()));
301 
302             getSubcategoryIds(
303                 categoryIds, category.getGroupId(), category.getCategoryId());
304         }
305     }
306 
307     public List getSubscribedCategories(
308             long groupId, long userId, int begin, int end)
309         throws SystemException {
310 
311         return mbCategoryFinder.findByS_G_U(groupId, userId, begin, end);
312     }
313 
314     public int getSubscribedCategoriesCount(long groupId, long userId)
315         throws SystemException {
316 
317         return mbCategoryFinder.countByS_G_U(groupId, userId);
318     }
319 
320     public MBCategory getSystemCategory()
321         throws PortalException, SystemException {
322 
323         long categoryId = CompanyImpl.SYSTEM;
324 
325         MBCategory category = mbCategoryPersistence.fetchByPrimaryKey(
326             categoryId);
327 
328         if (category == null) {
329             category = mbCategoryPersistence.create(categoryId);
330 
331             category.setCompanyId(CompanyImpl.SYSTEM);
332             category.setUserId(CompanyImpl.SYSTEM);
333 
334             mbCategoryPersistence.update(category);
335         }
336 
337         return category;
338     }
339 
340     public void reIndex(String[] ids) throws SystemException {
341         if (LuceneUtil.INDEX_READ_ONLY) {
342             return;
343         }
344 
345         long companyId = GetterUtil.getLong(ids[0]);
346 
347         IndexWriter writer = null;
348 
349         try {
350             writer = LuceneUtil.getWriter(companyId);
351 
352             Iterator itr1 = mbCategoryPersistence.findByCompanyId(
353                 companyId).iterator();
354 
355             while (itr1.hasNext()) {
356                 MBCategory category = (MBCategory)itr1.next();
357 
358                 long categoryId = category.getCategoryId();
359 
360                 Iterator itr2 = mbMessagePersistence.findByCategoryId(
361                     categoryId).iterator();
362 
363                 while (itr2.hasNext()) {
364                     MBMessage message = (MBMessage)itr2.next();
365 
366                     long groupId = category.getGroupId();
367                     String userName = message.getUserName();
368                     long threadId = message.getThreadId();
369                     long messageId = message.getMessageId();
370                     String title = message.getSubject();
371                     String content = message.getBody();
372 
373                     String[] tagsEntries = tagsEntryLocalService.getEntryNames(
374                         MBMessage.class.getName(), messageId);
375 
376                     try {
377                         Document doc = Indexer.getAddMessageDocument(
378                             companyId, groupId, userName, categoryId, threadId,
379                             messageId, title, content, tagsEntries);
380 
381                         writer.addDocument(doc);
382                     }
383                     catch (Exception e1) {
384                         _log.error("Reindexing " + messageId, e1);
385                     }
386                 }
387             }
388         }
389         catch (SystemException se) {
390             throw se;
391         }
392         catch (Exception e2) {
393             throw new SystemException(e2);
394         }
395         finally {
396             try {
397                 if (writer != null) {
398                     LuceneUtil.write(companyId);
399                 }
400             }
401             catch (Exception e) {
402                 _log.error(e);
403             }
404         }
405     }
406 
407     public Hits search(
408             long companyId, long groupId, long[] categoryIds, long threadId,
409             String keywords)
410         throws SystemException {
411 
412         Searcher searcher = null;
413 
414         try {
415             HitsImpl hits = new HitsImpl();
416 
417             BooleanQuery contextQuery = new BooleanQuery();
418 
419             LuceneUtil.addRequiredTerm(
420                 contextQuery, LuceneFields.PORTLET_ID, Indexer.PORTLET_ID);
421 
422             if (groupId > 0) {
423                 LuceneUtil.addRequiredTerm(
424                     contextQuery, LuceneFields.GROUP_ID, groupId);
425             }
426 
427             if ((categoryIds != null) && (categoryIds.length > 0)) {
428                 BooleanQuery categoryIdsQuery = new BooleanQuery();
429 
430                 for (int i = 0; i < categoryIds.length; i++) {
431                     Term term = new Term(
432                         "categoryId", String.valueOf(categoryIds[i]));
433                     TermQuery termQuery = new TermQuery(term);
434 
435                     categoryIdsQuery.add(termQuery, BooleanClause.Occur.SHOULD);
436                 }
437 
438                 contextQuery.add(categoryIdsQuery, BooleanClause.Occur.MUST);
439             }
440 
441             if (threadId > 0) {
442                 LuceneUtil.addTerm(contextQuery, "threadId", threadId);
443             }
444 
445             BooleanQuery searchQuery = new BooleanQuery();
446 
447             if (Validator.isNotNull(keywords)) {
448                 LuceneUtil.addTerm(
449                     searchQuery, LuceneFields.USER_NAME, keywords);
450                 LuceneUtil.addTerm(searchQuery, LuceneFields.TITLE, keywords);
451                 LuceneUtil.addTerm(searchQuery, LuceneFields.CONTENT, keywords);
452                 LuceneUtil.addTerm(
453                     searchQuery, LuceneFields.TAG_ENTRY, keywords);
454             }
455 
456             BooleanQuery fullQuery = new BooleanQuery();
457 
458             fullQuery.add(contextQuery, BooleanClause.Occur.MUST);
459 
460             if (searchQuery.clauses().size() > 0) {
461                 fullQuery.add(searchQuery, BooleanClause.Occur.MUST);
462             }
463 
464             searcher = LuceneUtil.getSearcher(companyId);
465 
466             hits.recordHits(searcher.search(fullQuery), searcher);
467 
468             return hits;
469         }
470         catch (Exception e) {
471             return LuceneUtil.closeSearcher(searcher, keywords, e);
472         }
473     }
474 
475     public MBCategory updateCategory(
476             long categoryId, long parentCategoryId, String name,
477             String description, boolean mergeWithParentCategory)
478         throws PortalException, SystemException {
479 
480         // Category
481 
482         MBCategory category = mbCategoryPersistence.findByPrimaryKey(
483             categoryId);
484 
485         parentCategoryId = getParentCategoryId(category, parentCategoryId);
486 
487         validate(name);
488 
489         category.setModifiedDate(new Date());
490         category.setParentCategoryId(parentCategoryId);
491         category.setName(name);
492         category.setDescription(description);
493 
494         mbCategoryPersistence.update(category);
495 
496         // Merge categories
497 
498         if (mergeWithParentCategory &&
499             (categoryId != parentCategoryId) &&
500             (parentCategoryId != MBCategoryImpl.DEFAULT_PARENT_CATEGORY_ID)) {
501 
502             mergeCategories(category, parentCategoryId);
503         }
504 
505         return category;
506     }
507 
508     protected long getParentCategoryId(long groupId, long parentCategoryId)
509         throws SystemException {
510 
511         if (parentCategoryId != MBCategoryImpl.DEFAULT_PARENT_CATEGORY_ID) {
512             MBCategory parentCategory = mbCategoryPersistence.fetchByPrimaryKey(
513                 parentCategoryId);
514 
515             if ((parentCategory == null) ||
516                 (groupId != parentCategory.getGroupId())) {
517 
518                 parentCategoryId = MBCategoryImpl.DEFAULT_PARENT_CATEGORY_ID;
519             }
520         }
521 
522         return parentCategoryId;
523     }
524 
525     protected long getParentCategoryId(
526             MBCategory category, long parentCategoryId)
527         throws SystemException {
528 
529         if (parentCategoryId == MBCategoryImpl.DEFAULT_PARENT_CATEGORY_ID) {
530             return parentCategoryId;
531         }
532 
533         if (category.getCategoryId() == parentCategoryId) {
534             return category.getParentCategoryId();
535         }
536         else {
537             MBCategory parentCategory = mbCategoryPersistence.fetchByPrimaryKey(
538                 parentCategoryId);
539 
540             if ((parentCategory == null) ||
541                 (category.getGroupId() != parentCategory.getGroupId())) {
542 
543                 return category.getParentCategoryId();
544             }
545 
546             List subcategoryIds = new ArrayList();
547 
548             getSubcategoryIds(
549                 subcategoryIds, category.getGroupId(),
550                 category.getCategoryId());
551 
552             if (subcategoryIds.contains(new Long(parentCategoryId))) {
553                 return category.getParentCategoryId();
554             }
555 
556             return parentCategoryId;
557         }
558     }
559 
560     protected void mergeCategories(MBCategory fromCategory, long toCategoryId)
561         throws PortalException, SystemException {
562 
563         Iterator itr = mbCategoryPersistence.findByG_P(
564             fromCategory.getGroupId(), fromCategory.getCategoryId()).iterator();
565 
566         while (itr.hasNext()) {
567             MBCategory category = (MBCategory)itr.next();
568 
569             mergeCategories(category, toCategoryId);
570         }
571 
572         Iterator itr1 = mbThreadPersistence.findByCategoryId(
573             fromCategory.getCategoryId()).iterator();
574 
575         while (itr1.hasNext()) {
576 
577             // Thread
578 
579             MBThread thread = (MBThread)itr1.next();
580 
581             thread.setCategoryId(toCategoryId);
582 
583             mbThreadPersistence.update(thread);
584 
585             Iterator itr2 = mbMessagePersistence.findByThreadId(
586                 thread.getThreadId()).iterator();
587 
588             while (itr2.hasNext()) {
589 
590                 // Message
591 
592                 MBMessage message = (MBMessage)itr2.next();
593 
594                 message.setCategoryId(toCategoryId);
595 
596                 mbMessagePersistence.update(message);
597 
598                 // Lucene
599 
600                 try {
601                     if (!fromCategory.isDiscussion()) {
602                         String[] tagsEntries =
603                             tagsEntryLocalService.getEntryNames(
604                                 MBMessage.class.getName(),
605                                 message.getMessageId());
606 
607                         Indexer.updateMessage(
608                             message.getCompanyId(), fromCategory.getGroupId(),
609                             message.getUserName(), toCategoryId,
610                             message.getThreadId(), message.getMessageId(),
611                             message.getSubject(), message.getBody(),
612                             tagsEntries);
613                     }
614                 }
615                 catch (IOException ioe) {
616                     _log.error("Indexing " + message.getMessageId(), ioe);
617                 }
618             }
619         }
620 
621         mbCategoryPersistence.remove(fromCategory.getCategoryId());
622     }
623 
624     public void subscribeCategory(long userId, long categoryId)
625         throws PortalException, SystemException {
626 
627         subscriptionLocalService.addSubscription(
628             userId, MBCategory.class.getName(), categoryId);
629     }
630 
631     public void unsubscribeCategory(long userId, long categoryId)
632         throws PortalException, SystemException {
633 
634         subscriptionLocalService.deleteSubscription(
635             userId, MBCategory.class.getName(), categoryId);
636     }
637 
638     protected void validate(String name) throws PortalException {
639         if (Validator.isNull(name)) {
640             throw new CategoryNameException();
641         }
642     }
643 
644     private static Log _log =
645         LogFactory.getLog(MBCategoryLocalServiceImpl.class);
646 
647 }