001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.wiki.lar;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryUtil;
018    import com.liferay.portal.kernel.lar.BasePortletDataHandler;
019    import com.liferay.portal.kernel.lar.PortletDataContext;
020    import com.liferay.portal.kernel.lar.PortletDataException;
021    import com.liferay.portal.kernel.lar.PortletDataHandlerBoolean;
022    import com.liferay.portal.kernel.lar.PortletDataHandlerControl;
023    import com.liferay.portal.kernel.util.MapUtil;
024    import com.liferay.portal.kernel.util.StreamUtil;
025    import com.liferay.portal.kernel.util.StringBundler;
026    import com.liferay.portal.kernel.util.StringPool;
027    import com.liferay.portal.kernel.workflow.WorkflowConstants;
028    import com.liferay.portal.kernel.xml.Document;
029    import com.liferay.portal.kernel.xml.Element;
030    import com.liferay.portal.kernel.xml.SAXReaderUtil;
031    import com.liferay.portal.model.CompanyConstants;
032    import com.liferay.portal.service.ServiceContext;
033    import com.liferay.portal.util.PortletKeys;
034    import com.liferay.portal.util.PropsValues;
035    import com.liferay.portlet.documentlibrary.store.DLStoreUtil;
036    import com.liferay.portlet.journal.lar.JournalPortletDataHandlerImpl;
037    import com.liferay.portlet.wiki.NoSuchNodeException;
038    import com.liferay.portlet.wiki.NoSuchPageException;
039    import com.liferay.portlet.wiki.model.WikiNode;
040    import com.liferay.portlet.wiki.model.WikiPage;
041    import com.liferay.portlet.wiki.service.WikiNodeLocalServiceUtil;
042    import com.liferay.portlet.wiki.service.WikiPageLocalServiceUtil;
043    import com.liferay.portlet.wiki.service.persistence.WikiNodeUtil;
044    import com.liferay.portlet.wiki.service.persistence.WikiPageUtil;
045    import com.liferay.portlet.wiki.util.WikiCacheThreadLocal;
046    import com.liferay.portlet.wiki.util.WikiCacheUtil;
047    import com.liferay.portlet.wiki.util.comparator.PageVersionComparator;
048    
049    import java.io.InputStream;
050    
051    import java.util.List;
052    import java.util.Map;
053    
054    import javax.portlet.PortletPreferences;
055    
056    /**
057     * @author Bruno Farache
058     * @author Jorge Ferrer
059     * @author Marcellus Tavares
060     * @author Juan Fernández
061     */
062    public class WikiPortletDataHandlerImpl extends BasePortletDataHandler {
063    
064            public static void exportNode(
065                            PortletDataContext portletDataContext, Element nodesElement,
066                            Element pagesElement, WikiNode node)
067                    throws Exception {
068    
069                    if (portletDataContext.isWithinDateRange(node.getModifiedDate())) {
070                            String path = getNodePath(portletDataContext, node);
071    
072                            if (portletDataContext.isPathNotProcessed(path)) {
073                                    Element nodeElement = nodesElement.addElement("node");
074    
075                                    portletDataContext.addClassedModel(
076                                            nodeElement, path, node, _NAMESPACE);
077                            }
078                    }
079    
080                    Element dlFileEntryTypesElement = pagesElement.addElement(
081                            "dl-file-entry-types");
082                    Element dlFoldersElement = pagesElement.addElement("dl-folders");
083                    Element dlFileEntriesElement = pagesElement.addElement(
084                            "dl-file-entries");
085                    Element dlFileRanksElement = pagesElement.addElement("dl-file-ranks");
086                    Element igFoldersElement = pagesElement.addElement("ig-folders");
087                    Element igImagesElement = pagesElement.addElement("ig-images");
088    
089                    List<WikiPage> pages = WikiPageUtil.findByN_S(
090                            node.getNodeId(), WorkflowConstants.STATUS_APPROVED,
091                            QueryUtil.ALL_POS, QueryUtil.ALL_POS,
092                            new PageVersionComparator(true));
093    
094                    for (WikiPage page : pages) {
095                            exportPage(
096                                    portletDataContext, nodesElement, pagesElement,
097                                    dlFileEntryTypesElement, dlFoldersElement, dlFileEntriesElement,
098                                    dlFileRanksElement, igFoldersElement, igImagesElement, page,
099                                    true);
100                    }
101            }
102    
103            public static void importNode(
104                            PortletDataContext portletDataContext, WikiNode node)
105                    throws Exception {
106    
107                    long userId = portletDataContext.getUserId(node.getUserUuid());
108    
109                    ServiceContext serviceContext = new ServiceContext();
110    
111                    serviceContext.setAddGroupPermissions(true);
112                    serviceContext.setAddGuestPermissions(true);
113                    serviceContext.setCreateDate(node.getCreateDate());
114                    serviceContext.setModifiedDate(node.getModifiedDate());
115                    serviceContext.setScopeGroupId(portletDataContext.getScopeGroupId());
116    
117                    WikiNode importedNode = null;
118    
119                    if (portletDataContext.isDataStrategyMirror()) {
120                            WikiNode existingNode = WikiNodeUtil.fetchByUUID_G(
121                                    node.getUuid(), portletDataContext.getScopeGroupId());
122    
123                            String initialNodeName = PropsValues.WIKI_INITIAL_NODE_NAME;
124    
125                            if ((existingNode == null) &&
126                                    initialNodeName.equals(node.getName())) {
127    
128                                    try {
129                                            WikiNodeUtil.removeByG_N(
130                                                    portletDataContext.getScopeGroupId(), node.getName());
131                                    }
132                                    catch (NoSuchNodeException nsne) {
133                                    }
134                            }
135    
136                            if (existingNode == null) {
137                                    serviceContext.setUuid(node.getUuid());
138    
139                                    importedNode = WikiNodeLocalServiceUtil.addNode(
140                                            userId, node.getName(), node.getDescription(),
141                                            serviceContext);
142                            }
143                            else {
144                                    importedNode = WikiNodeLocalServiceUtil.updateNode(
145                                            existingNode.getNodeId(), node.getName(),
146                                            node.getDescription(), serviceContext);
147                            }
148                    }
149                    else {
150                            String initialNodeName = PropsValues.WIKI_INITIAL_NODE_NAME;
151    
152                            if (initialNodeName.equals(node.getName())) {
153                                    try {
154                                            WikiNodeUtil.removeByG_N(
155                                                    portletDataContext.getScopeGroupId(), node.getName());
156                                    }
157                                    catch (NoSuchNodeException nsne) {
158                                    }
159                            }
160    
161                            String nodeName = getNodeName(
162                                    portletDataContext, node, node.getName(), 2);
163    
164                            importedNode = WikiNodeLocalServiceUtil.addNode(
165                                    userId, nodeName, node.getDescription(), serviceContext);
166                    }
167    
168                    portletDataContext.importClassedModel(node, importedNode, _NAMESPACE);
169            }
170    
171            public static void importPage(
172                            PortletDataContext portletDataContext, Element pageElement,
173                            WikiPage page)
174                    throws Exception {
175    
176                    long userId = portletDataContext.getUserId(page.getUserUuid());
177    
178                    Map<Long, Long> nodePKs =
179                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
180                                    WikiNode.class);
181    
182                    long nodeId = MapUtil.getLong(
183                            nodePKs, page.getNodeId(), page.getNodeId());
184    
185                    String content = JournalPortletDataHandlerImpl.importReferencedContent(
186                            portletDataContext, pageElement, page.getContent());
187    
188                    page.setContent(content);
189    
190                    ServiceContext serviceContext = portletDataContext.createServiceContext(
191                            pageElement, page, _NAMESPACE);
192    
193                    if (page.getStatus() != WorkflowConstants.STATUS_APPROVED) {
194                            serviceContext.setWorkflowAction(
195                                    WorkflowConstants.ACTION_SAVE_DRAFT);
196                    }
197    
198                    WikiPage importedPage = null;
199    
200                    WikiPage existingPage = WikiPageUtil.fetchByUUID_G(
201                            page.getUuid(), portletDataContext.getScopeGroupId());
202    
203                    if (existingPage == null) {
204                            try {
205                                    existingPage = WikiPageLocalServiceUtil.getPage(
206                                            nodeId, page.getTitle());
207                            }
208                            catch (NoSuchPageException nspe) {
209                            }
210                    }
211    
212                    if (existingPage == null) {
213                            serviceContext.setUuid(page.getUuid());
214    
215                            importedPage = WikiPageLocalServiceUtil.addPage(
216                                    userId, nodeId, page.getTitle(), page.getVersion(),
217                                    page.getContent(), page.getSummary(), page.isMinorEdit(),
218                                    page.getFormat(), page.getHead(), page.getParentTitle(),
219                                    page.getRedirectTitle(), serviceContext);
220                    }
221                    else {
222                            importedPage = WikiPageLocalServiceUtil.updatePage(
223                                    userId, nodeId, existingPage.getTitle(), 0, page.getContent(),
224                                    page.getSummary(), page.isMinorEdit(), page.getFormat(),
225                                    page.getParentTitle(), page.getRedirectTitle(), serviceContext);
226                    }
227    
228                    if (portletDataContext.getBooleanParameter(_NAMESPACE, "attachments") &&
229                            page.isHead()) {
230    
231                            for (Element attachmentElement :
232                                            pageElement.elements("attachment")) {
233    
234                                    String name = attachmentElement.attributeValue("name");
235                                    String binPath = attachmentElement.attributeValue("bin-path");
236    
237                                    InputStream inputStream = null;
238    
239                                    try {
240                                            inputStream = portletDataContext.getZipEntryAsInputStream(
241                                                    binPath);
242    
243                                            WikiPageLocalServiceUtil.addPageAttachment(
244                                                    importedPage.getCompanyId(),
245                                                    importedPage.getAttachmentsDir(),
246                                                    importedPage.getModifiedDate(), name, inputStream);
247                                    }
248                                    finally {
249                                            StreamUtil.cleanUp(inputStream);
250                                    }
251                            }
252                    }
253    
254                    portletDataContext.importClassedModel(page, importedPage, _NAMESPACE);
255            }
256    
257            @Override
258            public PortletDataHandlerControl[] getExportControls() {
259                    return new PortletDataHandlerControl[] {
260                            _nodesAndPages, _attachments, _categories, _comments, _ratings,
261                            _tags
262                    };
263            }
264    
265            @Override
266            public PortletDataHandlerControl[] getImportControls() {
267                    return new PortletDataHandlerControl[] {
268                            _nodesAndPages, _attachments, _categories, _comments, _ratings,
269                            _tags
270                    };
271            }
272    
273            @Override
274            public PortletPreferences importData(
275                            PortletDataContext portletDataContext, String portletId,
276                            PortletPreferences portletPreferences, String data)
277                    throws PortletDataException {
278    
279                    WikiCacheThreadLocal.setClearCache(false);
280    
281                    try {
282                            return super.importData(
283                                    portletDataContext, portletId, portletPreferences, data);
284                    }
285                    finally {
286                            WikiCacheThreadLocal.setClearCache(true);
287                    }
288            }
289    
290            protected static void exportNode(
291                            PortletDataContext portletDataContext, Element nodesElement,
292                            long nodeId)
293                    throws Exception {
294    
295                    if (!portletDataContext.hasDateRange()) {
296                            return;
297                    }
298    
299                    WikiNode node = WikiNodeUtil.findByPrimaryKey(nodeId);
300    
301                    String path = getNodePath(portletDataContext, node);
302    
303                    if (!portletDataContext.isPathNotProcessed(path)) {
304                            return;
305                    }
306    
307                    Element nodeElement = nodesElement.addElement("node");
308    
309                    portletDataContext.addClassedModel(nodeElement, path, node, _NAMESPACE);
310            }
311    
312            protected static void exportPage(
313                            PortletDataContext portletDataContext, Element nodesElement,
314                            Element pagesElement, Element dlFileEntryTypesElement,
315                            Element dlFoldersElement, Element dlFileEntriesElement,
316                            Element dlFileRanksElement, Element igFoldersElement,
317                            Element igImagesElement, WikiPage page, boolean checkDateRange)
318                    throws Exception {
319    
320                    if (!portletDataContext.isWithinDateRange(page.getModifiedDate())) {
321                            return;
322                    }
323    
324                    String path = getPagePath(portletDataContext, page);
325    
326                    // Clone this page to make sure changes to its content are never
327                    // persisted
328    
329                    page = (WikiPage)page.clone();
330    
331                    Element pageElement = (Element)pagesElement.selectSingleNode(
332                            "//page[@path='".concat(path).concat("']"));
333    
334                    if (portletDataContext.isPathNotProcessed(path)) {
335                            if (pageElement == null) {
336                                    pageElement = pagesElement.addElement("page");
337                            }
338    
339                            String content =
340                                    JournalPortletDataHandlerImpl.exportReferencedContent(
341                                            portletDataContext, dlFileEntryTypesElement,
342                                            dlFoldersElement, dlFileEntriesElement, dlFileRanksElement,
343                                            igFoldersElement, igImagesElement, pageElement,
344                                            page.getContent(), checkDateRange);
345    
346                            page.setContent(content);
347    
348                            String imagePath = getPageImagePath(portletDataContext, page);
349    
350                            pageElement.addAttribute("image-path", imagePath);
351    
352                            if (portletDataContext.getBooleanParameter(
353                                            _NAMESPACE, "attachments") &&
354                                    page.isHead()) {
355    
356                                    String[] attachmentsFiles = page.getAttachmentsFiles();
357    
358                                    for (int i = 0; i < attachmentsFiles.length; i++) {
359                                            String attachment = attachmentsFiles[i];
360    
361                                            Element attachmentElement = pageElement.addElement(
362                                                    "attachment");
363    
364                                            int pos = attachment.lastIndexOf(StringPool.SLASH);
365    
366                                            String name = attachment.substring(pos + 1);
367    
368                                            attachmentElement.addAttribute("name", name);
369    
370                                            String binPath = getPageAttachementBinPath(
371                                                    portletDataContext, page, i);
372    
373                                            attachmentElement.addAttribute("bin-path", binPath);
374    
375                                            byte[] bytes = DLStoreUtil.getFileAsBytes(
376                                                    portletDataContext.getCompanyId(),
377                                                    CompanyConstants.SYSTEM, attachment);
378    
379                                            portletDataContext.addZipEntry(binPath, bytes);
380                                    }
381    
382                                    page.setAttachmentsDir(page.getAttachmentsDir());
383                            }
384    
385                            portletDataContext.addClassedModel(
386                                    pageElement, path, page, _NAMESPACE);
387                    }
388    
389                    exportNode(portletDataContext, nodesElement, page.getNodeId());
390            }
391    
392            protected static String getNodeName(
393                            PortletDataContext portletDataContext, WikiNode node, String name,
394                            int count)
395                    throws Exception {
396    
397                    WikiNode existingNode = WikiNodeUtil.fetchByG_N(
398                            portletDataContext.getScopeGroupId(), name);
399    
400                    if (existingNode == null) {
401                            return name;
402                    }
403    
404                    String nodeName = node.getName();
405    
406                    return getNodeName(
407                            portletDataContext, node,
408                            nodeName.concat(StringPool.SPACE).concat(String.valueOf(count)),
409                            ++count);
410            }
411    
412            protected static String getNodePath(
413                    PortletDataContext portletDataContext, WikiNode node) {
414    
415                    StringBundler sb = new StringBundler(4);
416    
417                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
418                    sb.append("/nodes/");
419                    sb.append(node.getNodeId());
420                    sb.append(".xml");
421    
422                    return sb.toString();
423            }
424    
425            protected static String getPageAttachementBinPath(
426                    PortletDataContext portletDataContext, WikiPage page, int count) {
427    
428                    StringBundler sb = new StringBundler(6);
429    
430                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
431                    sb.append("/bin/");
432                    sb.append(page.getPageId());
433                    sb.append(StringPool.SLASH);
434                    sb.append("attachement");
435                    sb.append(count);
436    
437                    return sb.toString();
438            }
439    
440            protected static String getPageImagePath(
441                            PortletDataContext portletDataContext, WikiPage page)
442                    throws Exception {
443    
444                    StringBundler sb = new StringBundler(6);
445    
446                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
447                    sb.append("/page/");
448                    sb.append(page.getUuid());
449                    sb.append(StringPool.SLASH);
450                    sb.append(page.getVersion());
451                    sb.append(StringPool.SLASH);
452    
453                    return sb.toString();
454            }
455    
456            protected static String getPagePath(
457                    PortletDataContext portletDataContext, WikiPage page) {
458    
459                    StringBundler sb = new StringBundler(4);
460    
461                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
462                    sb.append("/pages/");
463                    sb.append(page.getPageId());
464                    sb.append(".xml");
465    
466                    return sb.toString();
467            }
468    
469            @Override
470            protected PortletPreferences doDeleteData(
471                            PortletDataContext portletDataContext, String portletId,
472                            PortletPreferences portletPreferences)
473                    throws Exception {
474    
475                    if (!portletDataContext.addPrimaryKey(
476                                    WikiPortletDataHandlerImpl.class, "deleteData")) {
477    
478                            WikiNodeLocalServiceUtil.deleteNodes(
479                                    portletDataContext.getScopeGroupId());
480                    }
481    
482                    return null;
483            }
484    
485            @Override
486            protected String doExportData(
487                            PortletDataContext portletDataContext, String portletId,
488                            PortletPreferences portletPreferences)
489                    throws Exception {
490    
491                    portletDataContext.addPermissions(
492                            "com.liferay.portlet.wiki", portletDataContext.getScopeGroupId());
493    
494                    Document document = SAXReaderUtil.createDocument();
495    
496                    Element rootElement = document.addElement("wiki-data");
497    
498                    rootElement.addAttribute(
499                            "group-id", String.valueOf(portletDataContext.getScopeGroupId()));
500    
501                    Element nodesElement = rootElement.addElement("nodes");
502                    Element pagesElement = rootElement.addElement("pages");
503    
504                    List<WikiNode> nodes = WikiNodeUtil.findByGroupId(
505                            portletDataContext.getScopeGroupId());
506    
507                    for (WikiNode node : nodes) {
508                            exportNode(portletDataContext, nodesElement, pagesElement, node);
509                    }
510    
511                    return document.formattedString();
512            }
513    
514            @Override
515            protected PortletPreferences doImportData(
516                            PortletDataContext portletDataContext, String portletId,
517                            PortletPreferences portletPreferences, String data)
518                    throws Exception {
519    
520                    portletDataContext.importPermissions(
521                            "com.liferay.portlet.wiki", portletDataContext.getSourceGroupId(),
522                            portletDataContext.getScopeGroupId());
523    
524                    Document document = SAXReaderUtil.read(data);
525    
526                    Element rootElement = document.getRootElement();
527    
528                    Element nodesElement = rootElement.element("nodes");
529    
530                    for (Element nodeElement : nodesElement.elements("node")) {
531                            String path = nodeElement.attributeValue("path");
532    
533                            if (!portletDataContext.isPathNotProcessed(path)) {
534                                    continue;
535                            }
536    
537                            WikiNode node = (WikiNode)portletDataContext.getZipEntryAsObject(
538                                    path);
539    
540                            importNode(portletDataContext, node);
541                    }
542    
543                    Element pagesElement = rootElement.element("pages");
544    
545                    JournalPortletDataHandlerImpl.importReferencedData(
546                            portletDataContext, pagesElement);
547    
548                    for (Element pageElement : pagesElement.elements("page")) {
549                            String path = pageElement.attributeValue("path");
550    
551                            if (!portletDataContext.isPathNotProcessed(path)) {
552                                    continue;
553                            }
554    
555                            WikiPage page = (WikiPage)portletDataContext.getZipEntryAsObject(
556                                    path);
557    
558                            importPage(portletDataContext, pageElement, page);
559                    }
560    
561                    Map<Long, Long> nodePKs =
562                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
563                                    WikiNode.class);
564    
565                    for (long nodeId : nodePKs.values()) {
566                            WikiCacheUtil.clearCache(nodeId);
567                    }
568    
569                    return null;
570            }
571    
572            private static final String _NAMESPACE = "wiki";
573    
574            private static PortletDataHandlerBoolean _attachments =
575                    new PortletDataHandlerBoolean(_NAMESPACE, "attachments");
576    
577            private static PortletDataHandlerBoolean _categories =
578                    new PortletDataHandlerBoolean(_NAMESPACE, "categories");
579    
580            private static PortletDataHandlerBoolean _comments =
581                    new PortletDataHandlerBoolean(_NAMESPACE, "comments");
582    
583            private static PortletDataHandlerBoolean _nodesAndPages =
584                    new PortletDataHandlerBoolean(
585                            _NAMESPACE, "wikis-and-pages", true, true);
586    
587            private static PortletDataHandlerBoolean _ratings =
588                    new PortletDataHandlerBoolean(_NAMESPACE, "ratings");
589    
590            private static PortletDataHandlerBoolean _tags =
591                    new PortletDataHandlerBoolean(_NAMESPACE, "tags");
592    
593    }