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