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