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.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    
087                    List<WikiPage> pages = WikiPageUtil.findByN_S(
088                            node.getNodeId(), WorkflowConstants.STATUS_APPROVED,
089                            QueryUtil.ALL_POS, QueryUtil.ALL_POS,
090                            new PageVersionComparator(true));
091    
092                    for (WikiPage page : pages) {
093                            exportPage(
094                                    portletDataContext, nodesElement, pagesElement,
095                                    dlFileEntryTypesElement, dlFoldersElement, dlFileEntriesElement,
096                                    dlFileRanksElement, page, true);
097                    }
098            }
099    
100            public static PortletDataHandlerControl[] getMetadataControls() {
101                    return _metadataControls;
102            }
103    
104            public static void importNode(
105                            PortletDataContext portletDataContext, WikiNode node)
106                    throws Exception {
107    
108                    long userId = portletDataContext.getUserId(node.getUserUuid());
109    
110                    ServiceContext serviceContext = new ServiceContext();
111    
112                    serviceContext.setAddGroupPermissions(true);
113                    serviceContext.setAddGuestPermissions(true);
114                    serviceContext.setCreateDate(node.getCreateDate());
115                    serviceContext.setModifiedDate(node.getModifiedDate());
116                    serviceContext.setScopeGroupId(portletDataContext.getScopeGroupId());
117    
118                    WikiNode importedNode = null;
119    
120                    if (portletDataContext.isDataStrategyMirror()) {
121                            WikiNode existingNode = WikiNodeUtil.fetchByUUID_G(
122                                    node.getUuid(), portletDataContext.getScopeGroupId());
123    
124                            String initialNodeName = PropsValues.WIKI_INITIAL_NODE_NAME;
125    
126                            if ((existingNode == null) &&
127                                    initialNodeName.equals(node.getName())) {
128    
129                                    try {
130                                            WikiNodeUtil.removeByG_N(
131                                                    portletDataContext.getScopeGroupId(), node.getName());
132                                    }
133                                    catch (NoSuchNodeException nsne) {
134                                    }
135                            }
136    
137                            if (existingNode == null) {
138                                    serviceContext.setUuid(node.getUuid());
139    
140                                    importedNode = WikiNodeLocalServiceUtil.addNode(
141                                            userId, node.getName(), node.getDescription(),
142                                            serviceContext);
143                            }
144                            else {
145                                    importedNode = WikiNodeLocalServiceUtil.updateNode(
146                                            existingNode.getNodeId(), node.getName(),
147                                            node.getDescription(), serviceContext);
148                            }
149                    }
150                    else {
151                            String initialNodeName = PropsValues.WIKI_INITIAL_NODE_NAME;
152    
153                            if (initialNodeName.equals(node.getName())) {
154                                    try {
155                                            WikiNodeUtil.removeByG_N(
156                                                    portletDataContext.getScopeGroupId(), node.getName());
157                                    }
158                                    catch (NoSuchNodeException nsne) {
159                                    }
160                            }
161    
162                            String nodeName = getNodeName(
163                                    portletDataContext, node, node.getName(), 2);
164    
165                            importedNode = WikiNodeLocalServiceUtil.addNode(
166                                    userId, nodeName, node.getDescription(), serviceContext);
167                    }
168    
169                    portletDataContext.importClassedModel(node, importedNode, _NAMESPACE);
170            }
171    
172            public static void importPage(
173                            PortletDataContext portletDataContext, Element pageElement,
174                            WikiPage page)
175                    throws Exception {
176    
177                    long userId = portletDataContext.getUserId(page.getUserUuid());
178    
179                    Map<Long, Long> nodePKs =
180                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
181                                    WikiNode.class);
182    
183                    long nodeId = MapUtil.getLong(
184                            nodePKs, page.getNodeId(), page.getNodeId());
185    
186                    String content = JournalPortletDataHandlerImpl.importReferencedContent(
187                            portletDataContext, pageElement, page.getContent());
188    
189                    page.setContent(content);
190    
191                    ServiceContext serviceContext = portletDataContext.createServiceContext(
192                            pageElement, page, _NAMESPACE);
193    
194                    if (page.getStatus() != WorkflowConstants.STATUS_APPROVED) {
195                            serviceContext.setWorkflowAction(
196                                    WorkflowConstants.ACTION_SAVE_DRAFT);
197                    }
198    
199                    WikiPage importedPage = null;
200    
201                    WikiPage existingPage = WikiPageUtil.fetchByUUID_G(
202                            page.getUuid(), portletDataContext.getScopeGroupId());
203    
204                    if (existingPage == null) {
205                            try {
206                                    existingPage = WikiPageLocalServiceUtil.getPage(
207                                            nodeId, page.getTitle());
208                            }
209                            catch (NoSuchPageException nspe) {
210                            }
211                    }
212    
213                    if (existingPage == null) {
214                            serviceContext.setUuid(page.getUuid());
215    
216                            importedPage = WikiPageLocalServiceUtil.addPage(
217                                    userId, nodeId, page.getTitle(), page.getVersion(),
218                                    page.getContent(), page.getSummary(), page.isMinorEdit(),
219                                    page.getFormat(), page.getHead(), page.getParentTitle(),
220                                    page.getRedirectTitle(), serviceContext);
221                    }
222                    else {
223                            importedPage = WikiPageLocalServiceUtil.updatePage(
224                                    userId, nodeId, existingPage.getTitle(), 0, page.getContent(),
225                                    page.getSummary(), page.isMinorEdit(), page.getFormat(),
226                                    page.getParentTitle(), page.getRedirectTitle(), serviceContext);
227                    }
228    
229                    if (portletDataContext.getBooleanParameter(_NAMESPACE, "attachments") &&
230                            page.isHead()) {
231    
232                            for (Element attachmentElement :
233                                            pageElement.elements("attachment")) {
234    
235                                    String name = attachmentElement.attributeValue("name");
236                                    String binPath = attachmentElement.attributeValue("bin-path");
237    
238                                    InputStream inputStream = null;
239    
240                                    try {
241                                            inputStream = portletDataContext.getZipEntryAsInputStream(
242                                                    binPath);
243    
244                                            WikiPageLocalServiceUtil.addPageAttachment(
245                                                    importedPage.getCompanyId(),
246                                                    importedPage.getAttachmentsDir(),
247                                                    importedPage.getModifiedDate(), name, inputStream);
248                                    }
249                                    finally {
250                                            StreamUtil.cleanUp(inputStream);
251                                    }
252                            }
253                    }
254    
255                    portletDataContext.importClassedModel(page, importedPage, _NAMESPACE);
256            }
257    
258            @Override
259            public PortletDataHandlerControl[] getExportControls() {
260                    return new PortletDataHandlerControl[] {
261                            _nodesAndPages
262                    };
263            }
264    
265            @Override
266            public PortletDataHandlerControl[] getExportMetadataControls() {
267                    return new PortletDataHandlerControl[] {
268                            new PortletDataHandlerBoolean(
269                                    _NAMESPACE, "wiki-pages", true, _metadataControls)
270                    };
271            }
272    
273            @Override
274            public PortletDataHandlerControl[] getImportControls() {
275                    return new PortletDataHandlerControl[] {
276                            _nodesAndPages
277                    };
278            }
279    
280            @Override
281            public PortletDataHandlerControl[] getImportMetadataControls() {
282                    return new PortletDataHandlerControl[] {
283                            new PortletDataHandlerBoolean(
284                                    _NAMESPACE, "wiki-pages", true, _metadataControls)
285                    };
286            }
287    
288            @Override
289            public PortletPreferences importData(
290                            PortletDataContext portletDataContext, String portletId,
291                            PortletPreferences portletPreferences, String data)
292                    throws PortletDataException {
293    
294                    WikiCacheThreadLocal.setClearCache(false);
295    
296                    try {
297                            return super.importData(
298                                    portletDataContext, portletId, portletPreferences, data);
299                    }
300                    finally {
301                            WikiCacheThreadLocal.setClearCache(true);
302                    }
303            }
304    
305            protected static void exportNode(
306                            PortletDataContext portletDataContext, Element nodesElement,
307                            long nodeId)
308                    throws Exception {
309    
310                    if (!portletDataContext.hasDateRange()) {
311                            return;
312                    }
313    
314                    WikiNode node = WikiNodeUtil.findByPrimaryKey(nodeId);
315    
316                    String path = getNodePath(portletDataContext, node);
317    
318                    if (!portletDataContext.isPathNotProcessed(path)) {
319                            return;
320                    }
321    
322                    Element nodeElement = nodesElement.addElement("node");
323    
324                    portletDataContext.addClassedModel(nodeElement, path, node, _NAMESPACE);
325            }
326    
327            protected static void exportPage(
328                            PortletDataContext portletDataContext, Element nodesElement,
329                            Element pagesElement, Element dlFileEntryTypesElement,
330                            Element dlFoldersElement, Element dlFileEntriesElement,
331                            Element dlFileRanksElement, WikiPage page, boolean checkDateRange)
332                    throws Exception {
333    
334                    if (!portletDataContext.isWithinDateRange(page.getModifiedDate())) {
335                            return;
336                    }
337    
338                    String path = getPagePath(portletDataContext, page);
339    
340                    // Clone this page to make sure changes to its content are never
341                    // persisted
342    
343                    page = (WikiPage)page.clone();
344    
345                    Element pageElement = (Element)pagesElement.selectSingleNode(
346                            "//page[@path='".concat(path).concat("']"));
347    
348                    if (portletDataContext.isPathNotProcessed(path)) {
349                            if (pageElement == null) {
350                                    pageElement = pagesElement.addElement("page");
351                            }
352    
353                            String content =
354                                    JournalPortletDataHandlerImpl.exportReferencedContent(
355                                            portletDataContext, dlFileEntryTypesElement,
356                                            dlFoldersElement, dlFileEntriesElement, dlFileRanksElement,
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                    List<WikiNode> nodes = WikiNodeUtil.findByGroupId(
518                            portletDataContext.getScopeGroupId());
519    
520                    for (WikiNode node : nodes) {
521                            exportNode(portletDataContext, nodesElement, pagesElement, node);
522                    }
523    
524                    return document.formattedString();
525            }
526    
527            @Override
528            protected PortletPreferences doImportData(
529                            PortletDataContext portletDataContext, String portletId,
530                            PortletPreferences portletPreferences, String data)
531                    throws Exception {
532    
533                    portletDataContext.importPermissions(
534                            "com.liferay.portlet.wiki", portletDataContext.getSourceGroupId(),
535                            portletDataContext.getScopeGroupId());
536    
537                    Document document = SAXReaderUtil.read(data);
538    
539                    Element rootElement = document.getRootElement();
540    
541                    Element nodesElement = rootElement.element("nodes");
542    
543                    for (Element nodeElement : nodesElement.elements("node")) {
544                            String path = nodeElement.attributeValue("path");
545    
546                            if (!portletDataContext.isPathNotProcessed(path)) {
547                                    continue;
548                            }
549    
550                            WikiNode node = (WikiNode)portletDataContext.getZipEntryAsObject(
551                                    path);
552    
553                            importNode(portletDataContext, node);
554                    }
555    
556                    Element pagesElement = rootElement.element("pages");
557    
558                    JournalPortletDataHandlerImpl.importReferencedData(
559                            portletDataContext, pagesElement);
560    
561                    for (Element pageElement : pagesElement.elements("page")) {
562                            String path = pageElement.attributeValue("path");
563    
564                            if (!portletDataContext.isPathNotProcessed(path)) {
565                                    continue;
566                            }
567    
568                            WikiPage page = (WikiPage)portletDataContext.getZipEntryAsObject(
569                                    path);
570    
571                            importPage(portletDataContext, pageElement, page);
572                    }
573    
574                    Map<Long, Long> nodePKs =
575                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
576                                    WikiNode.class);
577    
578                    for (long nodeId : nodePKs.values()) {
579                            WikiCacheUtil.clearCache(nodeId);
580                    }
581    
582                    return null;
583            }
584    
585            private static final String _NAMESPACE = "wiki";
586    
587            private static PortletDataHandlerControl[] _metadataControls =
588                    new PortletDataHandlerControl[] {
589                            new PortletDataHandlerBoolean(_NAMESPACE, "attachments"),
590                            new PortletDataHandlerBoolean(_NAMESPACE, "categories"),
591                            new PortletDataHandlerBoolean(_NAMESPACE, "comments"),
592                            new PortletDataHandlerBoolean(_NAMESPACE, "ratings"),
593                            new PortletDataHandlerBoolean(_NAMESPACE, "tags")
594                    };
595    
596            private static PortletDataHandlerBoolean _nodesAndPages =
597                    new PortletDataHandlerBoolean(
598                            _NAMESPACE, "wikis-and-pages", true, true);
599    
600    }