001    /**
002     * Copyright (c) 2000-2013 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, Element dlFileEntryTypesElement,
067                            Element dlFoldersElement, Element dlFileEntriesElement,
068                            Element dlFileRanksElement, Element dlRepositoriesElement,
069                            Element dlRepositoryEntriesElement, WikiNode node)
070                    throws Exception {
071    
072                    if (portletDataContext.isWithinDateRange(node.getModifiedDate())) {
073                            String path = getNodePath(portletDataContext, node);
074    
075                            if (portletDataContext.isPathNotProcessed(path)) {
076                                    Element nodeElement = nodesElement.addElement("node");
077    
078                                    portletDataContext.addClassedModel(
079                                            nodeElement, path, node, _NAMESPACE);
080                            }
081                    }
082    
083                    List<WikiPage> pages = WikiPageUtil.findByN_S(
084                            node.getNodeId(), WorkflowConstants.STATUS_APPROVED,
085                            QueryUtil.ALL_POS, QueryUtil.ALL_POS,
086                            new PageVersionComparator(true));
087    
088                    for (WikiPage page : pages) {
089                            exportPage(
090                                    portletDataContext, nodesElement, pagesElement,
091                                    dlFileEntryTypesElement, dlFoldersElement, dlFileEntriesElement,
092                                    dlFileRanksElement, dlRepositoriesElement,
093                                    dlRepositoryEntriesElement, page, true);
094                    }
095            }
096    
097            public static PortletDataHandlerControl[] getMetadataControls() {
098                    return _metadataControls;
099            }
100    
101            public static void importNode(
102                            PortletDataContext portletDataContext, WikiNode node)
103                    throws Exception {
104    
105                    long userId = portletDataContext.getUserId(node.getUserUuid());
106    
107                    ServiceContext serviceContext = new ServiceContext();
108    
109                    serviceContext.setAddGroupPermissions(true);
110                    serviceContext.setAddGuestPermissions(true);
111                    serviceContext.setCreateDate(node.getCreateDate());
112                    serviceContext.setModifiedDate(node.getModifiedDate());
113                    serviceContext.setScopeGroupId(portletDataContext.getScopeGroupId());
114    
115                    WikiNode importedNode = null;
116    
117                    if (portletDataContext.isDataStrategyMirror()) {
118                            WikiNode existingNode = WikiNodeUtil.fetchByUUID_G(
119                                    node.getUuid(), portletDataContext.getScopeGroupId());
120    
121                            String initialNodeName = PropsValues.WIKI_INITIAL_NODE_NAME;
122    
123                            if ((existingNode == null) &&
124                                    initialNodeName.equals(node.getName())) {
125    
126                                    try {
127                                            WikiNodeUtil.removeByG_N(
128                                                    portletDataContext.getScopeGroupId(), node.getName());
129                                    }
130                                    catch (NoSuchNodeException nsne) {
131                                    }
132                            }
133    
134                            if (existingNode == null) {
135                                    serviceContext.setUuid(node.getUuid());
136    
137                                    importedNode = WikiNodeLocalServiceUtil.addNode(
138                                            userId, node.getName(), node.getDescription(),
139                                            serviceContext);
140                            }
141                            else {
142                                    importedNode = WikiNodeLocalServiceUtil.updateNode(
143                                            existingNode.getNodeId(), node.getName(),
144                                            node.getDescription(), serviceContext);
145                            }
146                    }
147                    else {
148                            String initialNodeName = PropsValues.WIKI_INITIAL_NODE_NAME;
149    
150                            if (initialNodeName.equals(node.getName())) {
151                                    try {
152                                            WikiNodeUtil.removeByG_N(
153                                                    portletDataContext.getScopeGroupId(), node.getName());
154                                    }
155                                    catch (NoSuchNodeException nsne) {
156                                    }
157                            }
158    
159                            String nodeName = getNodeName(
160                                    portletDataContext, node, node.getName(), 2);
161    
162                            importedNode = WikiNodeLocalServiceUtil.addNode(
163                                    userId, nodeName, node.getDescription(), serviceContext);
164                    }
165    
166                    portletDataContext.importClassedModel(node, importedNode, _NAMESPACE);
167            }
168    
169            public static void importPage(
170                            PortletDataContext portletDataContext, Element pageElement,
171                            WikiPage page)
172                    throws Exception {
173    
174                    long userId = portletDataContext.getUserId(page.getUserUuid());
175    
176                    Map<Long, Long> nodeIds =
177                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
178                                    WikiNode.class);
179    
180                    long nodeId = MapUtil.getLong(
181                            nodeIds, page.getNodeId(), page.getNodeId());
182    
183                    String content = JournalPortletDataHandlerImpl.importReferencedContent(
184                            portletDataContext, pageElement, page.getContent());
185    
186                    page.setContent(content);
187    
188                    ServiceContext serviceContext = portletDataContext.createServiceContext(
189                            pageElement, page, _NAMESPACE);
190    
191                    if (page.getStatus() != WorkflowConstants.STATUS_APPROVED) {
192                            serviceContext.setWorkflowAction(
193                                    WorkflowConstants.ACTION_SAVE_DRAFT);
194                    }
195    
196                    WikiPage importedPage = null;
197    
198                    WikiPage existingPage = WikiPageUtil.fetchByUUID_G(
199                            page.getUuid(), portletDataContext.getScopeGroupId());
200    
201                    if (existingPage == null) {
202                            try {
203                                    existingPage = WikiPageLocalServiceUtil.getPage(
204                                            nodeId, page.getTitle());
205                            }
206                            catch (NoSuchPageException nspe) {
207                            }
208                    }
209    
210                    if (existingPage == null) {
211                            serviceContext.setUuid(page.getUuid());
212    
213                            importedPage = WikiPageLocalServiceUtil.addPage(
214                                    userId, nodeId, page.getTitle(), page.getVersion(),
215                                    page.getContent(), page.getSummary(), page.isMinorEdit(),
216                                    page.getFormat(), page.getHead(), page.getParentTitle(),
217                                    page.getRedirectTitle(), serviceContext);
218                    }
219                    else {
220                            importedPage = WikiPageLocalServiceUtil.updatePage(
221                                    userId, nodeId, existingPage.getTitle(), 0, page.getContent(),
222                                    page.getSummary(), page.isMinorEdit(), page.getFormat(),
223                                    page.getParentTitle(), page.getRedirectTitle(), serviceContext);
224                    }
225    
226                    if (portletDataContext.getBooleanParameter(_NAMESPACE, "attachments") &&
227                            page.isHead()) {
228    
229                            for (Element attachmentElement :
230                                            pageElement.elements("attachment")) {
231    
232                                    String name = attachmentElement.attributeValue("name");
233                                    String binPath = attachmentElement.attributeValue("bin-path");
234    
235                                    InputStream inputStream = null;
236    
237                                    try {
238                                            inputStream = portletDataContext.getZipEntryAsInputStream(
239                                                    binPath);
240    
241                                            WikiPageLocalServiceUtil.addPageAttachment(
242                                                    importedPage.getCompanyId(),
243                                                    importedPage.getAttachmentsDir(),
244                                                    importedPage.getModifiedDate(), name, inputStream);
245                                    }
246                                    finally {
247                                            StreamUtil.cleanUp(inputStream);
248                                    }
249                            }
250                    }
251    
252                    portletDataContext.importClassedModel(page, importedPage, _NAMESPACE);
253            }
254    
255            @Override
256            public PortletDataHandlerControl[] getExportControls() {
257                    return new PortletDataHandlerControl[] {
258                            _nodesAndPages
259                    };
260            }
261    
262            @Override
263            public PortletDataHandlerControl[] getExportMetadataControls() {
264                    return new PortletDataHandlerControl[] {
265                            new PortletDataHandlerBoolean(
266                                    _NAMESPACE, "wiki-pages", true, _metadataControls)
267                    };
268            }
269    
270            @Override
271            public PortletDataHandlerControl[] getImportControls() {
272                    return new PortletDataHandlerControl[] {
273                            _nodesAndPages
274                    };
275            }
276    
277            @Override
278            public PortletDataHandlerControl[] getImportMetadataControls() {
279                    return new PortletDataHandlerControl[] {
280                            new PortletDataHandlerBoolean(
281                                    _NAMESPACE, "wiki-pages", true, _metadataControls)
282                    };
283            }
284    
285            @Override
286            public PortletPreferences importData(
287                            PortletDataContext portletDataContext, String portletId,
288                            PortletPreferences portletPreferences, String data)
289                    throws PortletDataException {
290    
291                    WikiCacheThreadLocal.setClearCache(false);
292    
293                    try {
294                            return super.importData(
295                                    portletDataContext, portletId, portletPreferences, data);
296                    }
297                    finally {
298                            WikiCacheThreadLocal.setClearCache(true);
299                    }
300            }
301    
302            protected static void exportNode(
303                            PortletDataContext portletDataContext, Element nodesElement,
304                            long nodeId)
305                    throws Exception {
306    
307                    if (!portletDataContext.hasDateRange()) {
308                            return;
309                    }
310    
311                    WikiNode node = WikiNodeUtil.findByPrimaryKey(nodeId);
312    
313                    String path = getNodePath(portletDataContext, node);
314    
315                    if (!portletDataContext.isPathNotProcessed(path)) {
316                            return;
317                    }
318    
319                    Element nodeElement = nodesElement.addElement("node");
320    
321                    portletDataContext.addClassedModel(nodeElement, path, node, _NAMESPACE);
322            }
323    
324            protected static void exportPage(
325                            PortletDataContext portletDataContext, Element nodesElement,
326                            Element pagesElement, Element dlFileEntryTypesElement,
327                            Element dlFoldersElement, Element dlFileEntriesElement,
328                            Element dlFileRanksElement, Element dlRepositoriesElement,
329                            Element dlRepositoryEntriesElement, WikiPage page,
330                            boolean checkDateRange)
331                    throws Exception {
332    
333                    if (!portletDataContext.isWithinDateRange(page.getModifiedDate())) {
334                            return;
335                    }
336    
337                    String path = getPagePath(portletDataContext, page);
338    
339                    // Clone this page to make sure changes to its content are never
340                    // persisted
341    
342                    page = (WikiPage)page.clone();
343    
344                    Element pageElement = (Element)pagesElement.selectSingleNode(
345                            "//page[@path='".concat(path).concat("']"));
346    
347                    if (portletDataContext.isPathNotProcessed(path)) {
348                            if (pageElement == null) {
349                                    pageElement = pagesElement.addElement("page");
350                            }
351    
352                            String content =
353                                    JournalPortletDataHandlerImpl.exportReferencedContent(
354                                            portletDataContext, dlFileEntryTypesElement,
355                                            dlFoldersElement, dlFileEntriesElement, dlFileRanksElement,
356                                            dlRepositoriesElement, dlRepositoryEntriesElement,
357                                            pageElement, page.getContent());
358    
359                            page.setContent(content);
360    
361                            String imagePath = getPageImagePath(portletDataContext, page);
362    
363                            pageElement.addAttribute("image-path", imagePath);
364    
365                            if (portletDataContext.getBooleanParameter(
366                                            _NAMESPACE, "attachments") &&
367                                    page.isHead()) {
368    
369                                    String[] attachmentsFiles = page.getAttachmentsFiles();
370    
371                                    for (int i = 0; i < attachmentsFiles.length; i++) {
372                                            String attachment = attachmentsFiles[i];
373    
374                                            Element attachmentElement = pageElement.addElement(
375                                                    "attachment");
376    
377                                            int pos = attachment.lastIndexOf(StringPool.SLASH);
378    
379                                            String name = attachment.substring(pos + 1);
380    
381                                            attachmentElement.addAttribute("name", name);
382    
383                                            String binPath = getPageAttachementBinPath(
384                                                    portletDataContext, page, i);
385    
386                                            attachmentElement.addAttribute("bin-path", binPath);
387    
388                                            byte[] bytes = DLStoreUtil.getFileAsBytes(
389                                                    portletDataContext.getCompanyId(),
390                                                    CompanyConstants.SYSTEM, attachment);
391    
392                                            portletDataContext.addZipEntry(binPath, bytes);
393                                    }
394    
395                                    page.setAttachmentsDir(page.getAttachmentsDir());
396                            }
397    
398                            portletDataContext.addClassedModel(
399                                    pageElement, path, page, _NAMESPACE);
400                    }
401    
402                    exportNode(portletDataContext, nodesElement, page.getNodeId());
403            }
404    
405            protected static String getNodeName(
406                            PortletDataContext portletDataContext, WikiNode node, String name,
407                            int count)
408                    throws Exception {
409    
410                    WikiNode existingNode = WikiNodeUtil.fetchByG_N(
411                            portletDataContext.getScopeGroupId(), name);
412    
413                    if (existingNode == null) {
414                            return name;
415                    }
416    
417                    String nodeName = node.getName();
418    
419                    return getNodeName(
420                            portletDataContext, node,
421                            nodeName.concat(StringPool.SPACE).concat(String.valueOf(count)),
422                            ++count);
423            }
424    
425            protected static String getNodePath(
426                    PortletDataContext portletDataContext, WikiNode node) {
427    
428                    StringBundler sb = new StringBundler(4);
429    
430                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
431                    sb.append("/nodes/");
432                    sb.append(node.getNodeId());
433                    sb.append(".xml");
434    
435                    return sb.toString();
436            }
437    
438            protected static String getPageAttachementBinPath(
439                    PortletDataContext portletDataContext, WikiPage page, int count) {
440    
441                    StringBundler sb = new StringBundler(6);
442    
443                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
444                    sb.append("/bin/");
445                    sb.append(page.getPageId());
446                    sb.append(StringPool.SLASH);
447                    sb.append("attachement");
448                    sb.append(count);
449    
450                    return sb.toString();
451            }
452    
453            protected static String getPageImagePath(
454                            PortletDataContext portletDataContext, WikiPage page)
455                    throws Exception {
456    
457                    StringBundler sb = new StringBundler(6);
458    
459                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
460                    sb.append("/page/");
461                    sb.append(page.getUuid());
462                    sb.append(StringPool.SLASH);
463                    sb.append(page.getVersion());
464                    sb.append(StringPool.SLASH);
465    
466                    return sb.toString();
467            }
468    
469            protected static String getPagePath(
470                    PortletDataContext portletDataContext, WikiPage page) {
471    
472                    StringBundler sb = new StringBundler(4);
473    
474                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
475                    sb.append("/pages/");
476                    sb.append(page.getPageId());
477                    sb.append(".xml");
478    
479                    return sb.toString();
480            }
481    
482            @Override
483            protected PortletPreferences doDeleteData(
484                            PortletDataContext portletDataContext, String portletId,
485                            PortletPreferences portletPreferences)
486                    throws Exception {
487    
488                    if (!portletDataContext.addPrimaryKey(
489                                    WikiPortletDataHandlerImpl.class, "deleteData")) {
490    
491                            WikiNodeLocalServiceUtil.deleteNodes(
492                                    portletDataContext.getScopeGroupId());
493                    }
494    
495                    return null;
496            }
497    
498            @Override
499            protected String doExportData(
500                            PortletDataContext portletDataContext, String portletId,
501                            PortletPreferences portletPreferences)
502                    throws Exception {
503    
504                    portletDataContext.addPermissions(
505                            "com.liferay.portlet.wiki", portletDataContext.getScopeGroupId());
506    
507                    Document document = SAXReaderUtil.createDocument();
508    
509                    Element rootElement = document.addElement("wiki-data");
510    
511                    rootElement.addAttribute(
512                            "group-id", String.valueOf(portletDataContext.getScopeGroupId()));
513    
514                    Element nodesElement = rootElement.addElement("nodes");
515                    Element pagesElement = rootElement.addElement("pages");
516    
517                    Element dlFileEntryTypesElement = pagesElement.addElement(
518                            "dl-file-entry-types");
519                    Element dlFoldersElement = pagesElement.addElement("dl-folders");
520                    Element dlFileEntriesElement = pagesElement.addElement(
521                            "dl-file-entries");
522                    Element dlFileRanksElement = pagesElement.addElement("dl-file-ranks");
523                    Element dlRepositoriesElement = pagesElement.addElement(
524                            "dl-repositories");
525                    Element dlRepositoryEntriesElement = pagesElement.addElement(
526                            "dl-repository-entries");
527    
528                    List<WikiNode> nodes = WikiNodeUtil.findByGroupId(
529                            portletDataContext.getScopeGroupId());
530    
531                    for (WikiNode node : nodes) {
532                            exportNode(
533                                    portletDataContext, nodesElement, pagesElement,
534                                    dlFileEntryTypesElement, dlFoldersElement, dlFileEntriesElement,
535                                    dlFileRanksElement, dlRepositoriesElement,
536                                    dlRepositoryEntriesElement, node);
537                    }
538    
539                    return document.formattedString();
540            }
541    
542            @Override
543            protected PortletPreferences doImportData(
544                            PortletDataContext portletDataContext, String portletId,
545                            PortletPreferences portletPreferences, String data)
546                    throws Exception {
547    
548                    portletDataContext.importPermissions(
549                            "com.liferay.portlet.wiki", portletDataContext.getSourceGroupId(),
550                            portletDataContext.getScopeGroupId());
551    
552                    Document document = SAXReaderUtil.read(data);
553    
554                    Element rootElement = document.getRootElement();
555    
556                    Element nodesElement = rootElement.element("nodes");
557    
558                    for (Element nodeElement : nodesElement.elements("node")) {
559                            String path = nodeElement.attributeValue("path");
560    
561                            if (!portletDataContext.isPathNotProcessed(path)) {
562                                    continue;
563                            }
564    
565                            WikiNode node = (WikiNode)portletDataContext.getZipEntryAsObject(
566                                    path);
567    
568                            importNode(portletDataContext, node);
569                    }
570    
571                    Element pagesElement = rootElement.element("pages");
572    
573                    JournalPortletDataHandlerImpl.importReferencedData(
574                            portletDataContext, pagesElement);
575    
576                    for (Element pageElement : pagesElement.elements("page")) {
577                            String path = pageElement.attributeValue("path");
578    
579                            if (!portletDataContext.isPathNotProcessed(path)) {
580                                    continue;
581                            }
582    
583                            WikiPage page = (WikiPage)portletDataContext.getZipEntryAsObject(
584                                    path);
585    
586                            importPage(portletDataContext, pageElement, page);
587                    }
588    
589                    Map<Long, Long> nodeIds =
590                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
591                                    WikiNode.class);
592    
593                    for (long nodeId : nodeIds.values()) {
594                            WikiCacheUtil.clearCache(nodeId);
595                    }
596    
597                    return null;
598            }
599    
600            private static final String _NAMESPACE = "wiki";
601    
602            private static PortletDataHandlerControl[] _metadataControls =
603                    new PortletDataHandlerControl[] {
604                            new PortletDataHandlerBoolean(_NAMESPACE, "attachments"),
605                            new PortletDataHandlerBoolean(_NAMESPACE, "categories"),
606                            new PortletDataHandlerBoolean(_NAMESPACE, "comments"),
607                            new PortletDataHandlerBoolean(_NAMESPACE, "ratings"),
608                            new PortletDataHandlerBoolean(_NAMESPACE, "tags")
609                    };
610            private static PortletDataHandlerBoolean _nodesAndPages =
611                    new PortletDataHandlerBoolean(
612                            _NAMESPACE, "wikis-and-pages", true, true);
613    
614    }