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.portal.kernel.lar;
016    
017    import com.liferay.portal.NoSuchModelException;
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.util.ArrayUtil;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.MapUtil;
024    import com.liferay.portal.kernel.workflow.WorkflowConstants;
025    import com.liferay.portal.kernel.xml.Element;
026    import com.liferay.portal.model.Group;
027    import com.liferay.portal.model.LocalizedModel;
028    import com.liferay.portal.model.StagedModel;
029    import com.liferay.portal.model.TrashedModel;
030    import com.liferay.portal.model.WorkflowedModel;
031    import com.liferay.portal.service.GroupLocalServiceUtil;
032    import com.liferay.portlet.asset.model.AssetCategory;
033    import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
034    import com.liferay.portlet.messageboards.model.MBDiscussion;
035    import com.liferay.portlet.messageboards.model.MBMessage;
036    import com.liferay.portlet.messageboards.service.MBDiscussionLocalServiceUtil;
037    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
038    import com.liferay.portlet.ratings.model.RatingsEntry;
039    import com.liferay.portlet.ratings.service.RatingsEntryLocalServiceUtil;
040    import com.liferay.portlet.sitesadmin.lar.StagedGroup;
041    
042    import java.util.ArrayList;
043    import java.util.HashMap;
044    import java.util.List;
045    import java.util.Map;
046    
047    /**
048     * @author Mate Thurzo
049     * @author Daniel Kocsis
050     * @author Zsolt Berentey
051     */
052    public abstract class BaseStagedModelDataHandler<T extends StagedModel>
053            implements StagedModelDataHandler<T> {
054    
055            @Override
056            public abstract void deleteStagedModel(
057                            String uuid, long groupId, String className, String extraData)
058                    throws PortalException;
059    
060            @Override
061            public void exportStagedModel(
062                            PortletDataContext portletDataContext, T stagedModel)
063                    throws PortletDataException {
064    
065                    validateExport(portletDataContext, stagedModel);
066    
067                    String path = ExportImportPathUtil.getModelPath(stagedModel);
068    
069                    if (portletDataContext.isPathExportedInScope(path)) {
070                            return;
071                    }
072    
073                    try {
074                            ManifestSummary manifestSummary =
075                                    portletDataContext.getManifestSummary();
076    
077                            PortletDataHandlerStatusMessageSenderUtil.sendStatusMessage(
078                                    "stagedModel", stagedModel, manifestSummary);
079    
080                            doExportStagedModel(portletDataContext, (T)stagedModel.clone());
081    
082                            exportAssetCategories(portletDataContext, stagedModel);
083                            exportComments(portletDataContext, stagedModel);
084                            exportRatings(portletDataContext, stagedModel);
085    
086                            if (countStagedModel(portletDataContext, stagedModel)) {
087                                    manifestSummary.incrementModelAdditionCount(
088                                            stagedModel.getStagedModelType());
089                            }
090    
091                            portletDataContext.cleanUpMissingReferences(stagedModel);
092                    }
093                    catch (PortletDataException pde) {
094                            throw pde;
095                    }
096                    catch (Exception e) {
097                            PortletDataException pde = new PortletDataException(e);
098    
099                            if (e instanceof NoSuchModelException) {
100                                    pde.setStagedModel(stagedModel);
101                                    pde.setType(PortletDataException.MISSING_DEPENDENCY);
102                            }
103    
104                            throw pde;
105                    }
106            }
107    
108            @Override
109            public T fetchMissingReference(String uuid, long groupId) {
110    
111                    // Try to fetch the existing staged model from the importing group
112    
113                    T existingStagedModel = fetchStagedModelByUuidAndGroupId(uuid, groupId);
114    
115                    if (existingStagedModel != null) {
116                            return existingStagedModel;
117                    }
118    
119                    try {
120    
121                            // Try to fetch the existing staged model from the parent sites
122    
123                            Group originalGroup = GroupLocalServiceUtil.getGroup(groupId);
124    
125                            Group group = originalGroup.getParentGroup();
126    
127                            while (group != null) {
128                                    existingStagedModel = fetchStagedModelByUuidAndGroupId(
129                                            uuid, group.getGroupId());
130    
131                                    if (existingStagedModel != null) {
132                                            break;
133                                    }
134    
135                                    group = group.getParentGroup();
136                            }
137    
138                            if (existingStagedModel == null) {
139                                    existingStagedModel = fetchStagedModelByUuidAndCompanyId(
140                                            uuid, originalGroup.getCompanyId());
141                            }
142    
143                            return existingStagedModel;
144                    }
145                    catch (Exception e) {
146                            if (_log.isDebugEnabled()) {
147                                    _log.debug(e, e);
148                            }
149                            else if (_log.isWarnEnabled()) {
150                                    _log.warn(
151                                            "Unable to fetch missing reference staged model from " +
152                                                    "group " + groupId);
153                            }
154    
155                            return null;
156                    }
157            }
158    
159            @Override
160            public abstract T fetchStagedModelByUuidAndCompanyId(
161                    String uuid, long companyId);
162    
163            @Override
164            public T fetchStagedModelByUuidAndGroupId(String uuid, long groupId) {
165                    return null;
166            }
167    
168            @Override
169            public abstract String[] getClassNames();
170    
171            @Override
172            public String getDisplayName(T stagedModel) {
173                    return stagedModel.getUuid();
174            }
175    
176            @Override
177            public int[] getExportableStatuses() {
178                    return new int[] {WorkflowConstants.STATUS_APPROVED};
179            }
180    
181            @Override
182            public Map<String, String> getReferenceAttributes(
183                    PortletDataContext portletDataContext, T stagedModel) {
184    
185                    return new HashMap<String, String>();
186            }
187    
188            /**
189             * @deprecated As of 7.0.0, replaced by {@link
190             *             #importMissingReference(PortletDataContext, Element)}
191             */
192            @Deprecated
193            @Override
194            public void importCompanyStagedModel(
195                            PortletDataContext portletDataContext, Element referenceElement)
196                    throws PortletDataException {
197    
198                    importMissingReference(portletDataContext, referenceElement);
199            }
200    
201            /**
202             * @deprecated As of 7.0.0, replaced by {@link
203             *             #importMissingReference(PortletDataContext, String, long,
204             *             long)}
205             */
206            @Deprecated
207            @Override
208            public void importCompanyStagedModel(
209                            PortletDataContext portletDataContext, String uuid, long classPK)
210                    throws PortletDataException {
211    
212                    importMissingReference(
213                            portletDataContext, uuid, portletDataContext.getCompanyGroupId(),
214                            classPK);
215            }
216    
217            @Override
218            public void importMissingReference(
219                            PortletDataContext portletDataContext, Element referenceElement)
220                    throws PortletDataException {
221    
222                    importMissingGroupReference(portletDataContext, referenceElement);
223    
224                    String uuid = referenceElement.attributeValue("uuid");
225    
226                    Map<Long, Long> groupIds =
227                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
228                                    Group.class);
229    
230                    long liveGroupId = GetterUtil.getLong(
231                            referenceElement.attributeValue("live-group-id"));
232    
233                    liveGroupId = MapUtil.getLong(groupIds, liveGroupId);
234    
235                    long classPK = GetterUtil.getLong(
236                            referenceElement.attributeValue("class-pk"));
237    
238                    importMissingReference(portletDataContext, uuid, liveGroupId, classPK);
239            }
240    
241            @Override
242            public void importMissingReference(
243                            PortletDataContext portletDataContext, String uuid, long groupId,
244                            long classPK)
245                    throws PortletDataException {
246    
247                    try {
248                            doImportMissingReference(
249                                    portletDataContext, uuid, groupId, classPK);
250                    }
251                    catch (PortletDataException pde) {
252                            throw pde;
253                    }
254                    catch (Exception e) {
255                            throw new PortletDataException(e);
256                    }
257            }
258    
259            @Override
260            public void importStagedModel(
261                            PortletDataContext portletDataContext, T stagedModel)
262                    throws PortletDataException {
263    
264                    String path = ExportImportPathUtil.getModelPath(stagedModel);
265    
266                    if (portletDataContext.isPathProcessed(path)) {
267                            return;
268                    }
269    
270                    try {
271                            ManifestSummary manifestSummary =
272                                    portletDataContext.getManifestSummary();
273    
274                            PortletDataHandlerStatusMessageSenderUtil.sendStatusMessage(
275                                    "stagedModel", stagedModel, manifestSummary);
276    
277                            if (stagedModel instanceof LocalizedModel) {
278                                    LocalizedModel localizedModel = (LocalizedModel)stagedModel;
279    
280                                    localizedModel.prepareLocalizedFieldsForImport();
281                            }
282    
283                            if (stagedModel instanceof TrashedModel) {
284                                    restoreStagedModel(portletDataContext, stagedModel);
285                            }
286    
287                            importAssetCategories(portletDataContext, stagedModel);
288    
289                            importReferenceStagedModels(portletDataContext, stagedModel);
290    
291                            doImportStagedModel(portletDataContext, stagedModel);
292    
293                            importComments(portletDataContext, stagedModel);
294                            importRatings(portletDataContext, stagedModel);
295    
296                            manifestSummary.incrementModelAdditionCount(
297                                    stagedModel.getStagedModelType());
298                    }
299                    catch (PortletDataException pde) {
300                            throw pde;
301                    }
302                    catch (Exception e) {
303                            throw new PortletDataException(e);
304                    }
305            }
306    
307            @Override
308            public void restoreStagedModel(
309                            PortletDataContext portletDataContext, T stagedModel)
310                    throws PortletDataException {
311    
312                    try {
313                            doRestoreStagedModel(portletDataContext, stagedModel);
314                    }
315                    catch (PortletDataException pde) {
316                            throw pde;
317                    }
318                    catch (Exception e) {
319                            throw new PortletDataException(e);
320                    }
321            }
322    
323            @Override
324            public boolean validateReference(
325                    PortletDataContext portletDataContext, Element referenceElement) {
326    
327                    validateMissingGroupReference(portletDataContext, referenceElement);
328    
329                    String uuid = referenceElement.attributeValue("uuid");
330    
331                    Map<Long, Long> groupIds =
332                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
333                                    Group.class);
334    
335                    long liveGroupId = GetterUtil.getLong(
336                            referenceElement.attributeValue("live-group-id"));
337    
338                    liveGroupId = MapUtil.getLong(groupIds, liveGroupId);
339    
340                    try {
341                            return validateMissingReference(uuid, liveGroupId);
342                    }
343                    catch (Exception e) {
344                            return false;
345                    }
346            }
347    
348            protected boolean countStagedModel(
349                    PortletDataContext portletDataContext, T stagedModel) {
350    
351                    return !portletDataContext.isStagedModelCounted(stagedModel);
352            }
353    
354            protected abstract void doExportStagedModel(
355                            PortletDataContext portletDataContext, T stagedModel)
356                    throws Exception;
357    
358            protected void doImportMissingReference(
359                            PortletDataContext portletDataContext, String uuid, long groupId,
360                            long classPK)
361                    throws Exception {
362    
363                    throw new UnsupportedOperationException();
364            }
365    
366            protected abstract void doImportStagedModel(
367                            PortletDataContext portletDataContext, T stagedModel)
368                    throws Exception;
369    
370            protected void doRestoreStagedModel(
371                            PortletDataContext portletDataContext, T stagedModel)
372                    throws Exception {
373    
374                    throw new UnsupportedOperationException();
375            }
376    
377            protected void exportAssetCategories(
378                            PortletDataContext portletDataContext, T stagedModel)
379                    throws PortletDataException {
380    
381                    List<AssetCategory> assetCategories =
382                            AssetCategoryLocalServiceUtil.getCategories(
383                                    ExportImportClassedModelUtil.getClassName(stagedModel),
384                                    ExportImportClassedModelUtil.getClassPK(stagedModel));
385    
386                    for (AssetCategory assetCategory : assetCategories) {
387                            StagedModelDataHandlerUtil.exportReferenceStagedModel(
388                                    portletDataContext, stagedModel, assetCategory,
389                                    PortletDataContext.REFERENCE_TYPE_WEAK);
390                    }
391            }
392    
393            protected void exportComments(
394                            PortletDataContext portletDataContext, T stagedModel)
395                    throws PortletDataException {
396    
397                    if (!MapUtil.getBoolean(
398                                    portletDataContext.getParameterMap(),
399                                    PortletDataHandlerKeys.PORTLET_DATA_ALL) &&
400                            !MapUtil.getBoolean(
401                                    portletDataContext.getParameterMap(),
402                                    PortletDataHandlerKeys.COMMENTS)) {
403    
404                            return;
405                    }
406    
407                    MBDiscussion mbDiscussion =
408                            MBDiscussionLocalServiceUtil.fetchDiscussion(
409                                    ExportImportClassedModelUtil.getClassName(stagedModel),
410                                    ExportImportClassedModelUtil.getClassPK(stagedModel));
411    
412                    if (mbDiscussion == null) {
413                            return;
414                    }
415    
416                    List<MBMessage> mbMessages =
417                            MBMessageLocalServiceUtil.getThreadMessages(
418                                    mbDiscussion.getThreadId(), WorkflowConstants.STATUS_APPROVED);
419    
420                    if (mbMessages.isEmpty()) {
421                            return;
422                    }
423    
424                    MBMessage firstMBMessage = mbMessages.get(0);
425    
426                    if ((mbMessages.size() == 1) && firstMBMessage.isRoot()) {
427                            return;
428                    }
429    
430                    for (MBMessage mbMessage : mbMessages) {
431                            StagedModelDataHandlerUtil.exportReferenceStagedModel(
432                                    portletDataContext, stagedModel, mbMessage,
433                                    PortletDataContext.REFERENCE_TYPE_WEAK);
434                    }
435            }
436    
437            protected void exportRatings(
438                            PortletDataContext portletDataContext, T stagedModel)
439                    throws PortletDataException {
440    
441                    if (!MapUtil.getBoolean(
442                                    portletDataContext.getParameterMap(),
443                                    PortletDataHandlerKeys.PORTLET_DATA_ALL) &&
444                            !MapUtil.getBoolean(
445                                    portletDataContext.getParameterMap(),
446                                    PortletDataHandlerKeys.RATINGS)) {
447    
448                            return;
449                    }
450    
451                    List<RatingsEntry> ratingsEntries =
452                            RatingsEntryLocalServiceUtil.getEntries(
453                                    ExportImportClassedModelUtil.getClassName(stagedModel),
454                                    ExportImportClassedModelUtil.getClassPK(stagedModel));
455    
456                    if (ratingsEntries.isEmpty()) {
457                            return;
458                    }
459    
460                    for (RatingsEntry ratingsEntry : ratingsEntries) {
461                            StagedModelDataHandlerUtil.exportReferenceStagedModel(
462                                    portletDataContext, stagedModel, ratingsEntry,
463                                    PortletDataContext.REFERENCE_TYPE_WEAK);
464                    }
465            }
466    
467            protected void importAssetCategories(
468                            PortletDataContext portletDataContext, T stagedModel)
469                    throws PortletDataException {
470    
471                    List<Element> referenceElements =
472                            portletDataContext.getReferenceElements(
473                                    stagedModel, AssetCategory.class);
474    
475                    List<Long> assetCategoryIds = new ArrayList<Long>(
476                            referenceElements.size());
477    
478                    for (Element referenceElement : referenceElements) {
479                            long classPK = GetterUtil.getLong(
480                                    referenceElement.attributeValue("class-pk"));
481    
482                            StagedModelDataHandlerUtil.importReferenceStagedModel(
483                                    portletDataContext, stagedModel, AssetCategory.class, classPK);
484    
485                            assetCategoryIds.add(classPK);
486                    }
487    
488                    Map<Long, Long> assetCategoryIdsMap =
489                            (Map<Long, Long>)portletDataContext.getNewPrimaryKeysMap(
490                                    AssetCategory.class);
491    
492                    long[] importedAssetCategoryIds = new long[assetCategoryIds.size()];
493    
494                    for (int i = 0; i < assetCategoryIds.size(); i++) {
495                            long categoryId = assetCategoryIds.get(i);
496    
497                            importedAssetCategoryIds[i] = MapUtil.getLong(
498                                    assetCategoryIdsMap, categoryId, categoryId);
499                    }
500    
501                    portletDataContext.addAssetCategories(
502                            ExportImportClassedModelUtil.getClassName(stagedModel),
503                            ExportImportClassedModelUtil.getClassPK(stagedModel),
504                            importedAssetCategoryIds);
505            }
506    
507            protected void importComments(
508                            PortletDataContext portletDataContext, T stagedModel)
509                    throws PortalException {
510    
511                    if (!MapUtil.getBoolean(
512                                    portletDataContext.getParameterMap(),
513                                    PortletDataHandlerKeys.PORTLET_DATA_ALL) &&
514                            !MapUtil.getBoolean(
515                                    portletDataContext.getParameterMap(),
516                                    PortletDataHandlerKeys.COMMENTS)) {
517    
518                            return;
519                    }
520    
521                    StagedModelDataHandlerUtil.importReferenceStagedModels(
522                            portletDataContext, stagedModel, MBMessage.class);
523            }
524    
525            protected void importMissingGroupReference(
526                            PortletDataContext portletDataContext, Element referenceElement)
527                    throws PortletDataException {
528    
529                    StagedModelDataHandler<StagedGroup> stagedModelDataHandler =
530                            (StagedModelDataHandler<StagedGroup>)
531                                    StagedModelDataHandlerRegistryUtil.getStagedModelDataHandler(
532                                            StagedGroup.class.getName());
533    
534                    stagedModelDataHandler.importMissingReference(
535                            portletDataContext, referenceElement);
536            }
537    
538            protected void importRatings(
539                            PortletDataContext portletDataContext, T stagedModel)
540                    throws PortalException {
541    
542                    if (!MapUtil.getBoolean(
543                                    portletDataContext.getParameterMap(),
544                                    PortletDataHandlerKeys.PORTLET_DATA_ALL) &&
545                            !MapUtil.getBoolean(
546                                    portletDataContext.getParameterMap(),
547                                    PortletDataHandlerKeys.RATINGS)) {
548    
549                            return;
550                    }
551    
552                    StagedModelDataHandlerUtil.importReferenceStagedModels(
553                            portletDataContext, stagedModel, RatingsEntry.class);
554            }
555    
556            protected void importReferenceStagedModels(
557                            PortletDataContext portletDataContext, T stagedModel)
558                    throws PortletDataException {
559    
560                    Element stagedModelElement =
561                            portletDataContext.getImportDataStagedModelElement(stagedModel);
562    
563                    Element referencesElement = stagedModelElement.element("references");
564    
565                    if (referencesElement == null) {
566                            return;
567                    }
568    
569                    List<Element> referenceElements = referencesElement.elements();
570    
571                    for (Element referenceElement : referenceElements) {
572                            String className = referenceElement.attributeValue("class-name");
573    
574                            if (className.equals(AssetCategory.class.getName()) ||
575                                    className.equals(RatingsEntry.class.getName()) ||
576                                    className.equals(MBMessage.class.getName())) {
577    
578                                    continue;
579                            }
580    
581                            long classPK = GetterUtil.getLong(
582                                    referenceElement.attributeValue("class-pk"));
583    
584                            StagedModelDataHandlerUtil.importReferenceStagedModel(
585                                    portletDataContext, stagedModel, className, classPK);
586                    }
587            }
588    
589            protected void validateExport(
590                            PortletDataContext portletDataContext, T stagedModel)
591                    throws PortletDataException {
592    
593                    if (stagedModel instanceof WorkflowedModel) {
594                            WorkflowedModel workflowedModel = (WorkflowedModel)stagedModel;
595    
596                            if (!ArrayUtil.contains(
597                                            getExportableStatuses(), workflowedModel.getStatus())) {
598    
599                                    PortletDataException pde = new PortletDataException(
600                                            PortletDataException.STATUS_UNAVAILABLE);
601    
602                                    pde.setStagedModel(stagedModel);
603    
604                                    throw pde;
605                            }
606                    }
607    
608                    if (stagedModel instanceof TrashedModel) {
609                            TrashedModel trashedModel = (TrashedModel)stagedModel;
610    
611                            if (trashedModel.isInTrash()) {
612                                    PortletDataException pde = new PortletDataException(
613                                            PortletDataException.STATUS_IN_TRASH);
614    
615                                    pde.setStagedModel(stagedModel);
616    
617                                    throw pde;
618                            }
619                    }
620            }
621    
622            protected boolean validateMissingGroupReference(
623                    PortletDataContext portletDataContext, Element referenceElement) {
624    
625                    StagedModelDataHandler<StagedGroup> stagedModelDataHandler =
626                            (StagedModelDataHandler<StagedGroup>)
627                                    StagedModelDataHandlerRegistryUtil.getStagedModelDataHandler(
628                                            StagedGroup.class.getName());
629    
630                    return stagedModelDataHandler.validateReference(
631                            portletDataContext, referenceElement);
632            }
633    
634            protected boolean validateMissingReference(String uuid, long groupId) {
635                    T existingStagedModel = fetchMissingReference(uuid, groupId);
636    
637                    if (existingStagedModel == null) {
638                            return false;
639                    }
640    
641                    return true;
642            }
643    
644            private static Log _log = LogFactoryUtil.getLog(
645                    BaseStagedModelDataHandler.class);
646    
647    }