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