1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights 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  * @author Wesley Gong
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         if (Validator.isNotNull(newParentTitle)) {
315             WikiPage parentPage = getPage(nodeId, newParentTitle);
316 
317             if (Validator.isNotNull(parentPage.getRedirectTitle())) {
318                 newParentTitle = parentPage.getRedirectTitle();
319             }
320         }
321 
322         WikiPage page = getPage(nodeId, title);
323 
324         String originalParentTitle = page.getParentTitle();
325 
326         double version = page.getVersion();
327         String content = page.getContent();
328         String summary = themeDisplay.translate(
329             "changed-parent-from-x", originalParentTitle);
330         boolean minorEdit = false;
331         String format = page.getFormat();
332         String redirectTitle = page.getRedirectTitle();
333         String[] tagsEntries = tagsEntryLocalService.getEntryNames(
334             WikiPage.class.getName(), page.getResourcePrimKey());
335 
336         updatePage(
337             userId, nodeId, title, version, content, summary, minorEdit,
338             format, newParentTitle, redirectTitle, tagsEntries, prefs,
339             themeDisplay);
340 
341         List<WikiPage> oldPages = wikiPagePersistence.findByN_T_H(
342             nodeId, title, false);
343 
344         for (WikiPage oldPage : oldPages) {
345             oldPage.setParentTitle(originalParentTitle);
346 
347             wikiPagePersistence.update(oldPage, false);
348         }
349     }
350 
351     public void deletePage(long nodeId, String title)
352         throws PortalException, SystemException {
353 
354         List<WikiPage> pages = wikiPagePersistence.findByN_T_H(
355             nodeId, title, true, 0, 1);
356 
357         if (pages.size() > 0) {
358             WikiPage page = pages.iterator().next();
359 
360             deletePage(page);
361         }
362     }
363 
364     public void deletePage(WikiPage page)
365         throws PortalException, SystemException {
366 
367         // Children
368 
369         List<WikiPage> children = wikiPagePersistence.findByN_H_P(
370             page.getNodeId(), true, page.getTitle());
371 
372         for (WikiPage curPage : children) {
373             deletePage(curPage);
374         }
375 
376         // Indexer
377 
378         try {
379             Indexer.deletePage(
380                 page.getCompanyId(), page.getNodeId(), page.getTitle());
381         }
382         catch (SearchException se) {
383             _log.error("Deleting index " + page.getPrimaryKey(), se);
384         }
385 
386         // Attachments
387 
388         long companyId = page.getCompanyId();
389         String portletId = CompanyConstants.SYSTEM_STRING;
390         long repositoryId = CompanyConstants.SYSTEM;
391         String dirName = page.getAttachmentsDir();
392 
393         try {
394             dlService.deleteDirectory(
395                 companyId, portletId, repositoryId, dirName);
396         }
397         catch (NoSuchDirectoryException nsde) {
398         }
399         catch (RemoteException re) {
400             throw new SystemException(re);
401         }
402 
403         // Tags
404 
405         tagsAssetLocalService.deleteAsset(
406             WikiPage.class.getName(), page.getResourcePrimKey());
407 
408         // Subscriptions
409 
410         subscriptionLocalService.deleteSubscriptions(
411             page.getCompanyId(), WikiPage.class.getName(), page.getPageId());
412 
413         // Social
414 
415         socialActivityLocalService.deleteActivities(
416             WikiPage.class.getName(), page.getResourcePrimKey());
417 
418         // Message boards
419 
420         mbMessageLocalService.deleteDiscussionMessages(
421             WikiPage.class.getName(), page.getResourcePrimKey());
422 
423         // Resources
424 
425         resourceLocalService.deleteResource(
426             page.getCompanyId(), WikiPage.class.getName(),
427             ResourceConstants.SCOPE_INDIVIDUAL, page.getResourcePrimKey());
428 
429         // Resource
430 
431         wikiPageResourceLocalService.deletePageResource(
432             page.getNodeId(), page.getTitle());
433 
434         // All versions
435 
436         wikiPagePersistence.removeByN_T(page.getNodeId(), page.getTitle());
437 
438         // All referrals
439 
440         wikiPagePersistence.removeByN_R(page.getNodeId(), page.getTitle());
441 
442         // Cache
443 
444         clearPageCache(page);
445         clearReferralsCache(page);
446     }
447 
448     public void deletePageAttachment(long nodeId, String title, String fileName)
449         throws PortalException, SystemException {
450 
451         if (Validator.isNull(fileName)) {
452             return;
453         }
454 
455         WikiPage page = getPage(nodeId, title);
456 
457         long companyId = page.getCompanyId();
458         String portletId = CompanyConstants.SYSTEM_STRING;
459         long repositoryId = CompanyConstants.SYSTEM;
460 
461         try {
462             dlService.deleteFile(companyId, portletId, repositoryId, fileName);
463         }
464         catch (NoSuchFileException nsfe) {
465         }
466         catch (RemoteException re) {
467             throw new SystemException(re);
468         }
469     }
470 
471     public void deletePages(long nodeId)
472         throws PortalException, SystemException {
473 
474         Iterator<WikiPage> itr = wikiPagePersistence.findByN_H_P(
475             nodeId, true, StringPool.BLANK).iterator();
476 
477         while (itr.hasNext()) {
478             WikiPage page = itr.next();
479 
480             deletePage(page);
481         }
482     }
483 
484     public List<WikiPage> getChildren(
485             long nodeId, boolean head, String parentTitle)
486         throws SystemException {
487 
488         return wikiPagePersistence.findByN_H_P(nodeId, head, parentTitle);
489     }
490 
491     public List<WikiPage> getIncomingLinks(long nodeId, String title)
492         throws PortalException, SystemException {
493 
494         List<WikiPage> links = new UniqueList<WikiPage>();
495 
496         List<WikiPage> pages = wikiPagePersistence.findByN_H(nodeId, true);
497 
498         for (WikiPage page : pages) {
499             if (isLinkedTo(page, title)) {
500                 links.add(page);
501             }
502         }
503 
504         List<WikiPage> referrals = wikiPagePersistence.findByN_R(nodeId, title);
505 
506         for (WikiPage referral : referrals) {
507             for (WikiPage page : pages) {
508                 if (isLinkedTo(page, referral.getTitle())) {
509                     links.add(page);
510                 }
511             }
512         }
513 
514         return ListUtil.sort(links);
515     }
516 
517     public List<WikiPage> getNoAssetPages() throws SystemException {
518         return wikiPageFinder.findByNoAssets();
519     }
520 
521     public List<WikiPage> getOrphans(long nodeId)
522         throws PortalException, SystemException {
523 
524         List<Map<String, Boolean>> pageTitles =
525             new ArrayList<Map<String, Boolean>>();
526 
527         List<WikiPage> pages = wikiPagePersistence.findByN_H(nodeId, true);
528 
529         for (WikiPage page : pages) {
530             pageTitles.add(WikiCacheUtil.getOutgoingLinks(page));
531         }
532 
533         Set<WikiPage> notOrphans = new HashSet<WikiPage>();
534 
535         for (WikiPage page : pages) {
536             for (Map<String, Boolean> pageTitle : pageTitles) {
537                 if (pageTitle.get(page.getTitle()) != null) {
538                     notOrphans.add(page);
539 
540                     break;
541                 }
542             }
543         }
544 
545         List<WikiPage> orphans = new ArrayList<WikiPage>();
546 
547         for (WikiPage page : pages) {
548             if (!notOrphans.contains(page)) {
549                 orphans.add(page);
550             }
551         }
552 
553         orphans = ListUtil.sort(orphans);
554 
555         return orphans;
556     }
557 
558     public List<WikiPage> getOutgoingLinks(long nodeId, String title)
559         throws PortalException, SystemException {
560 
561         WikiPage page = getPage(nodeId, title);
562 
563         Map<String, WikiPage> pages = new LinkedHashMap<String, WikiPage>();
564 
565         Map<String, Boolean> links = WikiCacheUtil.getOutgoingLinks(page);
566 
567         for (String curTitle : links.keySet()) {
568             Boolean exists = links.get(curTitle);
569 
570             if (exists) {
571                 if (!pages.containsKey(curTitle)) {
572                     pages.put(curTitle, getPage(nodeId, curTitle));
573                 }
574             }
575             else {
576                 WikiPageImpl newPage = new WikiPageImpl();
577 
578                 newPage.setNew(true);
579                 newPage.setNodeId(nodeId);
580                 newPage.setTitle(curTitle);
581 
582                 if (!pages.containsKey(curTitle)) {
583                     pages.put(curTitle, newPage);
584                 }
585             }
586         }
587 
588         return ListUtil.fromCollection(pages.values());
589     }
590 
591     public WikiPage getPage(long resourcePrimKey)
592         throws PortalException, SystemException {
593 
594         WikiPageResource wikiPageResource =
595             wikiPageResourceLocalService.getPageResource(resourcePrimKey);
596 
597         return getPage(
598             wikiPageResource.getNodeId(), wikiPageResource.getTitle());
599     }
600 
601     public WikiPage getPage(long nodeId, String title)
602         throws PortalException, SystemException {
603 
604         List<WikiPage> pages = wikiPagePersistence.findByN_T_H(
605             nodeId, title, true, 0, 1);
606 
607         if (pages.size() > 0) {
608             return pages.get(0);
609         }
610         else {
611             throw new NoSuchPageException();
612         }
613     }
614 
615     public WikiPage getPage(long nodeId, String title, double version)
616         throws PortalException, SystemException {
617 
618         WikiPage page = null;
619 
620         if (version == 0) {
621             page = getPage(nodeId, title);
622         }
623         else {
624             page = wikiPagePersistence.findByN_T_V(nodeId, title, version);
625         }
626 
627         return page;
628     }
629 
630     public WikiPageDisplay getPageDisplay(
631             long nodeId, String title, PortletURL viewPageURL,
632             PortletURL editPageURL, String attachmentURLPrefix)
633         throws PortalException, SystemException {
634 
635         WikiPage page = getPage(nodeId, title);
636 
637         String formattedContent = WikiUtil.convert(
638             page, viewPageURL, editPageURL, attachmentURLPrefix);
639 
640         return new WikiPageDisplayImpl(
641             page.getUserId(), page.getNodeId(), page.getTitle(),
642             page.getVersion(), page.getContent(), formattedContent,
643             page.getFormat(), page.getHead(), page.getAttachmentsFiles());
644     }
645 
646     public List<WikiPage> getPages(long nodeId, int start, int end)
647         throws SystemException {
648 
649         return wikiPagePersistence.findByNodeId(
650             nodeId, start, end, new PageCreateDateComparator(false));
651     }
652 
653     public List<WikiPage> getPages(String format) throws SystemException {
654         return wikiPagePersistence.findByFormat(format);
655     }
656 
657     public List<WikiPage> getPages(
658             long nodeId, String title, int start, int end)
659         throws SystemException {
660 
661         return wikiPagePersistence.findByN_T(
662             nodeId, title, start, end, new PageCreateDateComparator(false));
663     }
664 
665     public List<WikiPage> getPages(
666             long nodeId, String title, int start, int end,
667             OrderByComparator obc)
668         throws SystemException {
669 
670         return wikiPagePersistence.findByN_T(nodeId, title, start, end, obc);
671     }
672 
673     public List<WikiPage> getPages(
674             long nodeId, boolean head, int start, int end)
675         throws SystemException {
676 
677         return wikiPagePersistence.findByN_H(
678             nodeId, head, start, end, new PageCreateDateComparator(false));
679     }
680 
681     public List<WikiPage> getPages(
682             long nodeId, String title, boolean head, int start, int end)
683         throws SystemException {
684 
685         return wikiPagePersistence.findByN_T_H(
686             nodeId, title, head, start, end,
687             new PageCreateDateComparator(false));
688     }
689 
690     public int getPagesCount(long nodeId) throws SystemException {
691         return wikiPagePersistence.countByNodeId(nodeId);
692     }
693 
694     public int getPagesCount(long nodeId, String title)
695         throws SystemException {
696 
697         return wikiPagePersistence.countByN_T(nodeId, title);
698     }
699 
700     public int getPagesCount(long nodeId, boolean head)
701         throws SystemException {
702 
703         return wikiPagePersistence.countByN_H(nodeId, head);
704     }
705 
706     public int getPagesCount(long nodeId, String title, boolean head)
707         throws SystemException {
708 
709         return wikiPagePersistence.countByN_T_H(nodeId, title, head);
710     }
711 
712     public int getPagesCount(String format) throws SystemException {
713         return wikiPagePersistence.countByFormat(format);
714     }
715 
716     public List<WikiPage> getRecentChanges(long nodeId, int start, int end)
717         throws SystemException {
718 
719         Calendar cal = CalendarFactoryUtil.getCalendar();
720 
721         cal.add(Calendar.WEEK_OF_YEAR, -1);
722 
723         return wikiPageFinder.findByCreateDate(
724             nodeId, cal.getTime(), false, start, end);
725     }
726 
727     public int getRecentChangesCount(long nodeId) throws SystemException {
728         Calendar cal = CalendarFactoryUtil.getCalendar();
729 
730         cal.add(Calendar.WEEK_OF_YEAR, -1);
731 
732         return wikiPageFinder.countByCreateDate(nodeId, cal.getTime(), false);
733     }
734 
735     public void movePage(
736             long userId, long nodeId, String title, String newTitle,
737             PortletPreferences prefs, ThemeDisplay themeDisplay)
738         throws PortalException, SystemException {
739 
740         movePage(userId, nodeId, title, newTitle, true, prefs, themeDisplay);
741     }
742 
743     public void movePage(
744             long userId, long nodeId, String title, String newTitle,
745             boolean strict, PortletPreferences prefs, ThemeDisplay themeDisplay)
746         throws PortalException, SystemException {
747 
748         validateTitle(newTitle);
749 
750         // Check if the new title already exists
751 
752         if (title.equalsIgnoreCase(newTitle)) {
753             throw new DuplicatePageException(newTitle);
754         }
755 
756         if (isUsedTitle(nodeId, newTitle)) {
757             WikiPage page = getPage(nodeId, newTitle);
758 
759             // Support moving back to a previously moved title
760 
761             if (((page.getVersion() == WikiPageImpl.DEFAULT_VERSION) &&
762                  (page.getContent().length() < 200)) ||
763                 !strict) {
764 
765                 deletePage(nodeId, newTitle);
766             }
767             else {
768                 throw new DuplicatePageException(newTitle);
769             }
770         }
771 
772         // All versions
773 
774         List<WikiPage> pageVersions = wikiPagePersistence.findByN_T(
775             nodeId, title);
776 
777         if (pageVersions.size() == 0) {
778             return;
779         }
780 
781         for (WikiPage page : pageVersions) {
782             page.setTitle(newTitle);
783 
784             wikiPagePersistence.update(page, false);
785         }
786 
787         // Children
788 
789         List<WikiPage> children = wikiPagePersistence.findByN_P(nodeId, title);
790 
791         for (WikiPage page : children) {
792             page.setParentTitle(newTitle);
793 
794             wikiPagePersistence.update(page, false);
795         }
796 
797         WikiPage page = pageVersions.get(pageVersions.size() - 1);
798 
799         // Page resource
800 
801         WikiPageResource wikiPageResource =
802             wikiPageResourcePersistence.findByPrimaryKey(
803                 page.getResourcePrimKey());
804 
805         wikiPageResource.setTitle(newTitle);
806 
807         wikiPageResourcePersistence.update(wikiPageResource, false);
808 
809         // Create stub page at the old location
810 
811         String uuid = null;
812         double version = WikiPageImpl.DEFAULT_VERSION;
813         String format = page.getFormat();
814         boolean head = true;
815         String parentTitle = page.getParentTitle();
816         String redirectTitle = page.getTitle();
817         String content =
818             StringPool.DOUBLE_OPEN_BRACKET + redirectTitle +
819                 StringPool.DOUBLE_CLOSE_BRACKET;
820         String summary = WikiPageImpl.MOVED + " to " + title;
821 
822         addPage(
823             uuid, userId, nodeId, title, version, content, summary, false,
824             format, head, parentTitle, redirectTitle, null, prefs,
825             themeDisplay);
826 
827         // Move redirects to point to the page with the new title
828 
829         List<WikiPage> redirectedPages = wikiPagePersistence.findByN_R(
830             nodeId, title);
831 
832         for (WikiPage redirectedPage : redirectedPages) {
833             redirectedPage.setRedirectTitle(newTitle);
834 
835             wikiPagePersistence.update(redirectedPage, false);
836         }
837 
838         // Tags
839 
840         String[] tagsEntries = tagsEntryLocalService.getEntryNames(
841             WikiPage.class.getName(), page.getResourcePrimKey());
842 
843         updateTagsAsset(userId, page, tagsEntries);
844 
845         // Indexer
846 
847         try {
848             Indexer.deletePage(page.getCompanyId(), page.getGroupId(), title);
849         }
850         catch (SearchException se) {
851             _log.error("Indexing " + newTitle, se);
852         }
853 
854         reIndex(page);
855     }
856 
857     public void reIndex(long resourcePrimKey) throws SystemException {
858         if (SearchEngineUtil.isIndexReadOnly()) {
859             return;
860         }
861 
862         WikiPage page = null;
863 
864         try {
865             page = wikiPageFinder.findByResourcePrimKey(resourcePrimKey);
866         }
867         catch (NoSuchPageException nspe) {
868             return;
869         }
870 
871         reIndex(page);
872     }
873 
874     public void reIndex(WikiPage page) throws SystemException {
875         if (Validator.isNotNull(page.getRedirectTitle())) {
876             return;
877         }
878 
879         long companyId = page.getCompanyId();
880         long groupId = page.getGroupId();
881         long resourcePrimKey = page.getResourcePrimKey();
882         long nodeId = page.getNodeId();
883         String title = page.getTitle();
884         String content = page.getContent();
885         Date modifiedDate = page.getModifiedDate();
886 
887         String[] tagsEntries = tagsEntryLocalService.getEntryNames(
888             WikiPage.class.getName(), resourcePrimKey);
889 
890         try {
891             Indexer.updatePage(
892                 companyId, groupId, nodeId, title, content, modifiedDate,
893                 tagsEntries);
894         }
895         catch (SearchException se) {
896             _log.error("Reindexing " + page.getPrimaryKey(), se);
897         }
898     }
899 
900     public WikiPage revertPage(
901             long userId, long nodeId, String title, double version,
902             PortletPreferences prefs, ThemeDisplay themeDisplay)
903         throws PortalException, SystemException {
904 
905         WikiPage oldPage = getPage(nodeId, title, version);
906 
907         return updatePage(
908             userId, nodeId, title, 0, oldPage.getContent(),
909             WikiPageImpl.REVERTED + " to " + version, false,
910             oldPage.getFormat(), null, oldPage.getRedirectTitle(), null, prefs,
911             themeDisplay);
912     }
913 
914     public void subscribePage(long userId, long nodeId, String title)
915         throws PortalException, SystemException {
916 
917         WikiPage page = getPage(nodeId, title);
918 
919         subscriptionLocalService.addSubscription(
920             userId, WikiPage.class.getName(), page.getResourcePrimKey());
921     }
922 
923     public void unsubscribePage(long userId, long nodeId, String title)
924         throws PortalException, SystemException {
925 
926         WikiPage page = getPage(nodeId, title);
927 
928         subscriptionLocalService.deleteSubscription(
929             userId, WikiPage.class.getName(), page.getResourcePrimKey());
930     }
931 
932     public WikiPage updatePage(
933             long userId, long nodeId, String title, double version,
934             String content, String summary, boolean minorEdit, String format,
935             String parentTitle, String redirectTitle, String[] tagsEntries,
936             PortletPreferences prefs, ThemeDisplay themeDisplay)
937         throws PortalException, SystemException {
938 
939         // Page
940 
941         User user = userPersistence.findByPrimaryKey(userId);
942         Date now = new Date();
943 
944         validate(nodeId, content, format);
945 
946         WikiPage page = null;
947 
948         try {
949             page = getPage(nodeId, title);
950         }
951         catch (NoSuchPageException nspe) {
952             return addPage(
953                 null, userId, nodeId, title, WikiPageImpl.DEFAULT_VERSION,
954                 content, summary, minorEdit, format, true, parentTitle,
955                 redirectTitle, tagsEntries, prefs, themeDisplay);
956         }
957 
958         double oldVersion = page.getVersion();
959 
960         if ((version > 0) && (version != oldVersion)) {
961             throw new PageVersionException();
962         }
963 
964         long resourcePrimKey = page.getResourcePrimKey();
965         long groupId = page.getGroupId();
966 
967         page.setHead(false);
968         page.setModifiedDate(now);
969 
970         wikiPagePersistence.update(page, false);
971 
972         double newVersion = MathUtil.format(oldVersion + 0.1, 1, 1);
973 
974         long pageId = counterLocalService.increment();
975 
976         page = wikiPagePersistence.create(pageId);
977 
978         page.setResourcePrimKey(resourcePrimKey);
979         page.setGroupId(groupId);
980         page.setCompanyId(user.getCompanyId());
981         page.setUserId(user.getUserId());
982         page.setUserName(user.getFullName());
983         page.setCreateDate(now);
984         page.setModifiedDate(now);
985         page.setNodeId(nodeId);
986         page.setTitle(title);
987         page.setVersion(newVersion);
988         page.setMinorEdit(minorEdit);
989         page.setContent(content);
990         page.setSummary(summary);
991         page.setFormat(format);
992         page.setHead(true);
993 
994         if (Validator.isNotNull(parentTitle)) {
995             page.setParentTitle(parentTitle);
996         }
997 
998         if (Validator.isNotNull(redirectTitle)) {
999             page.setRedirectTitle(redirectTitle);
1000        }
1001
1002        wikiPagePersistence.update(page, false);
1003
1004        // Node
1005
1006        WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
1007
1008        node.setLastPostDate(now);
1009
1010        wikiNodePersistence.update(node, false);
1011
1012        // Social
1013
1014        socialActivityLocalService.addActivity(
1015            userId, page.getGroupId(), WikiPage.class.getName(),
1016            page.getResourcePrimKey(), WikiActivityKeys.UPDATE_PAGE,
1017            StringPool.BLANK, 0);
1018
1019        // Subscriptions
1020
1021        if (!minorEdit && NotificationThreadLocal.isNotificationEnabled()) {
1022            notifySubscribers(node, page, prefs, themeDisplay, true);
1023        }
1024
1025        // Tags
1026
1027        updateTagsAsset(userId, page, tagsEntries);
1028
1029        // Indexer
1030
1031        reIndex(page);
1032
1033        // Cache
1034
1035        clearPageCache(page);
1036
1037        return page;
1038    }
1039
1040    public void updateTagsAsset(
1041            long userId, WikiPage page, String[] tagsEntries)
1042        throws PortalException, SystemException {
1043
1044        tagsAssetLocalService.updateAsset(
1045            userId, page.getGroupId(), WikiPage.class.getName(),
1046            page.getResourcePrimKey(), tagsEntries, null, null, null, null,
1047            ContentTypes.TEXT_HTML, page.getTitle(), null, null, null, 0, 0,
1048            null, false);
1049    }
1050
1051    public void validateTitle(String title) throws PortalException {
1052        if (title.equals("all_pages") || title.equals("orphan_pages") ||
1053            title.equals("recent_changes")) {
1054
1055            throw new PageTitleException(title + " is reserved");
1056        }
1057
1058        if (Validator.isNotNull(PropsValues.WIKI_PAGE_TITLES_REGEXP)) {
1059            Pattern pattern = Pattern.compile(
1060                PropsValues.WIKI_PAGE_TITLES_REGEXP);
1061
1062            Matcher matcher = pattern.matcher(title);
1063
1064            if (!matcher.matches()) {
1065                throw new PageTitleException();
1066            }
1067        }
1068    }
1069
1070    protected void clearPageCache(WikiPage page) {
1071        if (!WikiCacheThreadLocal.isClearCache()) {
1072            return;
1073        }
1074
1075        WikiCacheUtil.clearCache(page.getNodeId(), page.getTitle());
1076    }
1077
1078    protected void clearReferralsCache(WikiPage page)
1079        throws PortalException, SystemException {
1080
1081        if (!WikiCacheThreadLocal.isClearCache()) {
1082            return;
1083        }
1084
1085        List<WikiPage> links = getIncomingLinks(
1086            page.getNodeId(), page.getTitle());
1087
1088        for (WikiPage curPage : links) {
1089            WikiCacheUtil.clearCache(curPage.getNodeId(), curPage.getTitle());
1090        }
1091    }
1092
1093    protected boolean isLinkedTo(WikiPage page, String targetTitle)
1094        throws PortalException {
1095
1096        Map<String, Boolean> links = WikiCacheUtil.getOutgoingLinks(page);
1097
1098        Boolean link = links.get(targetTitle);
1099
1100        if (link != null) {
1101            return true;
1102        }
1103        else {
1104            return false;
1105        }
1106    }
1107
1108    protected boolean isUsedTitle(long nodeId, String title)
1109        throws SystemException {
1110
1111        if (getPagesCount(nodeId, title, true) > 0) {
1112            return true;
1113        }
1114        else {
1115            return false;
1116        }
1117    }
1118
1119    protected void notifySubscribers(
1120            WikiNode node, WikiPage page, PortletPreferences prefs,
1121            ThemeDisplay themeDisplay, boolean update)
1122        throws PortalException, SystemException {
1123
1124        if (prefs == null) {
1125            long ownerId = node.getGroupId();
1126            int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
1127            long plid = PortletKeys.PREFS_PLID_SHARED;
1128            String portletId = PortletKeys.WIKI;
1129            String defaultPreferences = null;
1130
1131            prefs = portletPreferencesLocalService.getPreferences(
1132                node.getCompanyId(), ownerId, ownerType, plid, portletId,
1133                defaultPreferences);
1134        }
1135
1136        if (!update && WikiUtil.getEmailPageAddedEnabled(prefs)) {
1137        }
1138        else if (update && WikiUtil.getEmailPageUpdatedEnabled(prefs)) {
1139        }
1140        else {
1141            return;
1142        }
1143
1144        Company company = companyPersistence.findByPrimaryKey(
1145            page.getCompanyId());
1146
1147        Group group = groupPersistence.findByPrimaryKey(node.getGroupId());
1148
1149        User user = userPersistence.findByPrimaryKey(page.getUserId());
1150
1151        String pageURL = StringPool.BLANK;
1152
1153        if (themeDisplay != null) {
1154            String layoutFullURL = PortalUtil.getLayoutFullURL(themeDisplay);
1155
1156            pageURL =
1157                layoutFullURL + Portal.FRIENDLY_URL_SEPARATOR + "wiki/" +
1158                    node.getNodeId() + StringPool.SLASH +
1159                        HttpUtil.encodeURL(page.getTitle());
1160        }
1161
1162        String portletName = PortalUtil.getPortletTitle(
1163            PortletKeys.WIKI, user);
1164
1165        String fromName = WikiUtil.getEmailFromName(prefs);
1166        String fromAddress = WikiUtil.getEmailFromAddress(prefs);
1167
1168        String replyToAddress = fromAddress;
1169        String mailId = WikiUtil.getMailId(
1170            company.getMx(), page.getNodeId(), page.getPageId());
1171
1172        fromName = StringUtil.replace(
1173            fromName,
1174            new String[] {
1175                "[$COMPANY_ID$]",
1176                "[$COMPANY_MX$]",
1177                "[$COMPANY_NAME$]",
1178                "[$COMMUNITY_NAME$]",
1179                "[$PAGE_USER_ADDRESS$]",
1180                "[$PAGE_USER_NAME$]",
1181                "[$PORTLET_NAME$]"
1182            },
1183            new String[] {
1184                String.valueOf(company.getCompanyId()),
1185                company.getMx(),
1186                company.getName(),
1187                group.getName(),
1188                user.getEmailAddress(),
1189                user.getFullName(),
1190                portletName
1191            });
1192
1193        fromAddress = StringUtil.replace(
1194            fromAddress,
1195            new String[] {
1196                "[$COMPANY_ID$]",
1197                "[$COMPANY_MX$]",
1198                "[$COMPANY_NAME$]",
1199                "[$COMMUNITY_NAME$]",
1200                "[$PAGE_USER_ADDRESS$]",
1201                "[$PAGE_USER_NAME$]",
1202                "[$PORTLET_NAME$]"
1203            },
1204            new String[] {
1205                String.valueOf(company.getCompanyId()),
1206                company.getMx(),
1207                company.getName(),
1208                group.getName(),
1209                user.getEmailAddress(),
1210                user.getFullName(),
1211                portletName
1212            });
1213
1214        String subjectPrefix = null;
1215        String body = null;
1216        String signature = null;
1217
1218        if (update) {
1219            subjectPrefix = WikiUtil.getEmailPageUpdatedSubjectPrefix(prefs);
1220            body = WikiUtil.getEmailPageUpdatedBody(prefs);
1221            signature = WikiUtil.getEmailPageUpdatedSignature(prefs);
1222        }
1223        else {
1224            subjectPrefix = WikiUtil.getEmailPageAddedSubjectPrefix(prefs);
1225            body = WikiUtil.getEmailPageAddedBody(prefs);
1226            signature = WikiUtil.getEmailPageAddedSignature(prefs);
1227        }
1228
1229        if (Validator.isNotNull(signature)) {
1230            body +=  "\n--\n" + signature;
1231        }
1232
1233        subjectPrefix = StringUtil.replace(
1234            subjectPrefix,
1235            new String[] {
1236                "[$COMPANY_ID$]",
1237                "[$COMPANY_MX$]",
1238                "[$COMPANY_NAME$]",
1239                "[$COMMUNITY_NAME$]",
1240                "[$FROM_ADDRESS$]",
1241                "[$FROM_NAME$]",
1242                "[$NODE_NAME$]",
1243                "[$PAGE_CONTENT$]",
1244                "[$PAGE_ID$]",
1245                "[$PAGE_TITLE$]",
1246                "[$PAGE_USER_ADDRESS$]",
1247                "[$PAGE_USER_NAME$]",
1248                "[$PORTAL_URL$]",
1249                "[$PORTLET_NAME$]"
1250            },
1251            new String[] {
1252                String.valueOf(company.getCompanyId()),
1253                company.getMx(),
1254                company.getName(),
1255                group.getName(),
1256                fromAddress,
1257                fromName,
1258                node.getName(),
1259                page.getContent(),
1260                String.valueOf(page.getPageId()),
1261                page.getTitle(),
1262                user.getEmailAddress(),
1263                user.getFullName(),
1264                company.getVirtualHost(),
1265                portletName
1266            });
1267
1268        body = StringUtil.replace(
1269            body,
1270            new String[] {
1271                "[$COMPANY_ID$]",
1272                "[$COMPANY_MX$]",
1273                "[$COMPANY_NAME$]",
1274                "[$COMMUNITY_NAME$]",
1275                "[$FROM_ADDRESS$]",
1276                "[$FROM_NAME$]",
1277                "[$NODE_NAME$]",
1278                "[$PAGE_CONTENT$]",
1279                "[$PAGE_ID$]",
1280                "[$PAGE_TITLE$]",
1281                "[$PAGE_URL$]",
1282                "[$PAGE_USER_ADDRESS$]",
1283                "[$PAGE_USER_NAME$]",
1284                "[$PORTAL_URL$]",
1285                "[$PORTLET_NAME$]"
1286            },
1287            new String[] {
1288                String.valueOf(company.getCompanyId()),
1289                company.getMx(),
1290                company.getName(),
1291                group.getName(),
1292                fromAddress,
1293                fromName,
1294                node.getName(),
1295                page.getContent(),
1296                String.valueOf(page.getPageId()),
1297                page.getTitle(),
1298                pageURL,
1299                user.getEmailAddress(),
1300                user.getFullName(),
1301                company.getVirtualHost(),
1302                portletName
1303            });
1304
1305        String subject = page.getTitle();
1306
1307        if (subject.indexOf(subjectPrefix) == -1) {
1308            subject = subjectPrefix + subject;
1309        }
1310
1311        Message message = new Message();
1312
1313        message.put("companyId", node.getCompanyId());
1314        message.put("userId", node.getUserId());
1315        message.put("nodeId", node.getNodeId());
1316        message.put("pageResourcePrimKey", page.getResourcePrimKey());
1317        message.put("fromName", fromName);
1318        message.put("fromAddress", fromAddress);
1319        message.put("subject", subject);
1320        message.put("body", body);
1321        message.put("replyToAddress", replyToAddress);
1322        message.put("mailId", mailId);
1323
1324        MessageBusUtil.sendMessage(DestinationNames.WIKI, message);
1325    }
1326
1327    protected void validate(long nodeId, String content, String format)
1328        throws PortalException {
1329
1330        if (!WikiUtil.validate(nodeId, content, format)) {
1331            throw new PageContentException();
1332        }
1333    }
1334
1335    protected void validate(
1336            String title, long nodeId, String content, String format)
1337        throws PortalException, SystemException {
1338
1339        if (Validator.isNull(title)) {
1340            throw new PageTitleException();
1341        }
1342
1343        if (isUsedTitle(nodeId, title)) {
1344            throw new DuplicatePageException();
1345        }
1346
1347        validateTitle(title);
1348
1349        validate(nodeId, content, format);
1350    }
1351
1352    private static Log _log =
1353        LogFactoryUtil.getLog(WikiPageLocalServiceImpl.class);
1354
1355}