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