1   /**
2    * Copyright (c) 2000-2009 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.wiki.service.impl;
24  
25  import com.liferay.documentlibrary.DuplicateDirectoryException;
26  import com.liferay.documentlibrary.DuplicateFileException;
27  import com.liferay.documentlibrary.NoSuchDirectoryException;
28  import com.liferay.documentlibrary.NoSuchFileException;
29  import com.liferay.portal.PortalException;
30  import com.liferay.portal.SystemException;
31  import com.liferay.portal.kernel.language.LanguageUtil;
32  import com.liferay.portal.kernel.log.Log;
33  import com.liferay.portal.kernel.log.LogFactoryUtil;
34  import com.liferay.portal.kernel.messaging.DestinationNames;
35  import com.liferay.portal.kernel.messaging.Message;
36  import com.liferay.portal.kernel.messaging.MessageBusUtil;
37  import com.liferay.portal.kernel.search.SearchEngineUtil;
38  import com.liferay.portal.kernel.search.SearchException;
39  import com.liferay.portal.kernel.util.CalendarFactoryUtil;
40  import com.liferay.portal.kernel.util.ContentTypes;
41  import com.liferay.portal.kernel.util.HttpUtil;
42  import com.liferay.portal.kernel.util.ListUtil;
43  import com.liferay.portal.kernel.util.MathUtil;
44  import com.liferay.portal.kernel.util.NotificationThreadLocal;
45  import com.liferay.portal.kernel.util.ObjectValuePair;
46  import com.liferay.portal.kernel.util.OrderByComparator;
47  import com.liferay.portal.kernel.util.StringPool;
48  import com.liferay.portal.kernel.util.StringUtil;
49  import com.liferay.portal.kernel.util.Validator;
50  import com.liferay.portal.model.Company;
51  import com.liferay.portal.model.CompanyConstants;
52  import com.liferay.portal.model.Group;
53  import com.liferay.portal.model.GroupConstants;
54  import com.liferay.portal.model.ResourceConstants;
55  import com.liferay.portal.model.User;
56  import com.liferay.portal.service.ServiceContext;
57  import com.liferay.portal.service.ServiceContextUtil;
58  import com.liferay.portal.util.Portal;
59  import com.liferay.portal.util.PortalUtil;
60  import com.liferay.portal.util.PortletKeys;
61  import com.liferay.portal.util.PropsValues;
62  import com.liferay.portlet.expando.model.ExpandoBridge;
63  import com.liferay.portlet.tags.model.TagsEntryConstants;
64  import com.liferay.portlet.wiki.DuplicatePageException;
65  import com.liferay.portlet.wiki.NoSuchPageException;
66  import com.liferay.portlet.wiki.NoSuchPageResourceException;
67  import com.liferay.portlet.wiki.PageContentException;
68  import com.liferay.portlet.wiki.PageTitleException;
69  import com.liferay.portlet.wiki.PageVersionException;
70  import com.liferay.portlet.wiki.model.WikiNode;
71  import com.liferay.portlet.wiki.model.WikiPage;
72  import com.liferay.portlet.wiki.model.WikiPageDisplay;
73  import com.liferay.portlet.wiki.model.WikiPageResource;
74  import com.liferay.portlet.wiki.model.impl.WikiPageDisplayImpl;
75  import com.liferay.portlet.wiki.model.impl.WikiPageImpl;
76  import com.liferay.portlet.wiki.service.base.WikiPageLocalServiceBaseImpl;
77  import com.liferay.portlet.wiki.social.WikiActivityKeys;
78  import com.liferay.portlet.wiki.util.Indexer;
79  import com.liferay.portlet.wiki.util.WikiCacheThreadLocal;
80  import com.liferay.portlet.wiki.util.WikiCacheUtil;
81  import com.liferay.portlet.wiki.util.WikiUtil;
82  import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
83  import com.liferay.util.UniqueList;
84  
85  import java.util.ArrayList;
86  import java.util.Calendar;
87  import java.util.Date;
88  import java.util.HashSet;
89  import java.util.Iterator;
90  import java.util.LinkedHashMap;
91  import java.util.List;
92  import java.util.Map;
93  import java.util.Set;
94  import java.util.regex.Matcher;
95  import java.util.regex.Pattern;
96  
97  import javax.portlet.PortletPreferences;
98  import javax.portlet.PortletURL;
99  
100 /**
101  * <a href="WikiPageLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
102  *
103  * @author Brian Wing Shun Chan
104  * @author Jorge Ferrer
105  * @author Raymond Augé
106  * @author Bruno Farache
107  *
108  */
109 public class WikiPageLocalServiceImpl extends WikiPageLocalServiceBaseImpl {
110 
111     public WikiPage addPage(
112             long userId, long nodeId, String title, String content,
113             String summary, boolean minorEdit, ServiceContext serviceContext)
114         throws PortalException, SystemException {
115 
116         String uuid = null;
117         double version = WikiPageImpl.DEFAULT_VERSION;
118         String format = WikiPageImpl.DEFAULT_FORMAT;
119         boolean head = true;
120         String parentTitle = null;
121         String redirectTitle = null;
122 
123         return addPage(
124             uuid, userId, nodeId, title, version, content, summary, minorEdit,
125             format, head, parentTitle, redirectTitle, serviceContext);
126     }
127 
128     public WikiPage addPage(
129             String uuid, long userId, long nodeId, String title, double version,
130             String content, String summary, boolean minorEdit, String format,
131             boolean head, String parentTitle, String redirectTitle,
132             ServiceContext serviceContext)
133         throws PortalException, SystemException {
134 
135         // Page
136 
137         User user = userPersistence.findByPrimaryKey(userId);
138         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
139 
140         Date now = new Date();
141 
142         validate(title, nodeId, content, format);
143 
144         long pageId = counterLocalService.increment();
145 
146         long resourcePrimKey =
147             wikiPageResourceLocalService.getPageResourcePrimKey(nodeId, title);
148 
149         WikiPage page = wikiPagePersistence.create(pageId);
150 
151         page.setUuid(uuid);
152         page.setResourcePrimKey(resourcePrimKey);
153         page.setGroupId(node.getGroupId());
154         page.setCompanyId(user.getCompanyId());
155         page.setUserId(user.getUserId());
156         page.setUserName(user.getFullName());
157         page.setCreateDate(now);
158         page.setModifiedDate(now);
159         page.setNodeId(nodeId);
160         page.setTitle(title);
161         page.setVersion(version);
162         page.setMinorEdit(minorEdit);
163         page.setContent(content);
164         page.setSummary(summary);
165         page.setFormat(format);
166         page.setHead(head);
167         page.setParentTitle(parentTitle);
168         page.setRedirectTitle(redirectTitle);
169 
170         wikiPagePersistence.update(page, false);
171 
172         // Resources
173 
174         addPageResources(page, true, true);
175 
176         // Node
177 
178         node.setLastPostDate(now);
179 
180         wikiNodePersistence.update(node, false);
181 
182         // Message boards
183 
184         if (PropsValues.WIKI_PAGE_COMMENTS_ENABLED) {
185             mbMessageLocalService.addDiscussionMessage(
186                 userId, page.getUserName(), WikiPage.class.getName(),
187                 resourcePrimKey);
188         }
189 
190         // Social
191 
192         socialActivityLocalService.addActivity(
193             userId, page.getGroupId(), WikiPage.class.getName(),
194             resourcePrimKey, WikiActivityKeys.ADD_PAGE, StringPool.BLANK, 0);
195 
196         // Subscriptions
197 
198         if (!minorEdit && NotificationThreadLocal.isNotificationEnabled()) {
199             notifySubscribers(node, page, serviceContext, false);
200         }
201 
202         // Tags
203 
204         updateTagsAsset(
205             userId, page, serviceContext.getTagsCategories(),
206             serviceContext.getTagsEntries());
207 
208         // Indexer
209 
210         reIndex(page);
211 
212         // Cache
213 
214         clearPageCache(page);
215         clearReferralsCache(page);
216 
217         return page;
218     }
219 
220     public void addPageAttachments(
221             long nodeId, String title,
222             List<ObjectValuePair<String, byte[]>> files)
223         throws PortalException, SystemException {
224 
225         if (files.size() == 0) {
226             return;
227         }
228 
229         WikiPage page = getPage(nodeId, title);
230 
231         long companyId = page.getCompanyId();
232         String portletId = CompanyConstants.SYSTEM_STRING;
233         long groupId = GroupConstants.DEFAULT_PARENT_GROUP_ID;
234         long repositoryId = CompanyConstants.SYSTEM;
235         String dirName = page.getAttachmentsDir();
236 
237         try {
238             dlService.addDirectory(companyId, repositoryId, dirName);
239         }
240         catch (DuplicateDirectoryException dde) {
241         }
242 
243         for (int i = 0; i < files.size(); i++) {
244             ObjectValuePair<String, byte[]> ovp = files.get(i);
245 
246             String fileName = ovp.getKey();
247             byte[] bytes = ovp.getValue();
248 
249             if (Validator.isNull(fileName)) {
250                 continue;
251             }
252 
253             try {
254                 dlService.addFile(
255                     companyId, portletId, groupId, repositoryId,
256                     dirName + "/" + fileName, 0, StringPool.BLANK,
257                     page.getModifiedDate(), new String[0], new String[0],
258                     bytes);
259             }
260             catch (DuplicateFileException dfe) {
261             }
262         }
263     }
264 
265     public void addPageResources(
266             long nodeId, String title, boolean addCommunityPermissions,
267             boolean addGuestPermissions)
268         throws PortalException, SystemException {
269 
270         WikiPage page = getPage(nodeId, title);
271 
272         addPageResources(page, addCommunityPermissions, addGuestPermissions);
273     }
274 
275     public void addPageResources(
276             WikiPage page, boolean addCommunityPermissions,
277             boolean addGuestPermissions)
278         throws PortalException, SystemException {
279 
280         resourceLocalService.addResources(
281             page.getCompanyId(), page.getGroupId(), page.getUserId(),
282             WikiPage.class.getName(), page.getResourcePrimKey(), false,
283             addCommunityPermissions, addGuestPermissions);
284     }
285 
286     public void addPageResources(
287             long nodeId, String title, String[] communityPermissions,
288             String[] guestPermissions)
289         throws PortalException, SystemException {
290 
291         WikiPage page = getPage(nodeId, title);
292 
293         addPageResources(page, communityPermissions, guestPermissions);
294     }
295 
296     public void addPageResources(
297             WikiPage page, String[] communityPermissions,
298             String[] guestPermissions)
299         throws PortalException, SystemException {
300 
301         resourceLocalService.addModelResources(
302             page.getCompanyId(), page.getGroupId(), page.getUserId(),
303             WikiPage.class.getName(), page.getResourcePrimKey(),
304             communityPermissions, guestPermissions);
305     }
306 
307     public void changeParent(
308             long userId, long nodeId, String title, String newParentTitle,
309             ServiceContext serviceContext)
310         throws PortalException, SystemException {
311 
312         WikiPage page = getPage(nodeId, title);
313 
314         String originalParentTitle = page.getParentTitle();
315 
316         double version = page.getVersion();
317         String content = page.getContent();
318         String summary = LanguageUtil.format(
319             page.getCompanyId(), ServiceContextUtil.getLocale(serviceContext),
320             "changed-parent-from-x", originalParentTitle);
321         boolean minorEdit = false;
322         String format = page.getFormat();
323         String redirectTitle = page.getRedirectTitle();
324 
325         String[] tagsCategories = tagsEntryLocalService.getEntryNames(
326             WikiPage.class.getName(), page.getResourcePrimKey(),
327             TagsEntryConstants.FOLKSONOMY_CATEGORY);
328         String[] tagsEntries = tagsEntryLocalService.getEntryNames(
329             WikiPage.class.getName(), page.getResourcePrimKey(),
330             TagsEntryConstants.FOLKSONOMY_TAG);
331 
332         serviceContext.setTagsCategories(tagsCategories);
333         serviceContext.setTagsEntries(tagsEntries);
334 
335         updatePage(
336             userId, nodeId, title, version, content, summary, minorEdit,
337             format, newParentTitle, redirectTitle, serviceContext);
338 
339         List<WikiPage> oldPages = wikiPagePersistence.findByN_T_H(
340             nodeId, title, false);
341 
342         for (WikiPage oldPage : oldPages) {
343             oldPage.setParentTitle(originalParentTitle);
344 
345             wikiPagePersistence.update(oldPage, false);
346         }
347     }
348 
349     public void deletePage(long nodeId, String title)
350         throws PortalException, SystemException {
351 
352         List<WikiPage> pages = wikiPagePersistence.findByN_T_H(
353             nodeId, title, true, 0, 1);
354 
355         if (pages.size() > 0) {
356             WikiPage page = pages.iterator().next();
357 
358             deletePage(page);
359         }
360     }
361 
362     public void deletePage(WikiPage page)
363         throws PortalException, SystemException {
364 
365         // Children
366 
367         List<WikiPage> children = wikiPagePersistence.findByN_P(
368             page.getNodeId(), page.getTitle());
369 
370         for (WikiPage curPage : children) {
371             deletePage(curPage);
372         }
373 
374         // Indexer
375 
376         try {
377             Indexer.deletePage(
378                 page.getCompanyId(), page.getNodeId(), page.getTitle());
379         }
380         catch (SearchException se) {
381             _log.error("Deleting index " + page.getPrimaryKey(), se);
382         }
383 
384         // Attachments
385 
386         long companyId = page.getCompanyId();
387         String portletId = CompanyConstants.SYSTEM_STRING;
388         long repositoryId = CompanyConstants.SYSTEM;
389         String dirName = page.getAttachmentsDir();
390 
391         try {
392             dlService.deleteDirectory(
393                 companyId, portletId, repositoryId, dirName);
394         }
395         catch (NoSuchDirectoryException nsde) {
396         }
397 
398         // Tags
399 
400         tagsAssetLocalService.deleteAsset(
401             WikiPage.class.getName(), page.getResourcePrimKey());
402 
403         // Subscriptions
404 
405         subscriptionLocalService.deleteSubscriptions(
406             page.getCompanyId(), WikiPage.class.getName(), page.getPageId());
407 
408         // Social
409 
410         socialActivityLocalService.deleteActivities(
411             WikiPage.class.getName(), page.getResourcePrimKey());
412 
413         // Message boards
414 
415         mbMessageLocalService.deleteDiscussionMessages(
416             WikiPage.class.getName(), page.getResourcePrimKey());
417 
418         // Resources
419 
420         resourceLocalService.deleteResource(
421             page.getCompanyId(), WikiPage.class.getName(),
422             ResourceConstants.SCOPE_INDIVIDUAL, page.getResourcePrimKey());
423 
424         // Resource
425 
426         try {
427             wikiPageResourceLocalService.deletePageResource(
428                 page.getNodeId(), page.getTitle());
429         }
430         catch (NoSuchPageResourceException nspre) {
431         }
432 
433         // All versions
434 
435         wikiPagePersistence.removeByN_T(page.getNodeId(), page.getTitle());
436 
437         // All referrals
438 
439         wikiPagePersistence.removeByN_R(page.getNodeId(), page.getTitle());
440 
441         // Cache
442 
443         clearPageCache(page);
444         clearReferralsCache(page);
445     }
446 
447     public void deletePageAttachment(long nodeId, String title, String fileName)
448         throws PortalException, SystemException {
449 
450         if (Validator.isNull(fileName)) {
451             return;
452         }
453 
454         WikiPage page = getPage(nodeId, title);
455 
456         long companyId = page.getCompanyId();
457         String portletId = CompanyConstants.SYSTEM_STRING;
458         long repositoryId = CompanyConstants.SYSTEM;
459 
460         try {
461             dlService.deleteFile(companyId, portletId, repositoryId, fileName);
462         }
463         catch (NoSuchFileException nsfe) {
464         }
465     }
466 
467     public void deletePages(long nodeId)
468         throws PortalException, SystemException {
469 
470         Iterator<WikiPage> itr = wikiPagePersistence.findByN_H(
471             nodeId, true).iterator();
472 
473         while (itr.hasNext()) {
474             WikiPage page = itr.next();
475 
476             deletePage(page);
477         }
478     }
479 
480     public List<WikiPage> getChildren(
481             long nodeId, boolean head, String parentTitle)
482         throws SystemException {
483 
484         return wikiPagePersistence.findByN_H_P(nodeId, head, parentTitle);
485     }
486 
487     public List<WikiPage> getIncomingLinks(long nodeId, String title)
488         throws PortalException, SystemException {
489 
490         List<WikiPage> links = new UniqueList<WikiPage>();
491 
492         List<WikiPage> pages = wikiPagePersistence.findByN_H(nodeId, true);
493 
494         for (WikiPage page : pages) {
495             if (isLinkedTo(page, title)) {
496                 links.add(page);
497             }
498         }
499 
500         List<WikiPage> referrals = wikiPagePersistence.findByN_R(nodeId, title);
501 
502         for (WikiPage referral : referrals) {
503             for (WikiPage page : pages) {
504                 if (isLinkedTo(page, referral.getTitle())) {
505                     links.add(page);
506                 }
507             }
508         }
509 
510         return ListUtil.sort(links);
511     }
512 
513     public List<WikiPage> getNoAssetPages() throws SystemException {
514         return wikiPageFinder.findByNoAssets();
515     }
516 
517     public List<WikiPage> getOrphans(long nodeId)
518         throws PortalException, SystemException {
519 
520         List<Map<String, Boolean>> pageTitles =
521             new ArrayList<Map<String, Boolean>>();
522 
523         List<WikiPage> pages = wikiPagePersistence.findByN_H(nodeId, true);
524 
525         for (WikiPage page : pages) {
526             pageTitles.add(WikiCacheUtil.getOutgoingLinks(page));
527         }
528 
529         Set<WikiPage> notOrphans = new HashSet<WikiPage>();
530 
531         for (WikiPage page : pages) {
532             for (Map<String, Boolean> pageTitle : pageTitles) {
533                 if (pageTitle.get(page.getTitle()) != null) {
534                     notOrphans.add(page);
535 
536                     break;
537                 }
538             }
539         }
540 
541         List<WikiPage> orphans = new ArrayList<WikiPage>();
542 
543         for (WikiPage page : pages) {
544             if (!notOrphans.contains(page)) {
545                 orphans.add(page);
546             }
547         }
548 
549         orphans = ListUtil.sort(orphans);
550 
551         return orphans;
552     }
553 
554     public List<WikiPage> getOutgoingLinks(long nodeId, String title)
555         throws PortalException, SystemException {
556 
557         WikiPage page = getPage(nodeId, title);
558 
559         Map<String, WikiPage> pages = new LinkedHashMap<String, WikiPage>();
560 
561         Map<String, Boolean> links = WikiCacheUtil.getOutgoingLinks(page);
562 
563         for (String curTitle : links.keySet()) {
564             Boolean exists = links.get(curTitle);
565 
566             if (exists) {
567                 if (!pages.containsKey(curTitle)) {
568                     pages.put(curTitle, getPage(nodeId, curTitle));
569                 }
570             }
571             else {
572                 WikiPageImpl newPage = new WikiPageImpl();
573 
574                 newPage.setNew(true);
575                 newPage.setNodeId(nodeId);
576                 newPage.setTitle(curTitle);
577 
578                 if (!pages.containsKey(curTitle)) {
579                     pages.put(curTitle, newPage);
580                 }
581             }
582         }
583 
584         return ListUtil.fromCollection(pages.values());
585     }
586 
587     public WikiPage getPage(long resourcePrimKey)
588         throws PortalException, SystemException {
589 
590         WikiPageResource wikiPageResource =
591             wikiPageResourceLocalService.getPageResource(resourcePrimKey);
592 
593         return getPage(
594             wikiPageResource.getNodeId(), wikiPageResource.getTitle());
595     }
596 
597     public WikiPage getPage(long nodeId, String title)
598         throws PortalException, SystemException {
599 
600         List<WikiPage> pages = wikiPagePersistence.findByN_T_H(
601             nodeId, title, true, 0, 1);
602 
603         if (pages.size() > 0) {
604             return pages.get(0);
605         }
606         else {
607             throw new NoSuchPageException();
608         }
609     }
610 
611     public WikiPage getPage(long nodeId, String title, double version)
612         throws PortalException, SystemException {
613 
614         WikiPage page = null;
615 
616         if (version == 0) {
617             page = getPage(nodeId, title);
618         }
619         else {
620             page = wikiPagePersistence.findByN_T_V(nodeId, title, version);
621         }
622 
623         return page;
624     }
625 
626     public WikiPageDisplay getPageDisplay(
627             long nodeId, String title, PortletURL viewPageURL,
628             PortletURL editPageURL, String attachmentURLPrefix)
629         throws PortalException, SystemException {
630 
631         WikiPage page = getPage(nodeId, title);
632 
633         String formattedContent = WikiUtil.convert(
634             page, viewPageURL, editPageURL, attachmentURLPrefix);
635 
636         return new WikiPageDisplayImpl(
637             page.getUserId(), page.getNodeId(), page.getTitle(),
638             page.getVersion(), page.getContent(), formattedContent,
639             page.getFormat(), page.getHead(), page.getAttachmentsFiles());
640     }
641 
642     public List<WikiPage> getPages(long nodeId, int start, int end)
643         throws SystemException {
644 
645         return wikiPagePersistence.findByNodeId(
646             nodeId, start, end, new PageCreateDateComparator(false));
647     }
648 
649     public List<WikiPage> getPages(String format) throws SystemException {
650         return wikiPagePersistence.findByFormat(format);
651     }
652 
653     public List<WikiPage> getPages(
654             long nodeId, String title, int start, int end)
655         throws SystemException {
656 
657         return wikiPagePersistence.findByN_T(
658             nodeId, title, start, end, new PageCreateDateComparator(false));
659     }
660 
661     public List<WikiPage> getPages(
662             long nodeId, String title, int start, int end,
663             OrderByComparator obc)
664         throws SystemException {
665 
666         return wikiPagePersistence.findByN_T(nodeId, title, start, end, obc);
667     }
668 
669     public List<WikiPage> getPages(
670             long nodeId, boolean head, int start, int end)
671         throws SystemException {
672 
673         return wikiPagePersistence.findByN_H(
674             nodeId, head, start, end, new PageCreateDateComparator(false));
675     }
676 
677     public List<WikiPage> getPages(
678             long nodeId, String title, boolean head, int start, int end)
679         throws SystemException {
680 
681         return wikiPagePersistence.findByN_T_H(
682             nodeId, title, head, start, end,
683             new PageCreateDateComparator(false));
684     }
685 
686     public int getPagesCount(long nodeId) throws SystemException {
687         return wikiPagePersistence.countByNodeId(nodeId);
688     }
689 
690     public int getPagesCount(long nodeId, String title)
691         throws SystemException {
692 
693         return wikiPagePersistence.countByN_T(nodeId, title);
694     }
695 
696     public int getPagesCount(long nodeId, boolean head)
697         throws SystemException {
698 
699         return wikiPagePersistence.countByN_H(nodeId, head);
700     }
701 
702     public int getPagesCount(long nodeId, String title, boolean head)
703         throws SystemException {
704 
705         return wikiPagePersistence.countByN_T_H(nodeId, title, head);
706     }
707 
708     public int getPagesCount(String format) throws SystemException {
709         return wikiPagePersistence.countByFormat(format);
710     }
711 
712     public List<WikiPage> getRecentChanges(long nodeId, int start, int end)
713         throws SystemException {
714 
715         Calendar cal = CalendarFactoryUtil.getCalendar();
716 
717         cal.add(Calendar.WEEK_OF_YEAR, -1);
718 
719         return wikiPageFinder.findByCreateDate(
720             nodeId, cal.getTime(), false, start, end);
721     }
722 
723     public int getRecentChangesCount(long nodeId) throws SystemException {
724         Calendar cal = CalendarFactoryUtil.getCalendar();
725 
726         cal.add(Calendar.WEEK_OF_YEAR, -1);
727 
728         return wikiPageFinder.countByCreateDate(nodeId, cal.getTime(), false);
729     }
730 
731     public void movePage(
732             long userId, long nodeId, String title, String newTitle,
733             ServiceContext serviceContext)
734         throws PortalException, SystemException {
735 
736         movePage(userId, nodeId, title, newTitle, true, serviceContext);
737     }
738 
739     public void movePage(
740             long userId, long nodeId, String title, String newTitle,
741             boolean strict, ServiceContext serviceContext)
742         throws PortalException, SystemException {
743 
744         validateTitle(newTitle);
745 
746         // Check if the new title already exists
747 
748         if (isUsedTitle(nodeId, newTitle)) {
749             WikiPage page = getPage(nodeId, newTitle);
750 
751             // Support moving back to a previously moved title
752 
753             if (((page.getVersion() == WikiPageImpl.DEFAULT_VERSION) &&
754                  (page.getContent().length() < 200)) ||
755                 !strict) {
756 
757                 deletePage(nodeId, newTitle);
758             }
759             else {
760                 throw new DuplicatePageException(newTitle);
761             }
762         }
763 
764         // All versions
765 
766         List<WikiPage> pageVersions = wikiPagePersistence.findByN_T(
767             nodeId, title);
768 
769         if (pageVersions.size() == 0) {
770             return;
771         }
772 
773         for (WikiPage page : pageVersions) {
774             page.setTitle(newTitle);
775 
776             wikiPagePersistence.update(page, false);
777         }
778 
779         // Children
780 
781         List<WikiPage> children = wikiPagePersistence.findByN_P(nodeId, title);
782 
783         for (WikiPage page : children) {
784             page.setParentTitle(newTitle);
785 
786             wikiPagePersistence.update(page, false);
787         }
788 
789         WikiPage page = pageVersions.get(pageVersions.size() - 1);
790 
791         long resourcePrimKey = page.getResourcePrimKey();
792 
793         // Page resource
794 
795         WikiPageResource wikiPageResource =
796             wikiPageResourcePersistence.findByPrimaryKey(resourcePrimKey);
797 
798         wikiPageResource.setTitle(newTitle);
799 
800         wikiPageResourcePersistence.update(wikiPageResource, false);
801 
802         // Create stub page at the old location
803 
804         String uuid = null;
805         double version = WikiPageImpl.DEFAULT_VERSION;
806         String summary = WikiPageImpl.MOVED + " to " + title;
807         String format = page.getFormat();
808         boolean head = true;
809         String parentTitle = page.getParentTitle();
810         String redirectTitle = page.getTitle();
811         String content =
812             StringPool.DOUBLE_OPEN_BRACKET + redirectTitle +
813                 StringPool.DOUBLE_CLOSE_BRACKET;
814 
815         addPage(
816             uuid, userId, nodeId, title, version, content, summary, false,
817             format, head, parentTitle, redirectTitle, serviceContext);
818 
819         // Move redirects to point to the page with the new title
820 
821         List<WikiPage> redirectedPages = wikiPagePersistence.findByN_R(
822             nodeId, title);
823 
824         for (WikiPage redirectedPage : redirectedPages) {
825             redirectedPage.setRedirectTitle(newTitle);
826 
827             wikiPagePersistence.update(redirectedPage, false);
828         }
829 
830         // Tags
831 
832         String[] tagsCategories = tagsEntryLocalService.getEntryNames(
833             WikiPage.class.getName(), resourcePrimKey,
834             TagsEntryConstants.FOLKSONOMY_CATEGORY);
835         String[] tagsEntries = tagsEntryLocalService.getEntryNames(
836             WikiPage.class.getName(), resourcePrimKey,
837             TagsEntryConstants.FOLKSONOMY_TAG);
838 
839         updateTagsAsset(userId, page, tagsCategories, tagsEntries);
840 
841         // Indexer
842 
843         try {
844             Indexer.deletePage(page.getCompanyId(), page.getGroupId(), title);
845         }
846         catch (SearchException se) {
847             _log.error("Indexing " + newTitle, se);
848         }
849 
850         reIndex(page);
851     }
852 
853     public void reIndex(long resourcePrimKey) throws SystemException {
854         if (SearchEngineUtil.isIndexReadOnly()) {
855             return;
856         }
857 
858         WikiPage page = null;
859 
860         try {
861             page = wikiPageFinder.findByResourcePrimKey(resourcePrimKey);
862         }
863         catch (NoSuchPageException nspe) {
864             return;
865         }
866 
867         reIndex(page);
868     }
869 
870     public void reIndex(WikiPage page) throws SystemException {
871         if (Validator.isNotNull(page.getRedirectTitle())) {
872             return;
873         }
874 
875         long companyId = page.getCompanyId();
876         long groupId = page.getGroupId();
877         long resourcePrimKey = page.getResourcePrimKey();
878         long nodeId = page.getNodeId();
879         String title = page.getTitle();
880         String content = page.getContent();
881         Date modifiedDate = page.getModifiedDate();
882 
883         String[] tagsCategories = tagsEntryLocalService.getEntryNames(
884             WikiPage.class.getName(), resourcePrimKey,
885             TagsEntryConstants.FOLKSONOMY_CATEGORY);
886         String[] tagsEntries = tagsEntryLocalService.getEntryNames(
887             WikiPage.class.getName(), resourcePrimKey);
888 
889         ExpandoBridge expandoBridge = page.getExpandoBridge();
890 
891         try {
892             Indexer.updatePage(
893                 companyId, groupId, resourcePrimKey, nodeId, title, content,
894                 modifiedDate, tagsCategories, tagsEntries, expandoBridge);
895         }
896         catch (SearchException se) {
897             _log.error("Reindexing " + page.getPrimaryKey(), se);
898         }
899     }
900 
901     public WikiPage revertPage(
902             long userId, long nodeId, String title, double version,
903             ServiceContext serviceContext)
904         throws PortalException, SystemException {
905 
906         WikiPage oldPage = getPage(nodeId, title, version);
907 
908         return updatePage(
909             userId, nodeId, title, 0, oldPage.getContent(),
910             WikiPageImpl.REVERTED + " to " + version, false,
911             oldPage.getFormat(), null, oldPage.getRedirectTitle(),
912             serviceContext);
913     }
914 
915     public void subscribePage(long userId, long nodeId, String title)
916         throws PortalException, SystemException {
917 
918         WikiPage page = getPage(nodeId, title);
919 
920         subscriptionLocalService.addSubscription(
921             userId, WikiPage.class.getName(), page.getResourcePrimKey());
922     }
923 
924     public void unsubscribePage(long userId, long nodeId, String title)
925         throws PortalException, SystemException {
926 
927         WikiPage page = getPage(nodeId, title);
928 
929         subscriptionLocalService.deleteSubscription(
930             userId, WikiPage.class.getName(), page.getResourcePrimKey());
931     }
932 
933     public WikiPage updatePage(
934             long userId, long nodeId, String title, double version,
935             String content, String summary, boolean minorEdit, String format,
936             String parentTitle, String redirectTitle,
937             ServiceContext serviceContext)
938         throws PortalException, SystemException {
939 
940         // Page
941 
942         User user = userPersistence.findByPrimaryKey(userId);
943         Date now = new Date();
944 
945         validate(nodeId, content, format);
946 
947         WikiPage page = null;
948 
949         try {
950             page = getPage(nodeId, title);
951         }
952         catch (NoSuchPageException nspe) {
953             return addPage(
954                 null, userId, nodeId, title, WikiPageImpl.DEFAULT_VERSION,
955                 content, summary, minorEdit, format, true, parentTitle,
956                 redirectTitle, serviceContext);
957         }
958 
959         double oldVersion = page.getVersion();
960 
961         if ((version > 0) && (version != oldVersion)) {
962             throw new PageVersionException();
963         }
964 
965         long resourcePrimKey = page.getResourcePrimKey();
966         long groupId = page.getGroupId();
967 
968         page.setHead(false);
969         page.setModifiedDate(now);
970 
971         wikiPagePersistence.update(page, false);
972 
973         double newVersion = MathUtil.format(oldVersion + 0.1, 1, 1);
974 
975         long pageId = counterLocalService.increment();
976 
977         page = wikiPagePersistence.create(pageId);
978 
979         page.setResourcePrimKey(resourcePrimKey);
980         page.setGroupId(groupId);
981         page.setCompanyId(user.getCompanyId());
982         page.setUserId(user.getUserId());
983         page.setUserName(user.getFullName());
984         page.setCreateDate(now);
985         page.setModifiedDate(now);
986         page.setNodeId(nodeId);
987         page.setTitle(title);
988         page.setVersion(newVersion);
989         page.setMinorEdit(minorEdit);
990         page.setContent(content);
991         page.setSummary(summary);
992         page.setFormat(format);
993         page.setHead(true);
994 
995         if (Validator.isNotNull(parentTitle)) {
996             page.setParentTitle(parentTitle);
997         }
998 
999         if (Validator.isNotNull(redirectTitle)) {
1000            page.setRedirectTitle(redirectTitle);
1001        }
1002
1003        wikiPagePersistence.update(page, false);
1004
1005        // Node
1006
1007        WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
1008
1009        node.setLastPostDate(now);
1010
1011        wikiNodePersistence.update(node, false);
1012
1013        // Social
1014
1015        socialActivityLocalService.addActivity(
1016            userId, page.getGroupId(), WikiPage.class.getName(),
1017            page.getResourcePrimKey(), WikiActivityKeys.UPDATE_PAGE,
1018            StringPool.BLANK, 0);
1019
1020        // Subscriptions
1021
1022        if (!minorEdit && NotificationThreadLocal.isNotificationEnabled()) {
1023            notifySubscribers(node, page, serviceContext, true);
1024        }
1025
1026        // Tags
1027
1028        updateTagsAsset(
1029            userId, page, serviceContext.getTagsCategories(),
1030            serviceContext.getTagsEntries());
1031
1032        // Indexer
1033
1034        reIndex(page);
1035
1036        // Cache
1037
1038        clearPageCache(page);
1039
1040        return page;
1041    }
1042
1043    public void updateTagsAsset(
1044            long userId, WikiPage page, String[] tagsCategories,
1045            String[] tagsEntries)
1046        throws PortalException, SystemException {
1047
1048        tagsAssetLocalService.updateAsset(
1049            userId, page.getGroupId(), WikiPage.class.getName(),
1050            page.getResourcePrimKey(), tagsCategories, tagsEntries, true, null,
1051            null, null, null, ContentTypes.TEXT_HTML, page.getTitle(), null,
1052            null, null, 0, 0, null, false);
1053    }
1054
1055    public void validateTitle(String title) throws PortalException {
1056        if (title.equals("all_pages") || title.equals("orphan_pages") ||
1057            title.equals("recent_changes")) {
1058
1059            throw new PageTitleException(title + " is reserved");
1060        }
1061
1062        if (Validator.isNotNull(PropsValues.WIKI_PAGE_TITLES_REGEXP)) {
1063            Pattern pattern = Pattern.compile(
1064                PropsValues.WIKI_PAGE_TITLES_REGEXP);
1065
1066            Matcher matcher = pattern.matcher(title);
1067
1068            if (!matcher.matches()) {
1069                throw new PageTitleException();
1070            }
1071        }
1072    }
1073
1074    protected void clearPageCache(WikiPage page) {
1075        if (!WikiCacheThreadLocal.isClearCache()) {
1076            return;
1077        }
1078
1079        WikiCacheUtil.clearCache(page.getNodeId(), page.getTitle());
1080    }
1081
1082    protected void clearReferralsCache(WikiPage page)
1083        throws PortalException, SystemException {
1084
1085        if (!WikiCacheThreadLocal.isClearCache()) {
1086            return;
1087        }
1088
1089        List<WikiPage> links = getIncomingLinks(
1090            page.getNodeId(), page.getTitle());
1091
1092        for (WikiPage curPage : links) {
1093            WikiCacheUtil.clearCache(curPage.getNodeId(), curPage.getTitle());
1094        }
1095    }
1096
1097    protected boolean isLinkedTo(WikiPage page, String targetTitle)
1098        throws PortalException {
1099
1100        Map<String, Boolean> links = WikiCacheUtil.getOutgoingLinks(page);
1101
1102        Boolean link = links.get(targetTitle);
1103
1104        if (link != null) {
1105            return true;
1106        }
1107        else {
1108            return false;
1109        }
1110    }
1111
1112    protected boolean isUsedTitle(long nodeId, String title)
1113        throws SystemException {
1114
1115        if (getPagesCount(nodeId, title, true) > 0) {
1116            return true;
1117        }
1118        else {
1119            return false;
1120        }
1121    }
1122
1123    protected void notifySubscribers(
1124            WikiNode node, WikiPage page, ServiceContext serviceContext,
1125            boolean update)
1126        throws PortalException, SystemException {
1127
1128        PortletPreferences preferences =
1129            ServiceContextUtil.getPortletPreferences(serviceContext);
1130
1131        if (preferences == null) {
1132            long ownerId = node.getGroupId();
1133            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
1134            long plid = PortletKeys.PREFS_PLID_SHARED;
1135            String portletId = PortletKeys.WIKI;
1136            String defaultPreferences = null;
1137
1138            preferences = portletPreferencesLocalService.getPreferences(
1139                node.getCompanyId(), ownerId, ownerType, plid, portletId,
1140                defaultPreferences);
1141        }
1142
1143        if (!update && WikiUtil.getEmailPageAddedEnabled(preferences)) {
1144        }
1145        else if (update && WikiUtil.getEmailPageUpdatedEnabled(preferences)) {
1146        }
1147        else {
1148            return;
1149        }
1150
1151        Company company = companyPersistence.findByPrimaryKey(
1152            page.getCompanyId());
1153
1154        Group group = groupPersistence.findByPrimaryKey(node.getGroupId());
1155
1156        User user = userPersistence.findByPrimaryKey(page.getUserId());
1157
1158        String pageURL = StringPool.BLANK;
1159
1160        String portalURL = serviceContext.getPortalURL();
1161        String layoutURL = serviceContext.getLayoutURL();
1162
1163        if (Validator.isNotNull(layoutURL) && Validator.isNotNull(portalURL)) {
1164            pageURL =
1165                portalURL + layoutURL + Portal.FRIENDLY_URL_SEPARATOR +
1166                    "wiki/" + node.getNodeId() + StringPool.SLASH +
1167                        HttpUtil.encodeURL(page.getTitle());
1168        }
1169
1170        String portletName = PortalUtil.getPortletTitle(
1171            PortletKeys.WIKI, user);
1172
1173        String fromName = WikiUtil.getEmailFromName(preferences);
1174        String fromAddress = WikiUtil.getEmailFromAddress(preferences);
1175
1176        String replyToAddress = fromAddress;
1177        String mailId = WikiUtil.getMailId(
1178            company.getMx(), page.getNodeId(), page.getPageId());
1179
1180        fromName = StringUtil.replace(
1181            fromName,
1182            new String[] {
1183                "[$COMPANY_ID$]",
1184                "[$COMPANY_MX$]",
1185                "[$COMPANY_NAME$]",
1186                "[$COMMUNITY_NAME$]",
1187                "[$PAGE_USER_ADDRESS$]",
1188                "[$PAGE_USER_NAME$]",
1189                "[$PORTLET_NAME$]"
1190            },
1191            new String[] {
1192                String.valueOf(company.getCompanyId()),
1193                company.getMx(),
1194                company.getName(),
1195                group.getName(),
1196                user.getEmailAddress(),
1197                user.getFullName(),
1198                portletName
1199            });
1200
1201        fromAddress = StringUtil.replace(
1202            fromAddress,
1203            new String[] {
1204                "[$COMPANY_ID$]",
1205                "[$COMPANY_MX$]",
1206                "[$COMPANY_NAME$]",
1207                "[$COMMUNITY_NAME$]",
1208                "[$PAGE_USER_ADDRESS$]",
1209                "[$PAGE_USER_NAME$]",
1210                "[$PORTLET_NAME$]"
1211            },
1212            new String[] {
1213                String.valueOf(company.getCompanyId()),
1214                company.getMx(),
1215                company.getName(),
1216                group.getName(),
1217                user.getEmailAddress(),
1218                user.getFullName(),
1219                portletName
1220            });
1221
1222        String subjectPrefix = null;
1223        String body = null;
1224        String signature = null;
1225
1226        if (update) {
1227            subjectPrefix = WikiUtil.getEmailPageUpdatedSubjectPrefix(
1228                preferences);
1229            body = WikiUtil.getEmailPageUpdatedBody(preferences);
1230            signature = WikiUtil.getEmailPageUpdatedSignature(preferences);
1231        }
1232        else {
1233            subjectPrefix = WikiUtil.getEmailPageAddedSubjectPrefix(
1234                preferences);
1235            body = WikiUtil.getEmailPageAddedBody(preferences);
1236            signature = WikiUtil.getEmailPageAddedSignature(preferences);
1237        }
1238
1239        if (Validator.isNotNull(signature)) {
1240            body +=  "\n--\n" + signature;
1241        }
1242
1243        subjectPrefix = StringUtil.replace(
1244            subjectPrefix,
1245            new String[] {
1246                "[$COMPANY_ID$]",
1247                "[$COMPANY_MX$]",
1248                "[$COMPANY_NAME$]",
1249                "[$COMMUNITY_NAME$]",
1250                "[$FROM_ADDRESS$]",
1251                "[$FROM_NAME$]",
1252                "[$NODE_NAME$]",
1253                "[$PAGE_CONTENT$]",
1254                "[$PAGE_ID$]",
1255                "[$PAGE_TITLE$]",
1256                "[$PAGE_USER_ADDRESS$]",
1257                "[$PAGE_USER_NAME$]",
1258                "[$PORTAL_URL$]",
1259                "[$PORTLET_NAME$]"
1260            },
1261            new String[] {
1262                String.valueOf(company.getCompanyId()),
1263                company.getMx(),
1264                company.getName(),
1265                group.getName(),
1266                fromAddress,
1267                fromName,
1268                node.getName(),
1269                page.getContent(),
1270                String.valueOf(page.getPageId()),
1271                page.getTitle(),
1272                user.getEmailAddress(),
1273                user.getFullName(),
1274                company.getVirtualHost(),
1275                portletName
1276            });
1277
1278        body = StringUtil.replace(
1279            body,
1280            new String[] {
1281                "[$COMPANY_ID$]",
1282                "[$COMPANY_MX$]",
1283                "[$COMPANY_NAME$]",
1284                "[$COMMUNITY_NAME$]",
1285                "[$FROM_ADDRESS$]",
1286                "[$FROM_NAME$]",
1287                "[$NODE_NAME$]",
1288                "[$PAGE_CONTENT$]",
1289                "[$PAGE_ID$]",
1290                "[$PAGE_TITLE$]",
1291                "[$PAGE_URL$]",
1292                "[$PAGE_USER_ADDRESS$]",
1293                "[$PAGE_USER_NAME$]",
1294                "[$PORTAL_URL$]",
1295                "[$PORTLET_NAME$]"
1296            },
1297            new String[] {
1298                String.valueOf(company.getCompanyId()),
1299                company.getMx(),
1300                company.getName(),
1301                group.getName(),
1302                fromAddress,
1303                fromName,
1304                node.getName(),
1305                page.getContent(),
1306                String.valueOf(page.getPageId()),
1307                page.getTitle(),
1308                pageURL,
1309                user.getEmailAddress(),
1310                user.getFullName(),
1311                company.getVirtualHost(),
1312                portletName
1313            });
1314
1315        String subject = page.getTitle();
1316
1317        if (subject.indexOf(subjectPrefix) == -1) {
1318            subject = subjectPrefix + subject;
1319        }
1320
1321        Message message = new Message();
1322
1323        message.put("companyId", node.getCompanyId());
1324        message.put("userId", node.getUserId());
1325        message.put("nodeId", node.getNodeId());
1326        message.put("pageResourcePrimKey", page.getResourcePrimKey());
1327        message.put("fromName", fromName);
1328        message.put("fromAddress", fromAddress);
1329        message.put("subject", subject);
1330        message.put("body", body);
1331        message.put("replyToAddress", replyToAddress);
1332        message.put("mailId", mailId);
1333
1334        MessageBusUtil.sendMessage(DestinationNames.WIKI, message);
1335    }
1336
1337    protected void validate(long nodeId, String content, String format)
1338        throws PortalException {
1339
1340        if (!WikiUtil.validate(nodeId, content, format)) {
1341            throw new PageContentException();
1342        }
1343    }
1344
1345    protected void validate(
1346            String title, long nodeId, String content, String format)
1347        throws PortalException, SystemException {
1348
1349        if (Validator.isNull(title)) {
1350            throw new PageTitleException();
1351        }
1352
1353        if (isUsedTitle(nodeId, title)) {
1354            throw new DuplicatePageException();
1355        }
1356
1357        validateTitle(title);
1358
1359        validate(nodeId, content, format);
1360    }
1361
1362    private static Log _log =
1363        LogFactoryUtil.getLog(WikiPageLocalServiceImpl.class);
1364
1365}