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.MimeTypesUtil;
026    import com.liferay.portal.kernel.util.StreamUtil;
027    import com.liferay.portal.kernel.util.StringBundler;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.workflow.WorkflowConstants;
030    import com.liferay.portal.kernel.xml.Document;
031    import com.liferay.portal.kernel.xml.Element;
032    import com.liferay.portal.kernel.xml.SAXReaderUtil;
033    import com.liferay.portal.service.ServiceContext;
034    import com.liferay.portal.util.PortletKeys;
035    import com.liferay.portal.util.PropsValues;
036    import com.liferay.portlet.dynamicdatamapping.lar.DDMPortletDataHandler;
037    import com.liferay.portlet.journal.lar.JournalPortletDataHandler;
038    import com.liferay.portlet.wiki.NoSuchNodeException;
039    import com.liferay.portlet.wiki.NoSuchPageException;
040    import com.liferay.portlet.wiki.model.WikiNode;
041    import com.liferay.portlet.wiki.model.WikiPage;
042    import com.liferay.portlet.wiki.service.WikiNodeLocalServiceUtil;
043    import com.liferay.portlet.wiki.service.WikiPageLocalServiceUtil;
044    import com.liferay.portlet.wiki.service.persistence.WikiNodeUtil;
045    import com.liferay.portlet.wiki.service.persistence.WikiPageUtil;
046    import com.liferay.portlet.wiki.util.WikiCacheThreadLocal;
047    import com.liferay.portlet.wiki.util.WikiCacheUtil;
048    import com.liferay.portlet.wiki.util.comparator.PageVersionComparator;
049    
050    import java.io.InputStream;
051    
052    import java.util.List;
053    import java.util.Map;
054    
055    import javax.portlet.PortletPreferences;
056    
057    /**
058     * @author Bruno Farache
059     * @author Jorge Ferrer
060     * @author Marcellus Tavares
061     * @author Juan Fernández
062     */
063    public class WikiPortletDataHandler extends BasePortletDataHandler {
064    
065            public static final String NAMESPACE = "wiki";
066    
067            public static void exportNode(
068                            PortletDataContext portletDataContext, Element nodesElement,
069                            Element pagesElement, 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                    Element dlFileEntryTypesElement = pagesElement.addElement(
084                            "dl-file-entry-types");
085                    Element dlFoldersElement = pagesElement.addElement("dl-folders");
086                    Element dlFileEntriesElement = pagesElement.addElement(
087                            "dl-file-entries");
088                    Element dlFileRanksElement = pagesElement.addElement("dl-file-ranks");
089                    Element dlRepositoriesElement = pagesElement.addElement(
090                            "dl-repositories");
091                    Element dlRepositoryEntriesElement = pagesElement.addElement(
092                            "dl-repository-entries");
093    
094                    List<WikiPage> pages = WikiPageUtil.findByN_S(
095                            node.getNodeId(), WorkflowConstants.STATUS_APPROVED,
096                            QueryUtil.ALL_POS, QueryUtil.ALL_POS,
097                            new PageVersionComparator(true));
098    
099                    for (WikiPage page : pages) {
100                            exportPage(
101                                    portletDataContext, nodesElement, pagesElement,
102                                    dlFileEntryTypesElement, dlFoldersElement, dlFileEntriesElement,
103                                    dlFileRanksElement, dlRepositoriesElement,
104                                    dlRepositoryEntriesElement, page, true);
105                    }
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 = JournalPortletDataHandler.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                                    String mimeType = null;
244    
245                                    try {
246                                            inputStream = portletDataContext.getZipEntryAsInputStream(
247                                                    binPath);
248    
249                                            mimeType = MimeTypesUtil.getContentType(inputStream, name);
250                                    }
251                                    finally {
252                                            StreamUtil.cleanUp(inputStream);
253                                    }
254    
255                                    try {
256                                            inputStream = portletDataContext.getZipEntryAsInputStream(
257                                                    binPath);
258    
259                                            WikiPageLocalServiceUtil.addPageAttachment(
260                                                    userId, importedPage.getNodeId(),
261                                                    importedPage.getTitle(), name, inputStream, mimeType);
262                                    }
263                                    finally {
264                                            StreamUtil.cleanUp(inputStream);
265                                    }
266                            }
267                    }
268    
269                    portletDataContext.importClassedModel(page, importedPage, NAMESPACE);
270            }
271    
272            public WikiPortletDataHandler() {
273                    setExportControls(
274                            new PortletDataHandlerBoolean(
275                                    NAMESPACE, "wikis-and-pages", true, true));
276                    setExportMetadataControls(
277                            new PortletDataHandlerBoolean(
278                                    NAMESPACE, "wiki-pages", true,
279                                    new PortletDataHandlerControl[] {
280                                            new PortletDataHandlerBoolean(NAMESPACE, "attachments"),
281                                            new PortletDataHandlerBoolean(NAMESPACE, "categories"),
282                                            new PortletDataHandlerBoolean(NAMESPACE, "comments"),
283                                            new PortletDataHandlerBoolean(NAMESPACE, "ratings"),
284                                            new PortletDataHandlerBoolean(NAMESPACE, "tags")
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, Element dlRepositoriesElement,
332                            Element dlRepositoryEntriesElement, WikiPage page,
333                            boolean checkDateRange)
334                    throws Exception {
335    
336                    if (!portletDataContext.isWithinDateRange(page.getModifiedDate())) {
337                            return;
338                    }
339    
340                    String path = getPagePath(portletDataContext, page);
341    
342                    // Clone this page to make sure changes to its content are never
343                    // persisted
344    
345                    page = (WikiPage)page.clone();
346    
347                    Element pageElement = (Element)pagesElement.selectSingleNode(
348                            "//page[@path='".concat(path).concat("']"));
349    
350                    if (portletDataContext.isPathNotProcessed(path)) {
351                            if (pageElement == null) {
352                                    pageElement = pagesElement.addElement("page");
353                            }
354    
355                            String content = DDMPortletDataHandler.exportReferencedContent(
356                                    portletDataContext, dlFileEntryTypesElement, dlFoldersElement,
357                                    dlFileEntriesElement, dlFileRanksElement, dlRepositoriesElement,
358                                    dlRepositoryEntriesElement, pageElement, page.getContent());
359    
360                            page.setContent(content);
361    
362                            String imagePath = getPageImagePath(portletDataContext, page);
363    
364                            pageElement.addAttribute("image-path", imagePath);
365    
366                            if (portletDataContext.getBooleanParameter(
367                                            NAMESPACE, "attachments") &&
368                                    page.isHead()) {
369    
370                                    int i = 0;
371    
372                                    for (FileEntry fileEntry : page.getAttachmentsFileEntries()) {
373                                            Element attachmentElement = pageElement.addElement(
374                                                    "attachment");
375    
376                                            attachmentElement.addAttribute(
377                                                    "name", fileEntry.getTitle());
378    
379                                            String binPath = getPageAttachementBinPath(
380                                                    portletDataContext, page, i++);
381    
382                                            attachmentElement.addAttribute("bin-path", binPath);
383    
384                                            portletDataContext.addZipEntry(
385                                                    binPath, fileEntry.getContentStream());
386                                    }
387    
388                                    page.setAttachmentsFolderId(page.getAttachmentsFolderId());
389                            }
390    
391                            portletDataContext.addClassedModel(
392                                    pageElement, path, page, NAMESPACE);
393                    }
394    
395                    exportNode(portletDataContext, nodesElement, page.getNodeId());
396            }
397    
398            protected static String getNodeName(
399                            PortletDataContext portletDataContext, WikiNode node, String name,
400                            int count)
401                    throws Exception {
402    
403                    WikiNode existingNode = WikiNodeUtil.fetchByG_N(
404                            portletDataContext.getScopeGroupId(), name);
405    
406                    if (existingNode == null) {
407                            return name;
408                    }
409    
410                    String nodeName = node.getName();
411    
412                    return getNodeName(
413                            portletDataContext, node,
414                            nodeName.concat(StringPool.SPACE).concat(String.valueOf(count)),
415                            ++count);
416            }
417    
418            protected static String getNodePath(
419                    PortletDataContext portletDataContext, WikiNode node) {
420    
421                    StringBundler sb = new StringBundler(4);
422    
423                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
424                    sb.append("/nodes/");
425                    sb.append(node.getNodeId());
426                    sb.append(".xml");
427    
428                    return sb.toString();
429            }
430    
431            protected static String getPageAttachementBinPath(
432                    PortletDataContext portletDataContext, WikiPage page, int count) {
433    
434                    StringBundler sb = new StringBundler(6);
435    
436                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
437                    sb.append("/bin/");
438                    sb.append(page.getPageId());
439                    sb.append(StringPool.SLASH);
440                    sb.append("attachement");
441                    sb.append(count);
442    
443                    return sb.toString();
444            }
445    
446            protected static String getPageImagePath(
447                            PortletDataContext portletDataContext, WikiPage page)
448                    throws Exception {
449    
450                    StringBundler sb = new StringBundler(6);
451    
452                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
453                    sb.append("/page/");
454                    sb.append(page.getUuid());
455                    sb.append(StringPool.SLASH);
456                    sb.append(page.getVersion());
457                    sb.append(StringPool.SLASH);
458    
459                    return sb.toString();
460            }
461    
462            protected static String getPagePath(
463                    PortletDataContext portletDataContext, WikiPage page) {
464    
465                    StringBundler sb = new StringBundler(4);
466    
467                    sb.append(portletDataContext.getPortletPath(PortletKeys.WIKI));
468                    sb.append("/pages/");
469                    sb.append(page.getPageId());
470                    sb.append(".xml");
471    
472                    return sb.toString();
473            }
474    
475            @Override
476            protected PortletPreferences doDeleteData(
477                            PortletDataContext portletDataContext, String portletId,
478                            PortletPreferences portletPreferences)
479                    throws Exception {
480    
481                    if (portletDataContext.addPrimaryKey(
482                                    WikiPortletDataHandler.class, "deleteData")) {
483    
484                            return portletPreferences;
485                    }
486    
487                    WikiNodeLocalServiceUtil.deleteNodes(
488                            portletDataContext.getScopeGroupId());
489    
490                    return portletPreferences;
491            }
492    
493            @Override
494            protected String doExportData(
495                            PortletDataContext portletDataContext, String portletId,
496                            PortletPreferences portletPreferences)
497                    throws Exception {
498    
499                    portletDataContext.addPermissions(
500                            "com.liferay.portlet.wiki", portletDataContext.getScopeGroupId());
501    
502                    Element rootElement = addExportRootElement();
503    
504                    rootElement.addAttribute(
505                            "group-id", String.valueOf(portletDataContext.getScopeGroupId()));
506    
507                    Element nodesElement = rootElement.addElement("nodes");
508                    Element pagesElement = rootElement.addElement("pages");
509    
510                    List<WikiNode> nodes = WikiNodeUtil.findByGroupId(
511                            portletDataContext.getScopeGroupId());
512    
513                    for (WikiNode node : nodes) {
514                            exportNode(portletDataContext, nodesElement, pagesElement, node);
515                    }
516    
517                    return rootElement.formattedString();
518            }
519    
520            @Override
521            protected PortletPreferences doImportData(
522                            PortletDataContext portletDataContext, String portletId,
523                            PortletPreferences portletPreferences, String data)
524                    throws Exception {
525    
526                    portletDataContext.importPermissions(
527                            "com.liferay.portlet.wiki", portletDataContext.getSourceGroupId(),
528                            portletDataContext.getScopeGroupId());
529    
530                    Document document = SAXReaderUtil.read(data);
531    
532                    Element rootElement = document.getRootElement();
533    
534                    Element nodesElement = rootElement.element("nodes");
535    
536                    for (Element nodeElement : nodesElement.elements("node")) {
537                            String path = nodeElement.attributeValue("path");
538    
539                            if (!portletDataContext.isPathNotProcessed(path)) {
540                                    continue;
541                            }
542    
543                            WikiNode node = (WikiNode)portletDataContext.getZipEntryAsObject(
544                                    path);
545    
546                            importNode(portletDataContext, node);
547                    }
548    
549                    Element pagesElement = rootElement.element("pages");
550    
551                    JournalPortletDataHandler.importReferencedData(
552                            portletDataContext, pagesElement);
553    
554                    for (Element pageElement : pagesElement.elements("page")) {
555                            String path = pageElement.attributeValue("path");
556    
557                            if (!portletDataContext.isPathNotProcessed(path)) {
558                                    continue;
559                            }
560    
561                            WikiPage page = (WikiPage)portletDataContext.getZipEntryAsObject(
562                                    path);
563    
564                            importPage(portletDataContext, pageElement, page);
565                    }
566    
567                    Map<Long, Long> nodeIds =
568                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
569                                    WikiNode.class);
570    
571                    for (long nodeId : nodeIds.values()) {
572                            WikiCacheUtil.clearCache(nodeId);
573                    }
574    
575                    return null;
576            }
577    
578    }