001    /**
002     * Copyright (c) 2000-present 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.messageboards.lar;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryUtil;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.repository.model.FileEntry;
022    import com.liferay.portal.kernel.trash.TrashHandler;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.MapUtil;
025    import com.liferay.portal.kernel.util.ObjectValuePair;
026    import com.liferay.portal.kernel.util.StreamUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.kernel.xml.Element;
029    import com.liferay.portal.service.ServiceContext;
030    import com.liferay.portlet.documentlibrary.lar.FileEntryUtil;
031    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
032    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
033    import com.liferay.portlet.exportimport.lar.BaseStagedModelDataHandler;
034    import com.liferay.portlet.exportimport.lar.ExportImportPathUtil;
035    import com.liferay.portlet.exportimport.lar.PortletDataContext;
036    import com.liferay.portlet.exportimport.lar.StagedModelDataHandlerUtil;
037    import com.liferay.portlet.exportimport.lar.StagedModelModifiedDateComparator;
038    import com.liferay.portlet.messageboards.model.MBCategory;
039    import com.liferay.portlet.messageboards.model.MBCategoryConstants;
040    import com.liferay.portlet.messageboards.model.MBDiscussion;
041    import com.liferay.portlet.messageboards.model.MBMessage;
042    import com.liferay.portlet.messageboards.model.MBThread;
043    import com.liferay.portlet.messageboards.service.MBDiscussionLocalServiceUtil;
044    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
045    import com.liferay.portlet.messageboards.service.MBThreadLocalServiceUtil;
046    import com.liferay.portlet.ratings.model.RatingsEntry;
047    import com.liferay.portlet.ratings.service.RatingsEntryLocalServiceUtil;
048    
049    import java.io.InputStream;
050    
051    import java.util.ArrayList;
052    import java.util.Collections;
053    import java.util.List;
054    import java.util.Map;
055    
056    /**
057     * @author Daniel Kocsis
058     */
059    public class MBMessageStagedModelDataHandler
060            extends BaseStagedModelDataHandler<MBMessage> {
061    
062            public static final String[] CLASS_NAMES = {MBMessage.class.getName()};
063    
064            @Override
065            public void deleteStagedModel(MBMessage message) throws PortalException {
066                    MBMessageLocalServiceUtil.deleteMessage(message);
067            }
068    
069            @Override
070            public void deleteStagedModel(
071                            String uuid, long groupId, String className, String extraData)
072                    throws PortalException {
073    
074                    MBMessage message = fetchStagedModelByUuidAndGroupId(uuid, groupId);
075    
076                    if (message != null) {
077                            deleteStagedModel(message);
078                    }
079            }
080    
081            @Override
082            public MBMessage fetchStagedModelByUuidAndGroupId(
083                    String uuid, long groupId) {
084    
085                    return MBMessageLocalServiceUtil.fetchMBMessageByUuidAndGroupId(
086                            uuid, groupId);
087            }
088    
089            @Override
090            public List<MBMessage> fetchStagedModelsByUuidAndCompanyId(
091                    String uuid, long companyId) {
092    
093                    return MBMessageLocalServiceUtil.getMBMessagesByUuidAndCompanyId(
094                            uuid, companyId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
095                            new StagedModelModifiedDateComparator<MBMessage>());
096            }
097    
098            @Override
099            public String[] getClassNames() {
100                    return CLASS_NAMES;
101            }
102    
103            @Override
104            public String getDisplayName(MBMessage message) {
105                    return message.getSubject();
106            }
107    
108            protected MBMessage addDiscussionMessage(
109                            PortletDataContext portletDataContext, long userId, long threadId,
110                            long parentMessageId, MBMessage message,
111                            ServiceContext serviceContext)
112                    throws PortalException {
113    
114                    MBDiscussion discussion =
115                            MBDiscussionLocalServiceUtil.getThreadDiscussion(threadId);
116    
117                    MBMessage importedMessage = null;
118    
119                    if (!message.isRoot()) {
120                            importedMessage = MBMessageLocalServiceUtil.addDiscussionMessage(
121                                    userId, message.getUserName(),
122                                    portletDataContext.getScopeGroupId(), discussion.getClassName(),
123                                    discussion.getClassPK(), threadId, parentMessageId,
124                                    message.getSubject(), message.getBody(), serviceContext);
125                    }
126                    else {
127                            MBThread thread = MBThreadLocalServiceUtil.getThread(threadId);
128    
129                            importedMessage = MBMessageLocalServiceUtil.getMBMessage(
130                                    thread.getRootMessageId());
131                    }
132    
133                    return importedMessage;
134            }
135    
136            @Override
137            protected void doExportStagedModel(
138                            PortletDataContext portletDataContext, MBMessage message)
139                    throws Exception {
140    
141                    if (message.isDiscussion()) {
142                            MBDiscussion discussion =
143                                    MBDiscussionLocalServiceUtil.getDiscussion(
144                                            message.getClassName(), message.getClassPK());
145    
146                            StagedModelDataHandlerUtil.exportReferenceStagedModel(
147                                    portletDataContext, message, discussion,
148                                    PortletDataContext.REFERENCE_TYPE_PARENT);
149    
150                            // Ratings that belong to discussion messages cannot be exported
151                            // automatically because of the special class name and class PK pair
152    
153                            List<RatingsEntry> ratingsEntries =
154                                    RatingsEntryLocalServiceUtil.getEntries(
155                                            MBDiscussion.class.getName(), message.getMessageId());
156    
157                            for (RatingsEntry ratingsEntry : ratingsEntries) {
158                                    StagedModelDataHandlerUtil.exportReferenceStagedModel(
159                                            portletDataContext, message, ratingsEntry,
160                                            PortletDataContext.REFERENCE_TYPE_WEAK);
161                            }
162                    }
163                    else if (message.getCategoryId() !=
164                                            MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
165    
166                            StagedModelDataHandlerUtil.exportReferenceStagedModel(
167                                    portletDataContext, message, message.getCategory(),
168                                    PortletDataContext.REFERENCE_TYPE_PARENT);
169                    }
170    
171                    if (!message.isRoot()) {
172                            MBMessage parentMessage = MBMessageLocalServiceUtil.getMessage(
173                                    message.getParentMessageId());
174    
175                            StagedModelDataHandlerUtil.exportReferenceStagedModel(
176                                    portletDataContext, message, parentMessage,
177                                    PortletDataContext.REFERENCE_TYPE_PARENT);
178                    }
179    
180                    message.setPriority(message.getPriority());
181    
182                    MBThread thread = message.getThread();
183    
184                    Element messageElement = portletDataContext.getExportDataElement(
185                            message);
186    
187                    messageElement.addAttribute(
188                            "question", String.valueOf(thread.isQuestion()));
189                    messageElement.addAttribute("threadUuid", thread.getUuid());
190    
191                    boolean hasAttachmentsFileEntries =
192                            message.getAttachmentsFileEntriesCount() > 0;
193    
194                    messageElement.addAttribute(
195                            "hasAttachmentsFileEntries",
196                            String.valueOf(hasAttachmentsFileEntries));
197    
198                    if (hasAttachmentsFileEntries) {
199                            for (FileEntry fileEntry : message.getAttachmentsFileEntries()) {
200                                    StagedModelDataHandlerUtil.exportReferenceStagedModel(
201                                            portletDataContext, message, fileEntry,
202                                            PortletDataContext.REFERENCE_TYPE_WEAK);
203                            }
204    
205                            long folderId = message.getAttachmentsFolderId();
206    
207                            if (folderId != DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
208                                    message.setAttachmentsFolderId(folderId);
209                            }
210                    }
211    
212                    portletDataContext.addClassedModel(
213                            messageElement, ExportImportPathUtil.getModelPath(message),
214                            message);
215            }
216    
217            @Override
218            protected void doImportStagedModel(
219                            PortletDataContext portletDataContext, MBMessage message)
220                    throws Exception {
221    
222                    if (!message.isRoot()) {
223                            StagedModelDataHandlerUtil.importReferenceStagedModel(
224                                    portletDataContext, message, MBMessage.class,
225                                    message.getParentMessageId());
226                    }
227    
228                    long userId = portletDataContext.getUserId(message.getUserUuid());
229    
230                    Map<Long, Long> categoryIds =
231                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
232                                    MBCategory.class);
233    
234                    long parentCategoryId = MapUtil.getLong(
235                            categoryIds, message.getCategoryId(), message.getCategoryId());
236    
237                    Map<Long, Long> threadIds =
238                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
239                                    MBThread.class);
240    
241                    long threadId = MapUtil.getLong(threadIds, message.getThreadId(), 0);
242    
243                    Element messageElement =
244                            portletDataContext.getImportDataStagedModelElement(message);
245    
246                    if (threadId == 0) {
247                            String threadUuid = messageElement.attributeValue("threadUuid");
248    
249                            MBThread thread =
250                                    MBThreadLocalServiceUtil.fetchMBThreadByUuidAndGroupId(
251                                            threadUuid, portletDataContext.getScopeGroupId());
252    
253                            if (thread != null) {
254                                    threadId = thread.getThreadId();
255                            }
256                    }
257    
258                    Map<Long, Long> messageIds =
259                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
260                                    MBMessage.class);
261    
262                    long parentMessageId = MapUtil.getLong(
263                            messageIds, message.getParentMessageId(),
264                            message.getParentMessageId());
265    
266                    List<ObjectValuePair<String, InputStream>> inputStreamOVPs =
267                            getAttachments(portletDataContext, messageElement, message);
268    
269                    try {
270                            ServiceContext serviceContext =
271                                    portletDataContext.createServiceContext(message);
272    
273                            MBMessage importedMessage = null;
274    
275                            if (portletDataContext.isDataStrategyMirror()) {
276                                    MBMessage existingMessage = fetchStagedModelByUuidAndGroupId(
277                                            message.getUuid(), portletDataContext.getScopeGroupId());
278    
279                                    if (existingMessage == null) {
280                                            serviceContext.setUuid(message.getUuid());
281    
282                                            if (message.isDiscussion()) {
283                                                    importedMessage = addDiscussionMessage(
284                                                            portletDataContext, userId, threadId,
285                                                            parentMessageId, message, serviceContext);
286                                            }
287                                            else {
288                                                    importedMessage = MBMessageLocalServiceUtil.addMessage(
289                                                            userId, message.getUserName(),
290                                                            portletDataContext.getScopeGroupId(),
291                                                            parentCategoryId, threadId, parentMessageId,
292                                                            message.getSubject(), message.getBody(),
293                                                            message.getFormat(), inputStreamOVPs,
294                                                            message.getAnonymous(), message.getPriority(),
295                                                            message.getAllowPingbacks(), serviceContext);
296                                            }
297                                    }
298                                    else {
299                                            if (!message.isRoot() && message.isDiscussion()) {
300                                                    MBDiscussion discussion =
301                                                            MBDiscussionLocalServiceUtil.getThreadDiscussion(
302                                                                    threadId);
303    
304                                                    importedMessage =
305                                                            MBMessageLocalServiceUtil.updateDiscussionMessage(
306                                                                    userId, existingMessage.getMessageId(),
307                                                                    discussion.getClassName(),
308                                                                    discussion.getClassPK(), message.getSubject(),
309                                                                    message.getBody(), serviceContext);
310                                            }
311                                            else {
312                                                    importedMessage =
313                                                            MBMessageLocalServiceUtil.updateMessage(
314                                                                    userId, existingMessage.getMessageId(),
315                                                                    message.getSubject(), message.getBody(),
316                                                                    inputStreamOVPs, new ArrayList<String>(),
317                                                                    message.getPriority(),
318                                                                    message.getAllowPingbacks(), serviceContext);
319                                            }
320                                    }
321                            }
322                            else {
323                                    if (message.isDiscussion()) {
324                                            importedMessage = addDiscussionMessage(
325                                                    portletDataContext, userId, threadId, parentMessageId,
326                                                    message, serviceContext);
327                                    }
328                                    else {
329                                            importedMessage = MBMessageLocalServiceUtil.addMessage(
330                                                    userId, message.getUserName(),
331                                                    portletDataContext.getScopeGroupId(), parentCategoryId,
332                                                    threadId, parentMessageId, message.getSubject(),
333                                                    message.getBody(), message.getFormat(), inputStreamOVPs,
334                                                    message.getAnonymous(), message.getPriority(),
335                                                    message.getAllowPingbacks(), serviceContext);
336                                    }
337                            }
338    
339                            MBMessageLocalServiceUtil.updateAnswer(
340                                    importedMessage, message.isAnswer(), false);
341    
342                            if (importedMessage.isRoot() && !importedMessage.isDiscussion()) {
343                                    MBThreadLocalServiceUtil.updateQuestion(
344                                            importedMessage.getThreadId(),
345                                            GetterUtil.getBoolean(
346                                                    messageElement.attributeValue("question")));
347                            }
348    
349                            if (message.isDiscussion()) {
350                                    Map<Long, Long> discussionIds =
351                                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
352                                                    MBDiscussion.class);
353    
354                                    discussionIds.put(
355                                            message.getMessageId(), importedMessage.getMessageId());
356                            }
357    
358                            threadIds.put(message.getThreadId(), importedMessage.getThreadId());
359    
360                            // Keep thread UUID
361    
362                            MBThread thread = importedMessage.getThread();
363    
364                            thread.setUuid(messageElement.attributeValue("threadUuid"));
365    
366                            MBThreadLocalServiceUtil.updateMBThread(thread);
367    
368                            portletDataContext.importClassedModel(message, importedMessage);
369                    }
370                    finally {
371                            for (ObjectValuePair<String, InputStream> inputStreamOVP :
372                                            inputStreamOVPs) {
373    
374                                    InputStream inputStream = inputStreamOVP.getValue();
375    
376                                    StreamUtil.cleanUp(inputStream);
377                            }
378                    }
379            }
380    
381            @Override
382            protected void doRestoreStagedModel(
383                            PortletDataContext portletDataContext, MBMessage message)
384                    throws Exception {
385    
386                    long userId = portletDataContext.getUserId(message.getUserUuid());
387    
388                    MBMessage existingMessage = fetchStagedModelByUuidAndGroupId(
389                            message.getUuid(), portletDataContext.getScopeGroupId());
390    
391                    if (existingMessage == null) {
392                            return;
393                    }
394    
395                    if (existingMessage.isInTrash()) {
396                            TrashHandler trashHandler = existingMessage.getTrashHandler();
397    
398                            if (trashHandler.isRestorable(existingMessage.getMessageId())) {
399                                    trashHandler.restoreTrashEntry(
400                                            userId, existingMessage.getMessageId());
401                            }
402                    }
403    
404                    if (existingMessage.isInTrashContainer()) {
405                            MBThread existingThread = existingMessage.getThread();
406    
407                            TrashHandler trashHandler = existingThread.getTrashHandler();
408    
409                            if (trashHandler.isRestorable(existingThread.getThreadId())) {
410                                    trashHandler.restoreTrashEntry(
411                                            userId, existingThread.getThreadId());
412                            }
413                    }
414            }
415    
416            protected List<ObjectValuePair<String, InputStream>> getAttachments(
417                    PortletDataContext portletDataContext, Element messageElement,
418                    MBMessage message) {
419    
420                    boolean hasAttachmentsFileEntries = GetterUtil.getBoolean(
421                            messageElement.attributeValue("hasAttachmentsFileEntries"));
422    
423                    if (!hasAttachmentsFileEntries) {
424                            return Collections.emptyList();
425                    }
426    
427                    List<ObjectValuePair<String, InputStream>> inputStreamOVPs =
428                            new ArrayList<>();
429    
430                    List<Element> attachmentElements =
431                            portletDataContext.getReferenceDataElements(
432                                    messageElement, DLFileEntry.class,
433                                    PortletDataContext.REFERENCE_TYPE_WEAK);
434    
435                    for (Element attachmentElement : attachmentElements) {
436                            String path = attachmentElement.attributeValue("path");
437    
438                            FileEntry fileEntry =
439                                    (FileEntry)portletDataContext.getZipEntryAsObject(path);
440    
441                            InputStream inputStream = null;
442    
443                            String binPath = attachmentElement.attributeValue("bin-path");
444    
445                            if (Validator.isNull(binPath) &&
446                                    portletDataContext.isPerformDirectBinaryImport()) {
447    
448                                    try {
449                                            inputStream = FileEntryUtil.getContentStream(fileEntry);
450                                    }
451                                    catch (Exception e) {
452                                    }
453                            }
454                            else {
455                                    inputStream = portletDataContext.getZipEntryAsInputStream(
456                                            binPath);
457                            }
458    
459                            if (inputStream == null) {
460                                    if (_log.isWarnEnabled()) {
461                                            _log.warn(
462                                                    "Unable to import attachment for file entry " +
463                                                            fileEntry.getFileEntryId());
464                                    }
465    
466                                    continue;
467                            }
468    
469                            ObjectValuePair<String, InputStream> inputStreamOVP =
470                                    new ObjectValuePair<>(fileEntry.getTitle(), inputStream);
471    
472                            inputStreamOVPs.add(inputStreamOVP);
473                    }
474    
475                    if (inputStreamOVPs.isEmpty()) {
476                            _log.error(
477                                    "Could not find attachments for message " +
478                                            message.getMessageId());
479                    }
480    
481                    return inputStreamOVPs;
482            }
483    
484            private static final Log _log = LogFactoryUtil.getLog(
485                    MBMessageStagedModelDataHandler.class);
486    
487    }