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