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