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