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