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