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