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