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