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