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