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.HttpUtil;
38  import com.liferay.portal.kernel.util.ListUtil;
39  import com.liferay.portal.kernel.util.NotificationThreadLocal;
40  import com.liferay.portal.kernel.util.ObjectValuePair;
41  import com.liferay.portal.kernel.util.OrderByComparator;
42  import com.liferay.portal.kernel.util.StringPool;
43  import com.liferay.portal.kernel.util.StringUtil;
44  import com.liferay.portal.kernel.util.Validator;
45  import com.liferay.portal.model.Company;
46  import com.liferay.portal.model.CompanyConstants;
47  import com.liferay.portal.model.Group;
48  import com.liferay.portal.model.GroupConstants;
49  import com.liferay.portal.model.ResourceConstants;
50  import com.liferay.portal.model.User;
51  import com.liferay.portal.theme.ThemeDisplay;
52  import com.liferay.portal.util.PortalUtil;
53  import com.liferay.portal.util.PortletKeys;
54  import com.liferay.portal.util.PropsValues;
55  import com.liferay.portlet.wiki.DuplicatePageException;
56  import com.liferay.portlet.wiki.NoSuchPageException;
57  import com.liferay.portlet.wiki.PageContentException;
58  import com.liferay.portlet.wiki.PageTitleException;
59  import com.liferay.portlet.wiki.PageVersionException;
60  import com.liferay.portlet.wiki.model.WikiNode;
61  import com.liferay.portlet.wiki.model.WikiPage;
62  import com.liferay.portlet.wiki.model.WikiPageDisplay;
63  import com.liferay.portlet.wiki.model.WikiPageResource;
64  import com.liferay.portlet.wiki.model.impl.WikiPageDisplayImpl;
65  import com.liferay.portlet.wiki.model.impl.WikiPageImpl;
66  import com.liferay.portlet.wiki.service.base.WikiPageLocalServiceBaseImpl;
67  import com.liferay.portlet.wiki.social.WikiActivityKeys;
68  import com.liferay.portlet.wiki.util.Indexer;
69  import com.liferay.portlet.wiki.util.WikiCacheThreadLocal;
70  import com.liferay.portlet.wiki.util.WikiCacheUtil;
71  import com.liferay.portlet.wiki.util.WikiUtil;
72  import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
73  import com.liferay.util.MathUtil;
74  import com.liferay.util.UniqueList;
75  
76  import java.rmi.RemoteException;
77  
78  import java.util.ArrayList;
79  import java.util.Calendar;
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         // Indexer
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         // 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         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         return ListUtil.sort(links);
506     }
507 
508     public List<WikiPage> getNoAssetPages() throws SystemException {
509         return wikiPageFinder.findByNoAssets();
510     }
511 
512     public List<WikiPage> getOrphans(long nodeId)
513         throws PortalException, SystemException {
514 
515         List<Map<String, Boolean>> pageTitles =
516             new ArrayList<Map<String, Boolean>>();
517 
518         List<WikiPage> pages = wikiPagePersistence.findByN_H(nodeId, true);
519 
520         for (WikiPage page : pages) {
521             pageTitles.add(WikiCacheUtil.getOutgoingLinks(page));
522         }
523 
524         Set<WikiPage> notOrphans = new HashSet<WikiPage>();
525 
526         for (WikiPage page : pages) {
527             for (Map<String, Boolean> pageTitle : pageTitles) {
528                 if (pageTitle.get(page.getTitle()) != null) {
529                     notOrphans.add(page);
530 
531                     break;
532                 }
533             }
534         }
535 
536         List<WikiPage> orphans = new ArrayList<WikiPage>();
537 
538         for (WikiPage page : pages) {
539             if (!notOrphans.contains(page)) {
540                 orphans.add(page);
541             }
542         }
543 
544         orphans = ListUtil.sort(orphans);
545 
546         return orphans;
547     }
548 
549     public List<WikiPage> getOutgoingLinks(long nodeId, String title)
550         throws PortalException, SystemException {
551 
552         WikiPage page = getPage(nodeId, title);
553 
554         Map<String, WikiPage> pages = new LinkedHashMap<String, WikiPage>();
555 
556         Map<String, Boolean> links = WikiCacheUtil.getOutgoingLinks(page);
557 
558         for (String curTitle : links.keySet()) {
559             Boolean exists = links.get(curTitle);
560 
561             if (exists) {
562                 if (!pages.containsKey(curTitle)) {
563                     pages.put(curTitle, getPage(nodeId, curTitle));
564                 }
565             }
566             else {
567                 WikiPageImpl newPage = new WikiPageImpl();
568 
569                 newPage.setNew(true);
570                 newPage.setNodeId(nodeId);
571                 newPage.setTitle(curTitle);
572 
573                 if (!pages.containsKey(curTitle)) {
574                     pages.put(curTitle, newPage);
575                 }
576             }
577         }
578 
579         return ListUtil.fromCollection(pages.values());
580     }
581 
582     public WikiPage getPage(long nodeId, String title)
583         throws PortalException, SystemException {
584 
585         List<WikiPage> pages = wikiPagePersistence.findByN_T_H(
586             nodeId, title, true, 0, 1);
587 
588         if (pages.size() > 0) {
589             return pages.get(0);
590         }
591         else {
592             throw new NoSuchPageException();
593         }
594     }
595 
596     public WikiPage getPage(long nodeId, String title, double version)
597         throws PortalException, SystemException {
598 
599         WikiPage page = null;
600 
601         if (version == 0) {
602             page = getPage(nodeId, title);
603         }
604         else {
605             page = wikiPagePersistence.findByN_T_V(nodeId, title, version);
606         }
607 
608         return page;
609     }
610 
611     public WikiPageDisplay getPageDisplay(
612             long nodeId, String title, PortletURL viewPageURL,
613             PortletURL editPageURL, String attachmentURLPrefix)
614         throws PortalException, SystemException {
615 
616         WikiPage page = getPage(nodeId, title);
617 
618         String formattedContent = WikiUtil.convert(
619             page, viewPageURL, editPageURL, attachmentURLPrefix);
620 
621         return new WikiPageDisplayImpl(
622             page.getUserId(), page.getNodeId(), page.getTitle(),
623             page.getVersion(), page.getContent(), formattedContent,
624             page.getFormat(), page.getHead(), page.getAttachmentsFiles());
625     }
626 
627     public List<WikiPage> getPages(long nodeId, int start, int end)
628         throws SystemException {
629 
630         return wikiPagePersistence.findByNodeId(
631             nodeId, start, end, new PageCreateDateComparator(false));
632     }
633 
634     public List<WikiPage> getPages(String format) throws SystemException {
635         return wikiPagePersistence.findByFormat(format);
636     }
637 
638     public List<WikiPage> getPages(
639             long nodeId, String title, int start, int end)
640         throws SystemException {
641 
642         return wikiPagePersistence.findByN_T(
643             nodeId, title, start, end, new PageCreateDateComparator(false));
644     }
645 
646     public List<WikiPage> getPages(
647             long nodeId, String title, int start, int end,
648             OrderByComparator obc)
649         throws SystemException {
650 
651         return wikiPagePersistence.findByN_T(nodeId, title, start, end, obc);
652     }
653 
654     public List<WikiPage> getPages(
655             long nodeId, boolean head, int start, int end)
656         throws SystemException {
657 
658         return wikiPagePersistence.findByN_H(
659             nodeId, head, start, end, new PageCreateDateComparator(false));
660     }
661 
662     public List<WikiPage> getPages(
663             long nodeId, String title, boolean head, int start, int end)
664         throws SystemException {
665 
666         return wikiPagePersistence.findByN_T_H(
667             nodeId, title, head, start, end,
668             new PageCreateDateComparator(false));
669     }
670 
671     public int getPagesCount(long nodeId) throws SystemException {
672         return wikiPagePersistence.countByNodeId(nodeId);
673     }
674 
675     public int getPagesCount(long nodeId, String title)
676         throws SystemException {
677 
678         return wikiPagePersistence.countByN_T(nodeId, title);
679     }
680 
681     public int getPagesCount(long nodeId, boolean head)
682         throws SystemException {
683 
684         return wikiPagePersistence.countByN_H(nodeId, head);
685     }
686 
687     public int getPagesCount(long nodeId, String title, boolean head)
688         throws SystemException {
689 
690         return wikiPagePersistence.countByN_T_H(nodeId, title, head);
691     }
692 
693     public List<WikiPage> getRecentChanges(long nodeId, int start, int end)
694         throws SystemException {
695 
696         Calendar cal = CalendarFactoryUtil.getCalendar();
697 
698         cal.add(Calendar.WEEK_OF_YEAR, -1);
699 
700         return wikiPageFinder.findByCreateDate(
701             nodeId, cal.getTime(), false, start, end);
702     }
703 
704     public int getRecentChangesCount(long nodeId) throws SystemException {
705         Calendar cal = CalendarFactoryUtil.getCalendar();
706 
707         cal.add(Calendar.WEEK_OF_YEAR, -1);
708 
709         return wikiPageFinder.countByCreateDate(nodeId, cal.getTime(), false);
710     }
711 
712     public void movePage(
713             long userId, long nodeId, String title, String newTitle,
714             PortletPreferences prefs, ThemeDisplay themeDisplay)
715         throws PortalException, SystemException {
716 
717         movePage(userId, nodeId, title, newTitle, true, prefs, themeDisplay);
718     }
719 
720     public void movePage(
721             long userId, long nodeId, String title, String newTitle,
722             boolean strict, PortletPreferences prefs, ThemeDisplay themeDisplay)
723         throws PortalException, SystemException {
724 
725         validateTitle(newTitle);
726 
727         // Check if the new title already exists
728 
729         if (isUsedTitle(nodeId, newTitle)) {
730             WikiPage page = getPage(nodeId, newTitle);
731 
732             // Support moving back to a previously moved title
733 
734             if (((page.getVersion() == WikiPageImpl.DEFAULT_VERSION) &&
735                  (page.getContent().length() < 200)) ||
736                 !strict) {
737 
738                 deletePage(nodeId, newTitle);
739             }
740             else {
741                 throw new DuplicatePageException(newTitle);
742             }
743         }
744 
745         // All versions
746 
747         List<WikiPage> pageVersions = wikiPagePersistence.findByN_T(
748             nodeId, title);
749 
750         if (pageVersions.size() == 0) {
751             return;
752         }
753 
754         for (WikiPage page : pageVersions) {
755             page.setTitle(newTitle);
756 
757             wikiPagePersistence.update(page, false);
758         }
759 
760         // Children
761 
762         List<WikiPage> children = wikiPagePersistence.findByN_P(nodeId, title);
763 
764         for (WikiPage page : children) {
765             page.setParentTitle(newTitle);
766 
767             wikiPagePersistence.update(page, false);
768         }
769 
770         WikiPage page = pageVersions.get(pageVersions.size() - 1);
771 
772         // Page resource
773 
774         WikiPageResource wikiPageResource =
775             wikiPageResourcePersistence.findByPrimaryKey(
776                 page.getResourcePrimKey());
777 
778         wikiPageResource.setTitle(newTitle);
779 
780         wikiPageResourcePersistence.update(wikiPageResource, false);
781 
782         // Create stub page at the old location
783 
784         String uuid = null;
785         double version = WikiPageImpl.DEFAULT_VERSION;
786         String format = page.getFormat();
787         boolean head = true;
788         String parentTitle = page.getParentTitle();
789         String redirectTitle = page.getTitle();
790         String content =
791             StringPool.DOUBLE_OPEN_BRACKET + redirectTitle +
792                 StringPool.DOUBLE_CLOSE_BRACKET;
793         String summary = WikiPageImpl.MOVED + " to " + title;
794 
795         addPage(
796             uuid, userId, nodeId, title, version, content, summary, false,
797             format, head, parentTitle, redirectTitle, null, prefs,
798             themeDisplay);
799 
800         // Move redirects to point to the page with the new title
801 
802         List<WikiPage> redirectedPages = wikiPagePersistence.findByN_R(
803             nodeId, title);
804 
805         for (WikiPage redirectedPage : redirectedPages) {
806             redirectedPage.setRedirectTitle(newTitle);
807 
808             wikiPagePersistence.update(redirectedPage, false);
809         }
810 
811         // Tags
812 
813         String[] tagsEntries = tagsEntryLocalService.getEntryNames(
814             WikiPage.class.getName(), page.getResourcePrimKey());
815 
816         updateTagsAsset(userId, page, tagsEntries);
817 
818         // Indexer
819 
820         try {
821             Indexer.updatePage(
822                 page.getCompanyId(), page.getNode().getGroupId(), nodeId,
823                 newTitle, content, tagsEntries);
824         }
825         catch (SearchException se) {
826             _log.error("Indexing " + newTitle, se);
827         }
828     }
829 
830     public WikiPage revertPage(
831             long userId, long nodeId, String title, double version,
832             PortletPreferences prefs, ThemeDisplay themeDisplay)
833         throws PortalException, SystemException {
834 
835         WikiPage oldPage = getPage(nodeId, title, version);
836 
837         return updatePage(
838             userId, nodeId, title, 0, oldPage.getContent(),
839             WikiPageImpl.REVERTED + " to " + version, false,
840             oldPage.getFormat(), null, oldPage.getRedirectTitle(), null, prefs,
841             themeDisplay);
842     }
843 
844     public void subscribePage(long userId, long nodeId, String title)
845         throws PortalException, SystemException {
846 
847         WikiPage page = getPage(nodeId, title);
848 
849         subscriptionLocalService.addSubscription(
850             userId, WikiPage.class.getName(), page.getResourcePrimKey());
851     }
852 
853     public void unsubscribePage(long userId, long nodeId, String title)
854         throws PortalException, SystemException {
855 
856         WikiPage page = getPage(nodeId, title);
857 
858         subscriptionLocalService.deleteSubscription(
859             userId, WikiPage.class.getName(), page.getResourcePrimKey());
860     }
861 
862     public WikiPage updatePage(
863             long userId, long nodeId, String title, double version,
864             String content, String summary, boolean minorEdit, String format,
865             String parentTitle, String redirectTitle, String[] tagsEntries,
866             PortletPreferences prefs, ThemeDisplay themeDisplay)
867         throws PortalException, SystemException {
868 
869         // Page
870 
871         User user = userPersistence.findByPrimaryKey(userId);
872         Date now = new Date();
873 
874         validate(nodeId, content, format);
875 
876         WikiPage page = null;
877 
878         try {
879             page = getPage(nodeId, title);
880         }
881         catch (NoSuchPageException nspe) {
882             return addPage(
883                 null, userId, nodeId, title, WikiPageImpl.DEFAULT_VERSION,
884                 content, summary, minorEdit, format, true, parentTitle,
885                 redirectTitle, tagsEntries, prefs, themeDisplay);
886         }
887 
888         double oldVersion = page.getVersion();
889 
890         if ((version > 0) && (version != oldVersion)) {
891             throw new PageVersionException();
892         }
893 
894         long resourcePrimKey = page.getResourcePrimKey();
895 
896         page.setHead(false);
897         page.setModifiedDate(now);
898 
899         wikiPagePersistence.update(page, false);
900 
901         double newVersion = MathUtil.format(oldVersion + 0.1, 1, 1);
902 
903         long pageId = counterLocalService.increment();
904 
905         page = wikiPagePersistence.create(pageId);
906 
907         page.setResourcePrimKey(resourcePrimKey);
908         page.setCompanyId(user.getCompanyId());
909         page.setUserId(user.getUserId());
910         page.setUserName(user.getFullName());
911         page.setCreateDate(now);
912         page.setModifiedDate(now);
913         page.setNodeId(nodeId);
914         page.setTitle(title);
915         page.setVersion(newVersion);
916         page.setMinorEdit(minorEdit);
917         page.setContent(content);
918         page.setSummary(summary);
919         page.setFormat(format);
920         page.setHead(true);
921 
922         if (Validator.isNotNull(parentTitle)) {
923             page.setParentTitle(parentTitle);
924         }
925 
926         if (Validator.isNotNull(redirectTitle)) {
927             page.setRedirectTitle(redirectTitle);
928         }
929 
930         wikiPagePersistence.update(page, false);
931 
932         // Node
933 
934         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
935 
936         node.setLastPostDate(now);
937 
938         wikiNodePersistence.update(node, false);
939 
940         // Social
941 
942         socialActivityLocalService.addActivity(
943             userId, node.getGroupId(), WikiPage.class.getName(),
944             page.getResourcePrimKey(), WikiActivityKeys.UPDATE_PAGE,
945             StringPool.BLANK, 0);
946 
947         // Subscriptions
948 
949         if (!minorEdit && NotificationThreadLocal.isNotificationEnabled()) {
950             notifySubscribers(node, page, prefs, themeDisplay, true);
951         }
952 
953         // Tags
954 
955         updateTagsAsset(userId, page, tagsEntries);
956 
957         // Indexer
958 
959         try {
960             Indexer.updatePage(
961                 node.getCompanyId(), node.getGroupId(), nodeId, title, content,
962                 tagsEntries);
963         }
964         catch (SearchException se) {
965             _log.error("Indexing " + page.getPrimaryKey(), se);
966         }
967 
968         // Cache
969 
970         clearPageCache(page);
971 
972         return page;
973     }
974 
975     public void updateTagsAsset(
976             long userId, WikiPage page, String[] tagsEntries)
977         throws PortalException, SystemException {
978 
979         tagsAssetLocalService.updateAsset(
980             userId, page.getNode().getGroupId(), WikiPage.class.getName(),
981             page.getResourcePrimKey(), tagsEntries, null, null, null, null,
982             ContentTypes.TEXT_HTML, page.getTitle(), null, null, null, 0, 0,
983             null, false);
984     }
985 
986     public void validateTitle(String title) throws PortalException {
987         if (title.equals("all_pages") || title.equals("orphan_pages") ||
988             title.equals("recent_changes")) {
989 
990             throw new PageTitleException(title + " is reserved");
991         }
992 
993         if (Validator.isNotNull(PropsValues.WIKI_PAGE_TITLES_REGEXP)) {
994             Pattern pattern = Pattern.compile(
995                 PropsValues.WIKI_PAGE_TITLES_REGEXP);
996 
997             Matcher matcher = pattern.matcher(title);
998 
999             if (!matcher.matches()) {
1000                throw new PageTitleException();
1001            }
1002        }
1003    }
1004
1005    protected void clearPageCache(WikiPage page) {
1006        if (!WikiCacheThreadLocal.isClearCache()) {
1007            return;
1008        }
1009
1010        WikiCacheUtil.clearCache(page.getNodeId(), page.getTitle());
1011    }
1012
1013    protected void clearReferralsCache(WikiPage page)
1014        throws PortalException, SystemException {
1015
1016        if (!WikiCacheThreadLocal.isClearCache()) {
1017            return;
1018        }
1019
1020        List<WikiPage> links = getIncomingLinks(
1021            page.getNodeId(), page.getTitle());
1022
1023        for (WikiPage curPage : links) {
1024            WikiCacheUtil.clearCache(curPage.getNodeId(), curPage.getTitle());
1025        }
1026    }
1027
1028    protected boolean isLinkedTo(WikiPage page, String targetTitle)
1029        throws PortalException {
1030
1031        Map<String, Boolean> links = WikiCacheUtil.getOutgoingLinks(page);
1032
1033        Boolean link = links.get(targetTitle);
1034
1035        if (link != null) {
1036            return true;
1037        }
1038        else {
1039            return false;
1040        }
1041    }
1042
1043    protected boolean isUsedTitle(long nodeId, String title)
1044        throws SystemException {
1045
1046        if (getPagesCount(nodeId, title, true) > 0) {
1047            return true;
1048        }
1049        else {
1050            return false;
1051        }
1052    }
1053
1054    protected void notifySubscribers(
1055            WikiNode node, WikiPage page, PortletPreferences prefs,
1056            ThemeDisplay themeDisplay, boolean update)
1057        throws PortalException, SystemException {
1058
1059        if (prefs == null) {
1060            long ownerId = node.getGroupId();
1061            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
1062            long plid = PortletKeys.PREFS_PLID_SHARED;
1063            String portletId = PortletKeys.WIKI;
1064            String defaultPreferences = null;
1065
1066            prefs = portletPreferencesLocalService.getPreferences(
1067                node.getCompanyId(), ownerId, ownerType, plid, portletId,
1068                defaultPreferences);
1069        }
1070
1071        if (!update && WikiUtil.getEmailPageAddedEnabled(prefs)) {
1072        }
1073        else if (update && WikiUtil.getEmailPageUpdatedEnabled(prefs)) {
1074        }
1075        else {
1076            return;
1077        }
1078
1079        Company company = companyPersistence.findByPrimaryKey(
1080            page.getCompanyId());
1081
1082        Group group = groupPersistence.findByPrimaryKey(node.getGroupId());
1083
1084        User user = userPersistence.findByPrimaryKey(page.getUserId());
1085
1086        String pageURL = StringPool.BLANK;
1087
1088        if (themeDisplay != null) {
1089            String portalURL = PortalUtil.getPortalURL(themeDisplay);
1090            String layoutURL = PortalUtil.getLayoutURL(themeDisplay);
1091
1092            pageURL =
1093                portalURL + layoutURL + "/-/wiki/" + node.getNodeId() + "/" +
1094                    HttpUtil.encodeURL(page.getTitle());
1095        }
1096
1097        String portletName = PortalUtil.getPortletTitle(
1098            PortletKeys.WIKI, user);
1099
1100        String fromName = WikiUtil.getEmailFromName(prefs);
1101        String fromAddress = WikiUtil.getEmailFromAddress(prefs);
1102
1103        String replyToAddress = fromAddress;
1104        String mailId = WikiUtil.getMailId(
1105            company.getMx(), page.getNodeId(), page.getPageId());
1106
1107        fromName = StringUtil.replace(
1108            fromName,
1109            new String[] {
1110                "[$COMPANY_ID$]",
1111                "[$COMPANY_MX$]",
1112                "[$COMPANY_NAME$]",
1113                "[$COMMUNITY_NAME$]",
1114                "[$PAGE_USER_ADDRESS$]",
1115                "[$PAGE_USER_NAME$]",
1116                "[$PORTLET_NAME$]"
1117            },
1118            new String[] {
1119                String.valueOf(company.getCompanyId()),
1120                company.getMx(),
1121                company.getName(),
1122                group.getName(),
1123                user.getEmailAddress(),
1124                user.getFullName(),
1125                portletName
1126            });
1127
1128        fromAddress = StringUtil.replace(
1129            fromAddress,
1130            new String[] {
1131                "[$COMPANY_ID$]",
1132                "[$COMPANY_MX$]",
1133                "[$COMPANY_NAME$]",
1134                "[$COMMUNITY_NAME$]",
1135                "[$PAGE_USER_ADDRESS$]",
1136                "[$PAGE_USER_NAME$]",
1137                "[$PORTLET_NAME$]"
1138            },
1139            new String[] {
1140                String.valueOf(company.getCompanyId()),
1141                company.getMx(),
1142                company.getName(),
1143                group.getName(),
1144                user.getEmailAddress(),
1145                user.getFullName(),
1146                portletName
1147            });
1148
1149        String subjectPrefix = null;
1150        String body = null;
1151        String signature = null;
1152
1153        if (update) {
1154            subjectPrefix = WikiUtil.getEmailPageUpdatedSubjectPrefix(prefs);
1155            body = WikiUtil.getEmailPageUpdatedBody(prefs);
1156            signature = WikiUtil.getEmailPageUpdatedSignature(prefs);
1157        }
1158        else {
1159            subjectPrefix = WikiUtil.getEmailPageAddedSubjectPrefix(prefs);
1160            body = WikiUtil.getEmailPageAddedBody(prefs);
1161            signature = WikiUtil.getEmailPageAddedSignature(prefs);
1162        }
1163
1164        if (Validator.isNotNull(signature)) {
1165            body +=  "\n--\n" + signature;
1166        }
1167
1168        subjectPrefix = StringUtil.replace(
1169            subjectPrefix,
1170            new String[] {
1171                "[$COMPANY_ID$]",
1172                "[$COMPANY_MX$]",
1173                "[$COMPANY_NAME$]",
1174                "[$COMMUNITY_NAME$]",
1175                "[$FROM_ADDRESS$]",
1176                "[$FROM_NAME$]",
1177                "[$NODE_NAME$]",
1178                "[$PAGE_CONTENT$]",
1179                "[$PAGE_ID$]",
1180                "[$PAGE_TITLE$]",
1181                "[$PAGE_USER_ADDRESS$]",
1182                "[$PAGE_USER_NAME$]",
1183                "[$PORTAL_URL$]",
1184                "[$PORTLET_NAME$]"
1185            },
1186            new String[] {
1187                String.valueOf(company.getCompanyId()),
1188                company.getMx(),
1189                company.getName(),
1190                group.getName(),
1191                fromAddress,
1192                fromName,
1193                node.getName(),
1194                page.getContent(),
1195                String.valueOf(page.getPageId()),
1196                page.getTitle(),
1197                user.getEmailAddress(),
1198                user.getFullName(),
1199                company.getVirtualHost(),
1200                portletName
1201            });
1202
1203        body = StringUtil.replace(
1204            body,
1205            new String[] {
1206                "[$COMPANY_ID$]",
1207                "[$COMPANY_MX$]",
1208                "[$COMPANY_NAME$]",
1209                "[$COMMUNITY_NAME$]",
1210                "[$FROM_ADDRESS$]",
1211                "[$FROM_NAME$]",
1212                "[$NODE_NAME$]",
1213                "[$PAGE_CONTENT$]",
1214                "[$PAGE_ID$]",
1215                "[$PAGE_TITLE$]",
1216                "[$PAGE_URL$]",
1217                "[$PAGE_USER_ADDRESS$]",
1218                "[$PAGE_USER_NAME$]",
1219                "[$PORTAL_URL$]",
1220                "[$PORTLET_NAME$]"
1221            },
1222            new String[] {
1223                String.valueOf(company.getCompanyId()),
1224                company.getMx(),
1225                company.getName(),
1226                group.getName(),
1227                fromAddress,
1228                fromName,
1229                node.getName(),
1230                page.getContent(),
1231                String.valueOf(page.getPageId()),
1232                page.getTitle(),
1233                pageURL,
1234                user.getEmailAddress(),
1235                user.getFullName(),
1236                company.getVirtualHost(),
1237                portletName
1238            });
1239
1240        String subject = page.getTitle();
1241
1242        if (subject.indexOf(subjectPrefix) == -1) {
1243            subject = subjectPrefix + subject;
1244        }
1245
1246        Message message = new Message();
1247
1248        message.put("companyId", node.getCompanyId());
1249        message.put("userId", node.getUserId());
1250        message.put("nodeId", node.getNodeId());
1251        message.put("pageResourcePrimKey", page.getResourcePrimKey());
1252        message.put("fromName", fromName);
1253        message.put("fromAddress", fromAddress);
1254        message.put("subject", subject);
1255        message.put("body", body);
1256        message.put("replyToAddress", replyToAddress);
1257        message.put("mailId", mailId);
1258
1259        MessageBusUtil.sendMessage(DestinationNames.WIKI, message);
1260    }
1261
1262    protected void validate(long nodeId, String content, String format)
1263        throws PortalException {
1264
1265        if (!WikiUtil.validate(nodeId, content, format)) {
1266            throw new PageContentException();
1267        }
1268    }
1269
1270    protected void validate(
1271            String title, long nodeId, String content, String format)
1272        throws PortalException {
1273
1274        if (Validator.isNull(title)) {
1275            throw new PageTitleException();
1276        }
1277
1278        validateTitle(title);
1279
1280        validate(nodeId, content, format);
1281    }
1282
1283    private static Log _log = LogFactory.getLog(WikiPageLocalServiceImpl.class);
1284
1285}