1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.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.messaging.DestinationNames;
32  import com.liferay.portal.kernel.messaging.Message;
33  import com.liferay.portal.kernel.messaging.MessageBusUtil;
34  import com.liferay.portal.kernel.search.SearchException;
35  import com.liferay.portal.kernel.util.CalendarFactoryUtil;
36  import com.liferay.portal.kernel.util.ContentTypes;
37  import com.liferay.portal.kernel.util.ListUtil;
38  import com.liferay.portal.kernel.util.NotificationThreadLocal;
39  import com.liferay.portal.kernel.util.ObjectValuePair;
40  import com.liferay.portal.kernel.util.OrderByComparator;
41  import com.liferay.portal.kernel.util.StringPool;
42  import com.liferay.portal.kernel.util.StringUtil;
43  import com.liferay.portal.kernel.util.Validator;
44  import com.liferay.portal.model.Company;
45  import com.liferay.portal.model.CompanyConstants;
46  import com.liferay.portal.model.Group;
47  import com.liferay.portal.model.GroupConstants;
48  import com.liferay.portal.model.ResourceConstants;
49  import com.liferay.portal.model.User;
50  import com.liferay.portal.theme.ThemeDisplay;
51  import com.liferay.portal.util.PortalUtil;
52  import com.liferay.portal.util.PortletKeys;
53  import com.liferay.portal.util.PropsValues;
54  import com.liferay.portlet.wiki.DuplicatePageException;
55  import com.liferay.portlet.wiki.NoSuchPageException;
56  import com.liferay.portlet.wiki.PageContentException;
57  import com.liferay.portlet.wiki.PageTitleException;
58  import com.liferay.portlet.wiki.PageVersionException;
59  import com.liferay.portlet.wiki.model.WikiNode;
60  import com.liferay.portlet.wiki.model.WikiPage;
61  import com.liferay.portlet.wiki.model.WikiPageDisplay;
62  import com.liferay.portlet.wiki.model.WikiPageResource;
63  import com.liferay.portlet.wiki.model.impl.WikiPageDisplayImpl;
64  import com.liferay.portlet.wiki.model.impl.WikiPageImpl;
65  import com.liferay.portlet.wiki.service.base.WikiPageLocalServiceBaseImpl;
66  import com.liferay.portlet.wiki.social.WikiActivityKeys;
67  import com.liferay.portlet.wiki.util.Indexer;
68  import com.liferay.portlet.wiki.util.WikiCacheThreadLocal;
69  import com.liferay.portlet.wiki.util.WikiCacheUtil;
70  import com.liferay.portlet.wiki.util.WikiUtil;
71  import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
72  import com.liferay.util.MathUtil;
73  import com.liferay.util.UniqueList;
74  
75  import java.rmi.RemoteException;
76  
77  import java.util.ArrayList;
78  import java.util.Calendar;
79  import java.util.Collections;
80  import java.util.Date;
81  import java.util.HashSet;
82  import java.util.Iterator;
83  import java.util.LinkedHashMap;
84  import java.util.List;
85  import java.util.Map;
86  import java.util.Set;
87  import java.util.regex.Matcher;
88  import java.util.regex.Pattern;
89  
90  import javax.portlet.PortletPreferences;
91  import javax.portlet.PortletURL;
92  
93  import org.apache.commons.logging.Log;
94  import org.apache.commons.logging.LogFactory;
95  
96  /**
97   * <a href="WikiPageLocalServiceImpl.java.html"><b><i>View Source</i></b></a>
98   *
99   * @author Brian Wing Shun Chan
100  * @author Jorge Ferrer
101  *
102  */
103 public class WikiPageLocalServiceImpl extends WikiPageLocalServiceBaseImpl {
104 
105     public WikiPage addPage(
106             long userId, long nodeId, String title, String content,
107             String summary, boolean minorEdit, PortletPreferences prefs,
108             ThemeDisplay themeDisplay)
109         throws PortalException, SystemException {
110 
111         String uuid = null;
112         double version = WikiPageImpl.DEFAULT_VERSION;
113         String format = WikiPageImpl.DEFAULT_FORMAT;
114         boolean head = true;
115         String parentTitle = null;
116         String redirectTitle = null;
117         String[] tagsEntries = null;
118 
119         return addPage(
120             uuid, userId, nodeId, title, version, content, summary, minorEdit,
121             format, head, parentTitle, redirectTitle, tagsEntries, prefs,
122             themeDisplay);
123     }
124 
125     public WikiPage addPage(
126             String uuid, long userId, long nodeId, String title, double version,
127             String content, String summary, boolean minorEdit, String format,
128             boolean head, String parentTitle, String redirectTitle,
129             String[] tagsEntries, PortletPreferences prefs,
130             ThemeDisplay themeDisplay)
131         throws PortalException, SystemException {
132 
133         // Page
134 
135         User user = userPersistence.findByPrimaryKey(userId);
136         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
137 
138         Date now = new Date();
139 
140         validate(title, nodeId, content, format);
141 
142         long pageId = counterLocalService.increment();
143 
144         long resourcePrimKey =
145             wikiPageResourceLocalService.getPageResourcePrimKey(nodeId, title);
146 
147         WikiPage page = wikiPagePersistence.create(pageId);
148 
149         page.setUuid(uuid);
150         page.setResourcePrimKey(resourcePrimKey);
151         page.setCompanyId(user.getCompanyId());
152         page.setUserId(user.getUserId());
153         page.setUserName(user.getFullName());
154         page.setCreateDate(now);
155         page.setModifiedDate(now);
156         page.setNodeId(nodeId);
157         page.setTitle(title);
158         page.setVersion(version);
159         page.setMinorEdit(minorEdit);
160         page.setContent(content);
161         page.setSummary(summary);
162         page.setFormat(format);
163         page.setHead(head);
164         page.setParentTitle(parentTitle);
165         page.setRedirectTitle(redirectTitle);
166 
167         wikiPagePersistence.update(page, false);
168 
169         // Resources
170 
171         addPageResources(page.getNode(), page, true, true);
172 
173         // Node
174 
175         node.setLastPostDate(now);
176 
177         wikiNodePersistence.update(node, false);
178 
179         // Social
180 
181         socialActivityLocalService.addActivity(
182             userId, node.getGroupId(), WikiPage.class.getName(),
183             page.getResourcePrimKey(), WikiActivityKeys.ADD_PAGE,
184             StringPool.BLANK, 0);
185 
186         // Subscriptions
187 
188         if (!minorEdit && NotificationThreadLocal.isNotificationEnabled()) {
189             notifySubscribers(node, page, prefs, themeDisplay, false);
190         }
191 
192         // Tags
193 
194         updateTagsAsset(userId, page, tagsEntries);
195 
196         // Lucene
197 
198         try {
199             Indexer.addPage(
200                 page.getCompanyId(), node.getGroupId(), nodeId, title,
201                 content, tagsEntries);
202         }
203         catch (SearchException se) {
204             _log.error("Indexing " + pageId, se);
205         }
206 
207         // Cache
208 
209         clearReferralsCache(page);
210         clearPageCache(page);
211 
212         return page;
213     }
214 
215     public void addPageAttachments(
216             long nodeId, String title,
217             List<ObjectValuePair<String, byte[]>> files)
218         throws PortalException, SystemException {
219 
220         if (files.size() == 0) {
221             return;
222         }
223 
224         WikiPage page = getPage(nodeId, title);
225 
226         long companyId = page.getCompanyId();
227         String portletId = CompanyConstants.SYSTEM_STRING;
228         long groupId = GroupConstants.DEFAULT_PARENT_GROUP_ID;
229         long repositoryId = CompanyConstants.SYSTEM;
230         String dirName = page.getAttachmentsDir();
231 
232         try {
233             try {
234                 dlService.addDirectory(companyId, repositoryId, dirName);
235             }
236             catch (DuplicateDirectoryException dde) {
237             }
238 
239             for (int i = 0; i < files.size(); i++) {
240                 ObjectValuePair<String, byte[]> ovp = files.get(i);
241 
242                 String fileName = ovp.getKey();
243                 byte[] bytes = ovp.getValue();
244 
245                 if (Validator.isNull(fileName)) {
246                     continue;
247                 }
248 
249                 try {
250                     dlService.addFile(
251                         companyId, portletId, groupId, repositoryId,
252                         dirName + "/" + fileName, StringPool.BLANK,
253                         new String[0], bytes);
254                 }
255                 catch (DuplicateFileException dfe) {
256                 }
257             }
258         }
259         catch (RemoteException re) {
260             throw new SystemException(re);
261         }
262     }
263 
264     public void addPageResources(
265             long nodeId, String title, boolean addCommunityPermissions,
266             boolean addGuestPermissions)
267         throws PortalException, SystemException {
268 
269         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
270         WikiPage page = getPage(nodeId, title);
271 
272         addPageResources(
273             node, page, addCommunityPermissions, addGuestPermissions);
274     }
275 
276     public void addPageResources(
277             WikiNode node, WikiPage page, boolean addCommunityPermissions,
278             boolean addGuestPermissions)
279         throws PortalException, SystemException {
280 
281         resourceLocalService.addResources(
282             page.getCompanyId(), node.getGroupId(), page.getUserId(),
283             WikiPage.class.getName(), page.getResourcePrimKey(), false,
284             addCommunityPermissions, addGuestPermissions);
285     }
286 
287     public void addPageResources(
288             long nodeId, String title, String[] communityPermissions,
289             String[] guestPermissions)
290         throws PortalException, SystemException {
291 
292         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
293         WikiPage page = getPage(nodeId, title);
294 
295         addPageResources(node, page, communityPermissions, guestPermissions);
296     }
297 
298     public void addPageResources(
299             WikiNode node, WikiPage page, String[] communityPermissions,
300             String[] guestPermissions)
301         throws PortalException, SystemException {
302 
303         resourceLocalService.addModelResources(
304             page.getCompanyId(), node.getGroupId(), page.getUserId(),
305             WikiPage.class.getName(), page.getResourcePrimKey(),
306             communityPermissions, guestPermissions);
307     }
308 
309     public void changeParent(
310             long userId, long nodeId, String title, String newParentTitle,
311             PortletPreferences prefs, ThemeDisplay themeDisplay)
312         throws PortalException, SystemException {
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_P(
362             page.getNodeId(), page.getTitle());
363 
364         for (WikiPage curPage : children) {
365             deletePage(curPage);
366         }
367 
368         // Lucene
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         clearReferralsCache(page);
437     }
438 
439     public void deletePageAttachment(long nodeId, String title, String fileName)
440         throws PortalException, SystemException {
441 
442         if (Validator.isNull(fileName)) {
443             return;
444         }
445 
446         WikiPage page = getPage(nodeId, title);
447 
448         long companyId = page.getCompanyId();
449         String portletId = CompanyConstants.SYSTEM_STRING;
450         long repositoryId = CompanyConstants.SYSTEM;
451 
452         try {
453             dlService.deleteFile(companyId, portletId, repositoryId, fileName);
454         }
455         catch (NoSuchFileException nsfe) {
456         }
457         catch (RemoteException re) {
458             throw new SystemException(re);
459         }
460     }
461 
462     public void deletePages(long nodeId)
463         throws PortalException, SystemException {
464 
465         Iterator<WikiPage> itr = wikiPagePersistence.findByN_H(
466             nodeId, true).iterator();
467 
468         while (itr.hasNext()) {
469             WikiPage page = itr.next();
470 
471             deletePage(page);
472         }
473     }
474 
475     public List<WikiPage> getChildren(
476             long nodeId, boolean head, String parentTitle)
477         throws SystemException {
478 
479         return wikiPagePersistence.findByN_H_P(nodeId, head, parentTitle);
480     }
481 
482     public List<WikiPage> getIncomingLinks(long nodeId, String title)
483         throws PortalException, SystemException {
484 
485         List<WikiPage> links = new UniqueList<WikiPage>();
486 
487         List<WikiPage> pages = wikiPagePersistence.findByN_H(nodeId, true);
488 
489         for (WikiPage page : pages) {
490             if (isLinkedTo(page, title)) {
491                 links.add(page);
492             }
493         }
494 
495         List<WikiPage> referrals = wikiPagePersistence.findByN_R(nodeId, title);
496 
497         for (WikiPage referral : referrals) {
498             for (WikiPage page : pages) {
499                 if (isLinkedTo(page, referral.getTitle())) {
500                     links.add(page);
501                 }
502             }
503         }
504 
505         Collections.sort(links);
506 
507         return links;
508     }
509 
510     public List<WikiPage> getNoAssetPages() throws SystemException {
511         return wikiPageFinder.findByNoAssets();
512     }
513 
514     public List<WikiPage> getOrphans(long nodeId)
515         throws PortalException, SystemException {
516 
517         List<Map<String, Boolean>> pageTitles =
518             new ArrayList<Map<String, Boolean>>();
519 
520         List<WikiPage> pages = wikiPagePersistence.findByN_H(nodeId, true);
521 
522         for (WikiPage page : pages) {
523             pageTitles.add(WikiCacheUtil.getOutgoingLinks(page));
524         }
525 
526         Set<WikiPage> notOrphans = new HashSet<WikiPage>();
527 
528         for (WikiPage page : pages) {
529             for (Map<String, Boolean> pageTitle : pageTitles) {
530                 if (pageTitle.get(page.getTitle()) != null) {
531                     notOrphans.add(page);
532 
533                     break;
534                 }
535             }
536         }
537 
538         List<WikiPage> orphans = new ArrayList<WikiPage>();
539 
540         for (WikiPage page : pages) {
541             if (!notOrphans.contains(page)) {
542                 orphans.add(page);
543             }
544         }
545 
546         Collections.sort(orphans);
547 
548         return orphans;
549     }
550 
551     public List<WikiPage> getOutgoingLinks(long nodeId, String title)
552         throws PortalException, SystemException {
553 
554         WikiPage page = getPage(nodeId, title);
555 
556         Map<String, WikiPage> pages = new LinkedHashMap<String, WikiPage>();
557 
558         Map<String, Boolean> links = WikiCacheUtil.getOutgoingLinks(page);
559 
560         for (String curTitle : links.keySet()) {
561             Boolean exists = links.get(curTitle);
562 
563             if (exists) {
564                 if (!pages.containsKey(curTitle)) {
565                     pages.put(curTitle, getPage(nodeId, curTitle));
566                 }
567             }
568             else {
569                 WikiPageImpl newPage = new WikiPageImpl();
570 
571                 newPage.setNew(true);
572                 newPage.setNodeId(nodeId);
573                 newPage.setTitle(curTitle);
574 
575                 if (!pages.containsKey(curTitle)) {
576                     pages.put(curTitle, newPage);
577                 }
578             }
579         }
580 
581         return ListUtil.fromCollection(pages.values());
582     }
583 
584     public WikiPage getPage(long nodeId, String title)
585         throws PortalException, SystemException {
586 
587         List<WikiPage> pages = wikiPagePersistence.findByN_T_H(
588             nodeId, title, true, 0, 1);
589 
590         if (pages.size() > 0) {
591             return pages.get(0);
592         }
593         else {
594             throw new NoSuchPageException();
595         }
596     }
597 
598     public WikiPage getPage(long nodeId, String title, double version)
599         throws PortalException, SystemException {
600 
601         WikiPage page = null;
602 
603         if (version == 0) {
604             page = getPage(nodeId, title);
605         }
606         else {
607             page = wikiPagePersistence.findByN_T_V(nodeId, title, version);
608         }
609 
610         return page;
611     }
612 
613     public WikiPageDisplay getPageDisplay(
614             long nodeId, String title, PortletURL viewPageURL,
615             PortletURL editPageURL, String attachmentURLPrefix)
616         throws PortalException, SystemException {
617 
618         WikiPage page = getPage(nodeId, title);
619 
620         String formattedContent = WikiUtil.convert(
621             page, viewPageURL, editPageURL, attachmentURLPrefix);
622 
623         return new WikiPageDisplayImpl(
624             page.getUserId(), page.getNodeId(), page.getTitle(),
625             page.getVersion(), page.getContent(), formattedContent,
626             page.getFormat(), page.getHead(), page.getAttachmentsFiles());
627     }
628 
629     public List<WikiPage> getPages(long nodeId, int start, int end)
630         throws SystemException {
631 
632         return wikiPagePersistence.findByNodeId(
633             nodeId, start, end, new PageCreateDateComparator(false));
634     }
635 
636     public List<WikiPage> getPages(String format) throws SystemException {
637         return wikiPagePersistence.findByFormat(format);
638     }
639 
640     public List<WikiPage> getPages(
641             long nodeId, String title, int start, int end)
642         throws SystemException {
643 
644         return wikiPagePersistence.findByN_T(
645             nodeId, title, start, end, new PageCreateDateComparator(false));
646     }
647 
648     public List<WikiPage> getPages(
649             long nodeId, String title, int start, int end,
650             OrderByComparator obc)
651         throws SystemException {
652 
653         return wikiPagePersistence.findByN_T(nodeId, title, start, end, obc);
654     }
655 
656     public List<WikiPage> getPages(
657             long nodeId, boolean head, int start, int end)
658         throws SystemException {
659 
660         return wikiPagePersistence.findByN_H(
661             nodeId, head, start, end, new PageCreateDateComparator(false));
662     }
663 
664     public List<WikiPage> getPages(
665             long nodeId, String title, boolean head, int start, int end)
666         throws SystemException {
667 
668         return wikiPagePersistence.findByN_T_H(
669             nodeId, title, head, start, end,
670             new PageCreateDateComparator(false));
671     }
672 
673     public int getPagesCount(long nodeId) throws SystemException {
674         return wikiPagePersistence.countByNodeId(nodeId);
675     }
676 
677     public int getPagesCount(long nodeId, String title)
678         throws SystemException {
679 
680         return wikiPagePersistence.countByN_T(nodeId, title);
681     }
682 
683     public int getPagesCount(long nodeId, boolean head)
684         throws SystemException {
685 
686         return wikiPagePersistence.countByN_H(nodeId, head);
687     }
688 
689     public int getPagesCount(long nodeId, String title, boolean head)
690         throws SystemException {
691 
692         return wikiPagePersistence.countByN_T_H(nodeId, title, head);
693     }
694 
695     public List<WikiPage> getRecentChanges(long nodeId, int start, int end)
696         throws SystemException {
697 
698         Calendar cal = CalendarFactoryUtil.getCalendar();
699 
700         cal.add(Calendar.WEEK_OF_YEAR, -1);
701 
702         return wikiPageFinder.findByCreateDate(
703             nodeId, cal.getTime(), false, start, end);
704     }
705 
706     public int getRecentChangesCount(long nodeId) throws SystemException {
707         Calendar cal = CalendarFactoryUtil.getCalendar();
708 
709         cal.add(Calendar.WEEK_OF_YEAR, -1);
710 
711         return wikiPageFinder.countByCreateDate(nodeId, cal.getTime(), false);
712     }
713 
714     public void movePage(
715             long userId, long nodeId, String title, String newTitle,
716             PortletPreferences prefs, ThemeDisplay themeDisplay)
717         throws PortalException, SystemException {
718 
719         movePage(userId, nodeId, title, newTitle, true, prefs, themeDisplay);
720     }
721 
722     public void movePage(
723             long userId, long nodeId, String title, String newTitle,
724             boolean strict, PortletPreferences prefs, ThemeDisplay themeDisplay)
725         throws PortalException, SystemException {
726 
727         validateTitle(newTitle);
728 
729         // Check if the new title already exists
730 
731         if (isUsedTitle(nodeId, newTitle)) {
732             WikiPage page = getPage(nodeId, newTitle);
733 
734             // Support moving back to a previously moved title
735 
736             if (((page.getVersion() == WikiPageImpl.DEFAULT_VERSION) &&
737                  (page.getContent().equals(WikiPageImpl.MOVED))) ||
738                 !strict) {
739 
740                 deletePage(nodeId, newTitle);
741             }
742             else {
743                 throw new DuplicatePageException(newTitle);
744             }
745         }
746 
747         // All versions
748 
749         List<WikiPage> pageVersions = wikiPagePersistence.findByN_T(
750             nodeId, title);
751 
752         if (pageVersions.size() == 0) {
753             return;
754         }
755 
756         for (WikiPage page : pageVersions) {
757             page.setTitle(newTitle);
758 
759             wikiPagePersistence.update(page, false);
760         }
761 
762         // Children
763 
764         List<WikiPage> children = wikiPagePersistence.findByN_P(nodeId, title);
765 
766         for (WikiPage page : children) {
767             page.setParentTitle(newTitle);
768 
769             wikiPagePersistence.update(page, false);
770         }
771 
772         WikiPage page = pageVersions.get(pageVersions.size() - 1);
773 
774         // Page resource
775 
776         WikiPageResource wikiPageResource =
777             wikiPageResourcePersistence.findByPrimaryKey(
778                 page.getResourcePrimKey());
779 
780         wikiPageResource.setTitle(newTitle);
781 
782         wikiPageResourcePersistence.update(wikiPageResource, false);
783 
784         // Create stub page at the old location
785 
786         String uuid = null;
787         double version = WikiPageImpl.DEFAULT_VERSION;
788         String format = page.getFormat();
789         boolean head = true;
790         String parentTitle = page.getParentTitle();
791         String redirectTitle = page.getTitle();
792         String content =
793             StringPool.DOUBLE_OPEN_BRACKET + redirectTitle +
794                 StringPool.DOUBLE_CLOSE_BRACKET;
795         String summary = WikiPageImpl.MOVED + " to " + title;
796 
797         addPage(
798             uuid, userId, nodeId, title, version, content, summary, false,
799             format, head, parentTitle, redirectTitle, null, prefs,
800             themeDisplay);
801 
802         // Move redirects to point to the page with the new title
803 
804         List<WikiPage> redirectedPages = wikiPagePersistence.findByN_R(
805             nodeId, title);
806 
807         for (WikiPage redirectedPage : redirectedPages) {
808             redirectedPage.setRedirectTitle(newTitle);
809 
810             wikiPagePersistence.update(redirectedPage, false);
811         }
812 
813         // Tags
814 
815         String[] tagsEntries = tagsEntryLocalService.getEntryNames(
816             WikiPage.class.getName(), page.getResourcePrimKey());
817 
818         updateTagsAsset(userId, page, tagsEntries);
819 
820         // Lucene
821 
822         try {
823             Indexer.updatePage(
824                 page.getCompanyId(), page.getNode().getGroupId(), nodeId,
825                 newTitle, content, tagsEntries);
826         }
827         catch (SearchException se) {
828             _log.error("Indexing " + newTitle, se);
829         }
830     }
831 
832     public WikiPage revertPage(
833             long userId, long nodeId, String title, double version,
834             PortletPreferences prefs, ThemeDisplay themeDisplay)
835         throws PortalException, SystemException {
836 
837         WikiPage oldPage = getPage(nodeId, title, version);
838 
839         return updatePage(
840             userId, nodeId, title, 0, oldPage.getContent(),
841             WikiPageImpl.REVERTED + " to " + version, false,
842             oldPage.getFormat(), null, oldPage.getRedirectTitle(), null, prefs,
843             themeDisplay);
844     }
845 
846     public void subscribePage(long userId, long nodeId, String title)
847         throws PortalException, SystemException {
848 
849         WikiPage page = getPage(nodeId, title);
850 
851         subscriptionLocalService.addSubscription(
852             userId, WikiPage.class.getName(), page.getResourcePrimKey());
853     }
854 
855     public void unsubscribePage(long userId, long nodeId, String title)
856         throws PortalException, SystemException {
857 
858         WikiPage page = getPage(nodeId, title);
859 
860         subscriptionLocalService.deleteSubscription(
861             userId, WikiPage.class.getName(), page.getResourcePrimKey());
862     }
863 
864     public WikiPage updatePage(
865             long userId, long nodeId, String title, double version,
866             String content, String summary, boolean minorEdit, String format,
867             String parentTitle, String redirectTitle, String[] tagsEntries,
868             PortletPreferences prefs, ThemeDisplay themeDisplay)
869         throws PortalException, SystemException {
870 
871         // Page
872 
873         User user = userPersistence.findByPrimaryKey(userId);
874         Date now = new Date();
875 
876         validate(nodeId, content, format);
877 
878         WikiPage page = null;
879 
880         try {
881             page = getPage(nodeId, title);
882         }
883         catch (NoSuchPageException nspe) {
884             return addPage(
885                 null, userId, nodeId, title, WikiPageImpl.DEFAULT_VERSION,
886                 content, summary, minorEdit, format, true, parentTitle,
887                 redirectTitle, tagsEntries, prefs, themeDisplay);
888         }
889 
890         double oldVersion = page.getVersion();
891 
892         if ((version > 0) && (version != oldVersion)) {
893             throw new PageVersionException();
894         }
895 
896         long resourcePrimKey = page.getResourcePrimKey();
897 
898         page.setHead(false);
899         page.setModifiedDate(now);
900 
901         wikiPagePersistence.update(page, false);
902 
903         double newVersion = MathUtil.format(oldVersion + 0.1, 1, 1);
904 
905         long pageId = counterLocalService.increment();
906 
907         page = wikiPagePersistence.create(pageId);
908 
909         page.setResourcePrimKey(resourcePrimKey);
910         page.setCompanyId(user.getCompanyId());
911         page.setUserId(user.getUserId());
912         page.setUserName(user.getFullName());
913         page.setCreateDate(now);
914         page.setModifiedDate(now);
915         page.setNodeId(nodeId);
916         page.setTitle(title);
917         page.setVersion(newVersion);
918         page.setMinorEdit(minorEdit);
919         page.setContent(content);
920         page.setSummary(summary);
921         page.setFormat(format);
922         page.setHead(true);
923 
924         if (Validator.isNotNull(parentTitle)) {
925             page.setParentTitle(parentTitle);
926         }
927 
928         if (Validator.isNotNull(redirectTitle)) {
929             page.setRedirectTitle(redirectTitle);
930         }
931 
932         wikiPagePersistence.update(page, false);
933 
934         // Node
935 
936         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
937 
938         node.setLastPostDate(now);
939 
940         wikiNodePersistence.update(node, false);
941 
942         // Social
943 
944         socialActivityLocalService.addActivity(
945             userId, node.getGroupId(), WikiPage.class.getName(),
946             page.getResourcePrimKey(), WikiActivityKeys.UPDATE_PAGE,
947             StringPool.BLANK, 0);
948 
949         // Subscriptions
950 
951         if (!minorEdit && NotificationThreadLocal.isNotificationEnabled()) {
952             notifySubscribers(node, page, prefs, themeDisplay, true);
953         }
954 
955         // Tags
956 
957         updateTagsAsset(userId, page, tagsEntries);
958 
959         // Lucene
960 
961         try {
962             Indexer.updatePage(
963                 node.getCompanyId(), node.getGroupId(), nodeId, title, content,
964                 tagsEntries);
965         }
966         catch (SearchException se) {
967             _log.error("Indexing " + page.getPrimaryKey(), se);
968         }
969 
970         // Cache
971 
972         clearPageCache(page);
973 
974         return page;
975     }
976 
977     public void updateTagsAsset(
978             long userId, WikiPage page, String[] tagsEntries)
979         throws PortalException, SystemException {
980 
981         tagsAssetLocalService.updateAsset(
982             userId, page.getNode().getGroupId(), WikiPage.class.getName(),
983             page.getResourcePrimKey(), tagsEntries, null, null, null, null,
984             ContentTypes.TEXT_HTML, page.getTitle(), null, null, null, 0, 0,
985             null, false);
986     }
987 
988     public void validateTitle(String title) throws PortalException {
989         if (title.equals("all_pages") || title.equals("orphan_pages") ||
990             title.equals("recent_changes")) {
991 
992             throw new PageTitleException(title + " is reserved");
993         }
994 
995         if (Validator.isNotNull(PropsValues.WIKI_PAGE_TITLES_REGEXP)) {
996             Pattern pattern = Pattern.compile(
997                 PropsValues.WIKI_PAGE_TITLES_REGEXP);
998 
999             Matcher matcher = pattern.matcher(title);
1000
1001            if (!matcher.matches()) {
1002                throw new PageTitleException();
1003            }
1004        }
1005    }
1006
1007    protected void clearPageCache(WikiPage page) {
1008        if (!WikiCacheThreadLocal.isClearCache()) {
1009            return;
1010        }
1011
1012        WikiCacheUtil.clearCache(page.getNodeId(), page.getTitle());
1013    }
1014
1015    protected void clearReferralsCache(WikiPage page)
1016        throws PortalException, SystemException {
1017
1018        if (!WikiCacheThreadLocal.isClearCache()) {
1019            return;
1020        }
1021
1022        List<WikiPage> links = getIncomingLinks(
1023            page.getNodeId(), page.getTitle());
1024
1025        for (WikiPage curPage : links) {
1026            WikiCacheUtil.clearCache(curPage.getNodeId(), curPage.getTitle());
1027        }
1028    }
1029
1030    protected boolean isLinkedTo(WikiPage page, String targetTitle)
1031        throws PortalException {
1032
1033        Map<String, Boolean> links = WikiCacheUtil.getOutgoingLinks(page);
1034
1035        Boolean link = links.get(targetTitle);
1036
1037        if (link != null) {
1038            return true;
1039        }
1040        else {
1041            return false;
1042        }
1043    }
1044
1045    protected boolean isUsedTitle(long nodeId, String title)
1046        throws SystemException {
1047
1048        if (getPagesCount(nodeId, title, true) > 0) {
1049            return true;
1050        }
1051        else {
1052            return false;
1053        }
1054    }
1055
1056    protected void notifySubscribers(
1057            WikiNode node, WikiPage page, PortletPreferences prefs,
1058            ThemeDisplay themeDisplay, boolean update)
1059        throws PortalException, SystemException {
1060
1061        if (prefs == null) {
1062            long ownerId = node.getGroupId();
1063            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
1064            long plid = PortletKeys.PREFS_PLID_SHARED;
1065            String portletId = PortletKeys.WIKI;
1066            String defaultPreferences = null;
1067
1068            prefs = portletPreferencesLocalService.getPreferences(
1069                node.getCompanyId(), ownerId, ownerType, plid, portletId,
1070                defaultPreferences);
1071        }
1072
1073        if (!update && WikiUtil.getEmailPageAddedEnabled(prefs)) {
1074        }
1075        else if (update && WikiUtil.getEmailPageUpdatedEnabled(prefs)) {
1076        }
1077        else {
1078            return;
1079        }
1080
1081        Company company = companyPersistence.findByPrimaryKey(
1082            page.getCompanyId());
1083
1084        Group group = groupPersistence.findByPrimaryKey(node.getGroupId());
1085
1086        User user = userPersistence.findByPrimaryKey(page.getUserId());
1087
1088        String pageURL = StringPool.BLANK;
1089
1090        if (themeDisplay != null) {
1091            String portalURL = PortalUtil.getPortalURL(themeDisplay);
1092            String layoutURL = PortalUtil.getLayoutURL(themeDisplay);
1093
1094            pageURL =
1095                portalURL + layoutURL + "/wiki/" + node.getNodeId() + "/" +
1096                    page.getTitle();
1097        }
1098
1099        String portletName = PortalUtil.getPortletTitle(
1100            PortletKeys.WIKI, user);
1101
1102        String fromName = WikiUtil.getEmailFromName(prefs);
1103        String fromAddress = WikiUtil.getEmailFromAddress(prefs);
1104
1105        String replyToAddress = fromAddress;
1106        String mailId = WikiUtil.getMailId(
1107            company.getMx(), page.getNodeId(), page.getPageId());
1108
1109        fromName = StringUtil.replace(
1110            fromName,
1111            new String[] {
1112                "[$COMPANY_ID$]",
1113                "[$COMPANY_MX$]",
1114                "[$COMPANY_NAME$]",
1115                "[$COMMUNITY_NAME$]",
1116                "[$PAGE_USER_ADDRESS$]",
1117                "[$PAGE_USER_NAME$]",
1118                "[$PORTLET_NAME$]"
1119            },
1120            new String[] {
1121                String.valueOf(company.getCompanyId()),
1122                company.getMx(),
1123                company.getName(),
1124                group.getName(),
1125                user.getEmailAddress(),
1126                user.getFullName(),
1127                portletName
1128            });
1129
1130        fromAddress = StringUtil.replace(
1131            fromAddress,
1132            new String[] {
1133                "[$COMPANY_ID$]",
1134                "[$COMPANY_MX$]",
1135                "[$COMPANY_NAME$]",
1136                "[$COMMUNITY_NAME$]",
1137                "[$PAGE_USER_ADDRESS$]",
1138                "[$PAGE_USER_NAME$]",
1139                "[$PORTLET_NAME$]"
1140            },
1141            new String[] {
1142                String.valueOf(company.getCompanyId()),
1143                company.getMx(),
1144                company.getName(),
1145                group.getName(),
1146                user.getEmailAddress(),
1147                user.getFullName(),
1148                portletName
1149            });
1150
1151        String subjectPrefix = null;
1152        String body = null;
1153        String signature = null;
1154
1155        if (update) {
1156            subjectPrefix = WikiUtil.getEmailPageUpdatedSubjectPrefix(prefs);
1157            body = WikiUtil.getEmailPageUpdatedBody(prefs);
1158            signature = WikiUtil.getEmailPageUpdatedSignature(prefs);
1159        }
1160        else {
1161            subjectPrefix = WikiUtil.getEmailPageAddedSubjectPrefix(prefs);
1162            body = WikiUtil.getEmailPageAddedBody(prefs);
1163            signature = WikiUtil.getEmailPageAddedSignature(prefs);
1164        }
1165
1166        if (Validator.isNotNull(signature)) {
1167            body +=  "\n--\n" + signature;
1168        }
1169
1170        subjectPrefix = StringUtil.replace(
1171            subjectPrefix,
1172            new String[] {
1173                "[$COMPANY_ID$]",
1174                "[$COMPANY_MX$]",
1175                "[$COMPANY_NAME$]",
1176                "[$COMMUNITY_NAME$]",
1177                "[$FROM_ADDRESS$]",
1178                "[$FROM_NAME$]",
1179                "[$NODE_NAME$]",
1180                "[$PAGE_CONTENT$]",
1181                "[$PAGE_ID$]",
1182                "[$PAGE_TITLE$]",
1183                "[$PAGE_USER_ADDRESS$]",
1184                "[$PAGE_USER_NAME$]",
1185                "[$PORTAL_URL$]",
1186                "[$PORTLET_NAME$]"
1187            },
1188            new String[] {
1189                String.valueOf(company.getCompanyId()),
1190                company.getMx(),
1191                company.getName(),
1192                group.getName(),
1193                fromAddress,
1194                fromName,
1195                node.getName(),
1196                page.getContent(),
1197                String.valueOf(page.getPageId()),
1198                page.getTitle(),
1199                user.getEmailAddress(),
1200                user.getFullName(),
1201                company.getVirtualHost(),
1202                portletName
1203            });
1204
1205        body = StringUtil.replace(
1206            body,
1207            new String[] {
1208                "[$COMPANY_ID$]",
1209                "[$COMPANY_MX$]",
1210                "[$COMPANY_NAME$]",
1211                "[$COMMUNITY_NAME$]",
1212                "[$FROM_ADDRESS$]",
1213                "[$FROM_NAME$]",
1214                "[$NODE_NAME$]",
1215                "[$PAGE_CONTENT$]",
1216                "[$PAGE_ID$]",
1217                "[$PAGE_TITLE$]",
1218                "[$PAGE_URL$]",
1219                "[$PAGE_USER_ADDRESS$]",
1220                "[$PAGE_USER_NAME$]",
1221                "[$PORTAL_URL$]",
1222                "[$PORTLET_NAME$]"
1223            },
1224            new String[] {
1225                String.valueOf(company.getCompanyId()),
1226                company.getMx(),
1227                company.getName(),
1228                group.getName(),
1229                fromAddress,
1230                fromName,
1231                node.getName(),
1232                page.getContent(),
1233                String.valueOf(page.getPageId()),
1234                page.getTitle(),
1235                pageURL,
1236                user.getEmailAddress(),
1237                user.getFullName(),
1238                company.getVirtualHost(),
1239                portletName
1240            });
1241
1242        String subject = page.getTitle();
1243
1244        if (subject.indexOf(subjectPrefix) == -1) {
1245            subject = subjectPrefix + subject;
1246        }
1247
1248        Message message = new Message();
1249
1250        message.put("companyId", node.getCompanyId());
1251        message.put("userId", node.getUserId());
1252        message.put("nodeId", node.getNodeId());
1253        message.put("pageResourcePrimKey", page.getResourcePrimKey());
1254        message.put("fromName", fromName);
1255        message.put("fromAddress", fromAddress);
1256        message.put("subject", subject);
1257        message.put("body", body);
1258        message.put("replyToAddress", replyToAddress);
1259        message.put("mailId", mailId);
1260
1261        MessageBusUtil.sendMessage(DestinationNames.WIKI, message);
1262    }
1263
1264    protected void validate(long nodeId, String content, String format)
1265        throws PortalException {
1266
1267        if (!WikiUtil.validate(nodeId, content, format)) {
1268            throw new PageContentException();
1269        }
1270    }
1271
1272    protected void validate(
1273            String title, long nodeId, String content, String format)
1274        throws PortalException {
1275
1276        if (Validator.isNull(title)) {
1277            throw new PageTitleException();
1278        }
1279
1280        validateTitle(title);
1281
1282        validate(nodeId, content, format);
1283    }
1284
1285    private static Log _log = LogFactory.getLog(WikiPageLocalServiceImpl.class);
1286
1287}