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.blogs.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.ArrayUtil;
29  import com.liferay.portal.kernel.util.ContentTypes;
30  import com.liferay.portal.kernel.util.GetterUtil;
31  import com.liferay.portal.kernel.util.OrderByComparator;
32  import com.liferay.portal.kernel.util.StringMaker;
33  import com.liferay.portal.kernel.util.Validator;
34  import com.liferay.portal.lucene.LuceneFields;
35  import com.liferay.portal.lucene.LuceneUtil;
36  import com.liferay.portal.model.Group;
37  import com.liferay.portal.model.User;
38  import com.liferay.portal.model.impl.ResourceImpl;
39  import com.liferay.portal.theme.ThemeDisplay;
40  import com.liferay.portal.util.PortalUtil;
41  import com.liferay.portlet.blogs.EntryContentException;
42  import com.liferay.portlet.blogs.EntryDisplayDateException;
43  import com.liferay.portlet.blogs.EntryTitleException;
44  import com.liferay.portlet.blogs.model.BlogsEntry;
45  import com.liferay.portlet.blogs.model.BlogsStatsUser;
46  import com.liferay.portlet.blogs.service.base.BlogsEntryLocalServiceBaseImpl;
47  import com.liferay.portlet.blogs.util.Indexer;
48  import com.liferay.util.Http;
49  import com.liferay.util.HttpUtil;
50  import com.liferay.util.Normalizer;
51  import com.liferay.util.lucene.HitsImpl;
52  
53  import java.io.IOException;
54  
55  import java.util.Date;
56  import java.util.Iterator;
57  import java.util.List;
58  
59  import org.apache.commons.logging.Log;
60  import org.apache.commons.logging.LogFactory;
61  import org.apache.lucene.document.Document;
62  import org.apache.lucene.index.IndexWriter;
63  import org.apache.lucene.search.BooleanClause;
64  import org.apache.lucene.search.BooleanQuery;
65  import org.apache.lucene.search.Searcher;
66  
67  /**
68   * <a href="BlogsEntryLocalServiceImpl.java.html"><b><i>View Source</i></b>
69   * </a>
70   *
71   * @author Brian Wing Shun Chan
72   * @author Wilson S. Man
73   *
74   */
75  public class BlogsEntryLocalServiceImpl extends BlogsEntryLocalServiceBaseImpl {
76  
77      public BlogsEntry addEntry(
78              long userId, long plid, String title, String content,
79              int displayDateMonth, int displayDateDay, int displayDateYear,
80              int displayDateHour, int displayDateMinute, String[] tagsEntries,
81              boolean addCommunityPermissions, boolean addGuestPermissions,
82              ThemeDisplay themeDisplay)
83          throws PortalException, SystemException {
84  
85          return addEntry(
86              null, userId, plid, title, content, displayDateMonth,
87              displayDateDay, displayDateYear, displayDateHour, displayDateMinute,
88              tagsEntries, Boolean.valueOf(addCommunityPermissions),
89              Boolean.valueOf(addGuestPermissions), null, null, themeDisplay);
90      }
91  
92      public BlogsEntry addEntry(
93              String uuid, long userId, long plid, String title, String content,
94              int displayDateMonth, int displayDateDay, int displayDateYear,
95              int displayDateHour, int displayDateMinute, String[] tagsEntries,
96              boolean addCommunityPermissions, boolean addGuestPermissions,
97              ThemeDisplay themeDisplay)
98          throws PortalException, SystemException {
99  
100         return addEntry(
101             uuid, userId, plid, title, content, displayDateMonth,
102             displayDateDay, displayDateYear, displayDateHour, displayDateMinute,
103             tagsEntries, Boolean.valueOf(addCommunityPermissions),
104             Boolean.valueOf(addGuestPermissions), null, null, themeDisplay);
105     }
106 
107     public BlogsEntry addEntry(
108             long userId, long plid, String title, String content,
109             int displayDateMonth, int displayDateDay, int displayDateYear,
110             int displayDateHour, int displayDateMinute, String[] tagsEntries,
111             String[] communityPermissions, String[] guestPermissions,
112             ThemeDisplay themeDisplay)
113         throws PortalException, SystemException {
114 
115         return addEntry(
116             null, userId, plid, title, content, displayDateMonth,
117             displayDateDay, displayDateYear, displayDateHour, displayDateMinute,
118             tagsEntries, null, null, communityPermissions, guestPermissions,
119             themeDisplay);
120     }
121 
122     public BlogsEntry addEntry(
123             String uuid, long userId, long plid, String title, String content,
124             int displayDateMonth, int displayDateDay, int displayDateYear,
125             int displayDateHour, int displayDateMinute, String[] tagsEntries,
126             Boolean addCommunityPermissions, Boolean addGuestPermissions,
127             String[] communityPermissions, String[] guestPermissions,
128             ThemeDisplay themeDisplay)
129         throws PortalException, SystemException {
130 
131         // Entry
132 
133         User user = userPersistence.findByPrimaryKey(userId);
134         long groupId = PortalUtil.getPortletGroupId(plid);
135         Date now = new Date();
136 
137         Date displayDate = PortalUtil.getDate(
138             displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
139             displayDateMinute, user.getTimeZone(),
140             new EntryDisplayDateException());
141 
142         validate(title, content);
143 
144         long entryId = counterLocalService.increment();
145 
146         BlogsEntry entry = blogsEntryPersistence.create(entryId);
147 
148         entry.setUuid(uuid);
149         entry.setGroupId(groupId);
150         entry.setCompanyId(user.getCompanyId());
151         entry.setUserId(user.getUserId());
152         entry.setUserName(user.getFullName());
153         entry.setCreateDate(now);
154         entry.setModifiedDate(now);
155         entry.setTitle(title);
156         entry.setUrlTitle(getUniqueUrlTitle(entryId, groupId, title));
157         entry.setContent(content);
158         entry.setDisplayDate(displayDate);
159 
160         blogsEntryPersistence.update(entry);
161 
162         // Resources
163 
164         if ((addCommunityPermissions != null) &&
165             (addGuestPermissions != null)) {
166 
167             addEntryResources(
168                 entry, addCommunityPermissions.booleanValue(),
169                 addGuestPermissions.booleanValue());
170         }
171         else {
172             addEntryResources(entry, communityPermissions, guestPermissions);
173         }
174 
175         // Statistics
176 
177         blogsStatsUserLocalService.updateStatsUser(
178             entry.getGroupId(), userId, now);
179 
180         // Tags
181 
182         updateTagsAsset(userId, entry, tagsEntries);
183 
184         // Lucene
185 
186         try {
187             Indexer.addEntry(
188                 entry.getCompanyId(), entry.getGroupId(), userId, entryId,
189                 title, content, tagsEntries);
190         }
191         catch (IOException ioe) {
192             _log.error("Indexing " + entryId, ioe);
193         }
194 
195         // Google
196 
197         pingGoogle(entry, themeDisplay);
198 
199         return entry;
200     }
201 
202     public void addEntryResources(
203             long entryId, boolean addCommunityPermissions,
204             boolean addGuestPermissions)
205         throws PortalException, SystemException {
206 
207         BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
208 
209         addEntryResources(entry, addCommunityPermissions, addGuestPermissions);
210     }
211 
212     public void addEntryResources(
213             BlogsEntry entry, boolean addCommunityPermissions,
214             boolean addGuestPermissions)
215         throws PortalException, SystemException {
216 
217         resourceLocalService.addResources(
218             entry.getCompanyId(), entry.getGroupId(), entry.getUserId(),
219             BlogsEntry.class.getName(), entry.getEntryId(), false,
220             addCommunityPermissions, addGuestPermissions);
221     }
222 
223     public void addEntryResources(
224             long entryId, String[] communityPermissions,
225             String[] guestPermissions)
226         throws PortalException, SystemException {
227 
228         BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
229 
230         addEntryResources(entry, communityPermissions, guestPermissions);
231     }
232 
233     public void addEntryResources(
234             BlogsEntry entry, String[] communityPermissions,
235             String[] guestPermissions)
236         throws PortalException, SystemException {
237 
238         resourceLocalService.addModelResources(
239             entry.getCompanyId(), entry.getGroupId(), entry.getUserId(),
240             BlogsEntry.class.getName(), entry.getEntryId(),
241             communityPermissions, guestPermissions);
242     }
243 
244     public void deleteEntries(long groupId)
245         throws PortalException, SystemException {
246 
247         Iterator itr = blogsEntryPersistence.findByGroupId(groupId).iterator();
248 
249         while (itr.hasNext()) {
250             BlogsEntry entry = (BlogsEntry)itr.next();
251 
252             deleteEntry(entry);
253         }
254     }
255 
256     public void deleteEntry(long entryId)
257         throws PortalException, SystemException {
258 
259         BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
260 
261         deleteEntry(entry);
262     }
263 
264     public void deleteEntry(BlogsEntry entry)
265         throws PortalException, SystemException {
266 
267         // Lucene
268 
269         try {
270             Indexer.deleteEntry(entry.getCompanyId(), entry.getEntryId());
271         }
272         catch (IOException ioe) {
273             _log.error("Deleting index " + entry.getEntryId(), ioe);
274         }
275 
276         // Tags
277 
278         tagsAssetLocalService.deleteAsset(
279             BlogsEntry.class.getName(), entry.getEntryId());
280 
281         // Ratings
282 
283         ratingsStatsLocalService.deleteStats(
284             BlogsEntry.class.getName(), entry.getEntryId());
285 
286         // Message boards
287 
288         mbMessageLocalService.deleteDiscussionMessages(
289             BlogsEntry.class.getName(), entry.getEntryId());
290 
291         // Resources
292 
293         resourceLocalService.deleteResource(
294             entry.getCompanyId(), BlogsEntry.class.getName(),
295             ResourceImpl.SCOPE_INDIVIDUAL, entry.getEntryId());
296 
297         // Entry
298 
299         blogsEntryPersistence.remove(entry.getEntryId());
300     }
301 
302     public List getCompanyEntries(long companyId, int begin, int end)
303         throws SystemException {
304 
305         return blogsEntryPersistence.findByCompanyId(companyId, begin, end);
306     }
307 
308     public List getCompanyEntries(
309             long companyId, int begin, int end, OrderByComparator obc)
310         throws SystemException {
311 
312         return blogsEntryPersistence.findByCompanyId(
313             companyId, begin, end, obc);
314     }
315 
316     public int getCompanyEntriesCount(long companyId) throws SystemException {
317         return blogsEntryPersistence.countByCompanyId(companyId);
318     }
319 
320     public BlogsEntry getEntry(long entryId)
321         throws PortalException, SystemException {
322 
323         return blogsEntryPersistence.findByPrimaryKey(entryId);
324     }
325 
326     public BlogsEntry getEntry(long groupId, String urlTitle)
327         throws PortalException, SystemException {
328 
329         return blogsEntryPersistence.findByG_UT(groupId, urlTitle);
330     }
331 
332     public List getGroupEntries(long groupId, int begin, int end)
333         throws SystemException {
334 
335         return blogsEntryPersistence.findByGroupId(groupId, begin, end);
336     }
337 
338     public List getGroupEntries(
339             long groupId, int begin, int end, OrderByComparator obc)
340         throws SystemException {
341 
342         return blogsEntryPersistence.findByGroupId(groupId, begin, end, obc);
343     }
344 
345     public int getGroupEntriesCount(long groupId) throws SystemException {
346         return blogsEntryPersistence.countByGroupId(groupId);
347     }
348 
349     public List getGroupUserEntries(
350             long groupId, long userId, int begin, int end)
351         throws SystemException {
352 
353         return blogsEntryPersistence.findByG_U(groupId, userId, begin, end);
354     }
355 
356     public int getGroupUserEntriesCount(long groupId, long userId)
357         throws SystemException {
358 
359         return blogsEntryPersistence.countByG_U(groupId, userId);
360     }
361 
362     public List getNoAssetEntries() throws SystemException {
363         return blogsEntryFinder.findByNoAssets();
364     }
365 
366     public List getOrganizationEntries(long organizationId, int begin, int end)
367         throws SystemException {
368 
369         return blogsEntryFinder.findByOrganizationId(
370             organizationId, begin, end);
371     }
372 
373     public int getOrganizationEntriesCount(long organizationId)
374         throws SystemException {
375 
376         return blogsEntryFinder.countByOrganizationId(
377             organizationId);
378     }
379 
380     public String getUrlTitle(long entryId, String title) {
381         String urlTitle = String.valueOf(entryId);
382 
383         title = title.trim().toLowerCase();
384 
385         if (Validator.isNull(title) || Validator.isNumber(title) ||
386             title.equals("rss")) {
387 
388             return urlTitle;
389         }
390 
391         title = Normalizer.normalizeToAscii(title);
392 
393         char[] urlTitleCharArray = title.toCharArray();
394 
395         for (int i = 0; i < urlTitleCharArray.length; i++) {
396             char oldChar = urlTitleCharArray[i];
397 
398             char newChar = oldChar;
399 
400             if ((oldChar == '_') || (Validator.isChar(oldChar)) ||
401                 (Validator.isDigit(oldChar))) {
402 
403             }
404             else if (ArrayUtil.contains(_URL_TITLE_REPLACE_CHARS, oldChar)) {
405                 newChar = '_';
406             }
407             else {
408                 return urlTitle;
409             }
410 
411             if (oldChar != newChar) {
412                 urlTitleCharArray[i] = newChar;
413             }
414         }
415 
416         urlTitle = new String(urlTitleCharArray);
417 
418         return urlTitle;
419     }
420 
421     public void reIndex(String[] ids) throws SystemException {
422         if (LuceneUtil.INDEX_READ_ONLY) {
423             return;
424         }
425 
426         long companyId = GetterUtil.getLong(ids[0]);
427 
428         IndexWriter writer = null;
429 
430         try {
431             writer = LuceneUtil.getWriter(companyId);
432 
433             Iterator itr = blogsEntryPersistence.findByCompanyId(
434                 companyId).iterator();
435 
436             while (itr.hasNext()) {
437                 BlogsEntry entry = (BlogsEntry)itr.next();
438 
439                 long groupId = entry.getGroupId();
440                 long userId = entry.getUserId();
441                 long entryId = entry.getEntryId();
442                 String title = entry.getTitle();
443                 String content = entry.getContent();
444 
445                 String[] tagsEntries = tagsEntryLocalService.getEntryNames(
446                     BlogsEntry.class.getName(), entryId);
447 
448                 try {
449                     Document doc = Indexer.getAddEntryDocument(
450                         companyId, groupId, userId, entryId, title, content,
451                         tagsEntries);
452 
453                     writer.addDocument(doc);
454                 }
455                 catch (Exception e1) {
456                     _log.error("Reindexing " + entryId, e1);
457                 }
458             }
459         }
460         catch (SystemException se) {
461             throw se;
462         }
463         catch (Exception e2) {
464             throw new SystemException(e2);
465         }
466         finally {
467             try {
468                 if (writer != null) {
469                     LuceneUtil.write(companyId);
470                 }
471             }
472             catch (Exception e) {
473                 _log.error(e);
474             }
475         }
476     }
477 
478     public Hits search(
479             long companyId, long groupId, long userId, String keywords)
480         throws SystemException {
481 
482         Searcher searcher = null;
483 
484         try {
485             HitsImpl hits = new HitsImpl();
486 
487             BooleanQuery contextQuery = new BooleanQuery();
488 
489             LuceneUtil.addRequiredTerm(
490                 contextQuery, LuceneFields.PORTLET_ID, Indexer.PORTLET_ID);
491 
492             if (groupId > 0) {
493                 LuceneUtil.addRequiredTerm(
494                     contextQuery, LuceneFields.GROUP_ID, groupId);
495             }
496 
497             if (userId > 0) {
498                 LuceneUtil.addRequiredTerm(
499                     contextQuery, LuceneFields.USER_ID, userId);
500             }
501 
502             BooleanQuery searchQuery = new BooleanQuery();
503 
504             if (Validator.isNotNull(keywords)) {
505                 LuceneUtil.addTerm(searchQuery, LuceneFields.TITLE, keywords);
506                 LuceneUtil.addTerm(searchQuery, LuceneFields.CONTENT, keywords);
507                 LuceneUtil.addTerm(
508                     searchQuery, LuceneFields.TAG_ENTRY, keywords);
509             }
510 
511             BooleanQuery fullQuery = new BooleanQuery();
512 
513             fullQuery.add(contextQuery, BooleanClause.Occur.MUST);
514 
515             if (searchQuery.clauses().size() > 0) {
516                 fullQuery.add(searchQuery, BooleanClause.Occur.MUST);
517             }
518 
519             searcher = LuceneUtil.getSearcher(companyId);
520 
521             hits.recordHits(searcher.search(fullQuery), searcher);
522 
523             return hits;
524         }
525         catch (Exception e) {
526             return LuceneUtil.closeSearcher(searcher, keywords, e);
527         }
528     }
529 
530     public BlogsEntry updateEntry(
531             long userId, long entryId, String title, String content,
532             int displayDateMonth, int displayDateDay, int displayDateYear,
533             int displayDateHour, int displayDateMinute, String[] tagsEntries,
534             ThemeDisplay themeDisplay)
535         throws PortalException, SystemException {
536 
537         // Entry
538 
539         User user = userPersistence.findByPrimaryKey(userId);
540         Date now = new Date();
541 
542         Date displayDate = PortalUtil.getDate(
543             displayDateMonth, displayDateDay, displayDateYear, displayDateHour,
544             displayDateMinute, user.getTimeZone(),
545             new EntryDisplayDateException());
546 
547         validate(title, content);
548 
549         BlogsEntry entry = blogsEntryPersistence.findByPrimaryKey(entryId);
550 
551         entry.setModifiedDate(now);
552         entry.setTitle(title);
553         entry.setUrlTitle(
554             getUniqueUrlTitle(entryId, entry.getGroupId(), title));
555         entry.setContent(content);
556         entry.setDisplayDate(displayDate);
557 
558         blogsEntryPersistence.update(entry);
559 
560         // Statistics
561 
562         BlogsStatsUser statsUser = blogsStatsUserPersistence.fetchByG_U(
563             entry.getGroupId(), entry.getUserId());
564 
565         if (statsUser != null) {
566             statsUser.setLastPostDate(now);
567 
568             blogsStatsUserPersistence.update(statsUser);
569         }
570 
571         // Tags
572 
573         updateTagsAsset(userId, entry, tagsEntries);
574 
575         // Lucene
576 
577         try {
578             Indexer.updateEntry(
579                 entry.getCompanyId(), entry.getGroupId(), userId, entryId,
580                 title, content, tagsEntries);
581         }
582         catch (IOException ioe) {
583             _log.error("Indexing " + entryId, ioe);
584         }
585 
586         // Google
587 
588         pingGoogle(entry, themeDisplay);
589 
590         return entry;
591     }
592 
593     public void updateTagsAsset(
594             long userId, BlogsEntry entry, String[] tagsEntries)
595         throws PortalException, SystemException {
596 
597         tagsAssetLocalService.updateAsset(
598             userId, entry.getGroupId(), BlogsEntry.class.getName(),
599             entry.getEntryId(), tagsEntries, null, null, null, null,
600             ContentTypes.TEXT_HTML, entry.getTitle(), null, null, null, 0, 0,
601             null, false);
602     }
603 
604     protected String getUniqueUrlTitle(
605             long entryId, long groupId, String title)
606         throws SystemException {
607 
608         String urlTitle = getUrlTitle(entryId, title);
609 
610         String newUrlTitle = new String(urlTitle);
611 
612         for (int i = 1;; i++) {
613             BlogsEntry entry = blogsEntryPersistence.fetchByG_UT(
614                 groupId, newUrlTitle);
615 
616             if ((entry == null) || (entry.getEntryId() == entryId)) {
617                 break;
618             }
619             else {
620                 newUrlTitle = urlTitle + "_" + i;
621             }
622         }
623 
624         return newUrlTitle;
625     }
626 
627     protected void pingGoogle(BlogsEntry entry, ThemeDisplay themeDisplay)
628         throws PortalException, SystemException {
629 
630         if (themeDisplay == null) {
631             return;
632         }
633 
634         Group group = groupPersistence.findByPrimaryKey(entry.getGroupId());
635 
636         String portalURL = PortalUtil.getPortalURL(themeDisplay);
637 
638         if ((portalURL.indexOf("://localhost") != -1) ||
639             (portalURL.indexOf("://127.0.0.1") != -1)) {
640 
641             return;
642         }
643 
644         String layoutURL = PortalUtil.getLayoutURL(themeDisplay);
645 
646         StringMaker sm = new StringMaker();
647 
648         String name = group.getDescriptiveName();
649         //String url = portalURL + layoutURL + "/blogs/" + entry.getUrlTitle();
650         String url = portalURL + layoutURL + "/blogs";
651         String changesURL = portalURL + layoutURL + "/blogs/rss";
652 
653         sm.append("http://blogsearch.google.com/ping?name=");
654         sm.append(HttpUtil.encodeURL(name));
655         sm.append("&url=");
656         sm.append(HttpUtil.encodeURL(url));
657         sm.append("&changesURL=");
658         sm.append(HttpUtil.encodeURL(changesURL));
659 
660         String location = sm.toString();
661 
662         if (_log.isInfoEnabled()) {
663             _log.info("Pinging Google at " + location);
664         }
665 
666         try {
667             String response = Http.URLtoString(sm.toString());
668 
669             if (_log.isInfoEnabled()) {
670                 _log.info("Google ping response: " + response);
671             }
672         }
673         catch (IOException ioe) {
674             _log.error("Unable to ping Google at " + location, ioe);
675         }
676     }
677 
678     protected void validate(String title, String content)
679         throws PortalException {
680 
681         if (Validator.isNull(title)) {
682             throw new EntryTitleException();
683         }
684         else if (Validator.isNull(content)) {
685             throw new EntryContentException();
686         }
687     }
688 
689     private static final char[] _URL_TITLE_REPLACE_CHARS = new char[] {
690         ' ',  '.',  '-',  ',',  '/',  '\\',  '\'',  '\"'
691     };
692 
693     private static Log _log =
694         LogFactory.getLog(BlogsEntryLocalServiceImpl.class);
695 
696 }