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.exportimport.lar;
016    
017    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_LAYOUT_EXPORT_FAILED;
018    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_LAYOUT_EXPORT_STARTED;
019    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_LAYOUT_EXPORT_SUCCEEDED;
020    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_PORTLET_EXPORT_FAILED;
021    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_PORTLET_EXPORT_STARTED;
022    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.EVENT_PORTLET_EXPORT_SUCCEEDED;
023    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.PROCESS_FLAG_LAYOUT_EXPORT_IN_PROCESS;
024    import static com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleConstants.PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS;
025    
026    import com.liferay.portal.kernel.backgroundtask.BackgroundTaskThreadLocal;
027    import com.liferay.portal.kernel.exception.PortalException;
028    import com.liferay.portal.kernel.language.LanguageUtil;
029    import com.liferay.portal.kernel.log.Log;
030    import com.liferay.portal.kernel.log.LogFactoryUtil;
031    import com.liferay.portal.kernel.settings.PortletInstanceSettingsLocator;
032    import com.liferay.portal.kernel.settings.Settings;
033    import com.liferay.portal.kernel.settings.SettingsFactoryUtil;
034    import com.liferay.portal.kernel.util.ArrayUtil;
035    import com.liferay.portal.kernel.util.Constants;
036    import com.liferay.portal.kernel.util.DateRange;
037    import com.liferay.portal.kernel.util.FileUtil;
038    import com.liferay.portal.kernel.util.LocaleUtil;
039    import com.liferay.portal.kernel.util.MapUtil;
040    import com.liferay.portal.kernel.util.ReleaseInfo;
041    import com.liferay.portal.kernel.util.StringPool;
042    import com.liferay.portal.kernel.util.StringUtil;
043    import com.liferay.portal.kernel.util.Time;
044    import com.liferay.portal.kernel.util.Validator;
045    import com.liferay.portal.kernel.xml.Document;
046    import com.liferay.portal.kernel.xml.Element;
047    import com.liferay.portal.kernel.xml.SAXReaderUtil;
048    import com.liferay.portal.kernel.zip.ZipWriter;
049    import com.liferay.portal.model.Group;
050    import com.liferay.portal.model.Image;
051    import com.liferay.portal.model.Layout;
052    import com.liferay.portal.model.LayoutConstants;
053    import com.liferay.portal.model.LayoutPrototype;
054    import com.liferay.portal.model.LayoutRevision;
055    import com.liferay.portal.model.LayoutSet;
056    import com.liferay.portal.model.LayoutSetBranch;
057    import com.liferay.portal.model.LayoutSetPrototype;
058    import com.liferay.portal.model.LayoutStagingHandler;
059    import com.liferay.portal.model.LayoutTypePortlet;
060    import com.liferay.portal.model.Portlet;
061    import com.liferay.portal.model.impl.LayoutImpl;
062    import com.liferay.portal.service.GroupLocalServiceUtil;
063    import com.liferay.portal.service.ImageLocalServiceUtil;
064    import com.liferay.portal.service.LayoutLocalServiceUtil;
065    import com.liferay.portal.service.LayoutPrototypeLocalServiceUtil;
066    import com.liferay.portal.service.LayoutRevisionLocalServiceUtil;
067    import com.liferay.portal.service.LayoutSetBranchLocalServiceUtil;
068    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
069    import com.liferay.portal.service.LayoutSetPrototypeLocalServiceUtil;
070    import com.liferay.portal.service.PortletLocalServiceUtil;
071    import com.liferay.portal.service.ServiceContext;
072    import com.liferay.portal.service.ServiceContextThreadLocal;
073    import com.liferay.portal.service.UserLocalServiceUtil;
074    import com.liferay.portal.service.permission.PortletPermissionUtil;
075    import com.liferay.portlet.exportimport.lifecycle.ExportImportLifecycleManager;
076    import com.liferay.portlet.exportimport.staging.LayoutStagingUtil;
077    import com.liferay.portlet.exportimport.xstream.XStreamAliasRegistryUtil;
078    
079    import java.io.File;
080    
081    import java.util.Collections;
082    import java.util.Date;
083    import java.util.Iterator;
084    import java.util.LinkedHashMap;
085    import java.util.List;
086    import java.util.Map;
087    import java.util.concurrent.Callable;
088    
089    import org.apache.commons.lang.time.StopWatch;
090    
091    /**
092     * @author Brian Wing Shun Chan
093     * @author Joel Kozikowski
094     * @author Charles May
095     * @author Raymond Aug??
096     * @author Jorge Ferrer
097     * @author Bruno Farache
098     * @author Karthik Sudarshan
099     * @author Zsigmond Rab
100     * @author Douglas Wong
101     * @author Mate Thurzo
102     */
103    public class LayoutExporter {
104    
105            public static List<Portlet> getDataSiteLevelPortlets(long companyId)
106                    throws Exception {
107    
108                    return getDataSiteLevelPortlets(companyId, false);
109            }
110    
111            public static List<Portlet> getDataSiteLevelPortlets(
112                            long companyId, boolean excludeDataAlwaysStaged)
113                    throws Exception {
114    
115                    List<Portlet> portlets = PortletLocalServiceUtil.getPortlets(companyId);
116    
117                    Iterator<Portlet> itr = portlets.iterator();
118    
119                    while (itr.hasNext()) {
120                            Portlet portlet = itr.next();
121    
122                            if (!portlet.isActive()) {
123                                    itr.remove();
124    
125                                    continue;
126                            }
127    
128                            PortletDataHandler portletDataHandler =
129                                    portlet.getPortletDataHandlerInstance();
130    
131                            if ((portletDataHandler == null) ||
132                                    !portletDataHandler.isDataSiteLevel() ||
133                                    (excludeDataAlwaysStaged &&
134                                     portletDataHandler.isDataAlwaysStaged())) {
135    
136                                    itr.remove();
137                            }
138                    }
139    
140                    return portlets;
141            }
142    
143            public static LayoutExporter getInstance() {
144                    return _instance;
145            }
146    
147            /**
148             * @deprecated As of 7.0.0, with no direct replacement
149             */
150            @Deprecated
151            public static List<Portlet> getPortletDataHandlerPortlets(
152                            long groupId, List<Layout> layouts)
153                    throws Exception {
154    
155                    return Collections.emptyList();
156            }
157    
158            /**
159             * @deprecated As of 7.0.0, with no direct replacement
160             */
161            @Deprecated
162            public byte[] exportLayouts(
163                            long groupId, boolean privateLayout, long[] layoutIds,
164                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
165                    throws Exception {
166    
167                    File file = exportLayoutsAsFile(
168                            groupId, privateLayout, layoutIds, parameterMap, startDate,
169                            endDate);
170    
171                    try {
172                            return FileUtil.getBytes(file);
173                    }
174                    finally {
175                            file.delete();
176                    }
177            }
178    
179            public File exportLayoutsAsFile(
180                            long groupId, boolean privateLayout, long[] layoutIds,
181                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
182                    throws Exception {
183    
184                    PortletDataContext portletDataContext = null;
185    
186                    try {
187                            ExportImportThreadLocal.setLayoutExportInProcess(true);
188    
189                            portletDataContext = getPortletDataContext(
190                                    groupId, privateLayout, parameterMap, startDate, endDate);
191    
192                            ExportImportLifecycleManager.fireExportImportLifecycleEvent(
193                                    EVENT_LAYOUT_EXPORT_STARTED, getProcessFlag(),
194                                    PortletDataContextFactoryUtil.clonePortletDataContext(
195                                            portletDataContext));
196    
197                            File file = doExportLayoutsAsFile(portletDataContext, layoutIds);
198    
199                            ExportImportThreadLocal.setLayoutExportInProcess(false);
200    
201                            ExportImportLifecycleManager.fireExportImportLifecycleEvent(
202                                    EVENT_LAYOUT_EXPORT_SUCCEEDED, getProcessFlag(),
203                                    PortletDataContextFactoryUtil.clonePortletDataContext(
204                                            portletDataContext));
205    
206                            return file;
207                    }
208                    catch (Throwable t) {
209                            ExportImportThreadLocal.setLayoutExportInProcess(false);
210    
211                            ExportImportLifecycleManager.fireExportImportLifecycleEvent(
212                                    EVENT_LAYOUT_EXPORT_FAILED, getProcessFlag(),
213                                    PortletDataContextFactoryUtil.clonePortletDataContext(
214                                            portletDataContext),
215                                    t);
216    
217                            throw t;
218                    }
219            }
220    
221            protected File doExportLayoutsAsFile(
222                            PortletDataContext portletDataContext, long[] layoutIds)
223                    throws Exception {
224    
225                    Map<String, String[]> parameterMap =
226                            portletDataContext.getParameterMap();
227    
228                    boolean exportIgnoreLastPublishDate = MapUtil.getBoolean(
229                            parameterMap, PortletDataHandlerKeys.IGNORE_LAST_PUBLISH_DATE);
230                    boolean exportPermissions = MapUtil.getBoolean(
231                            parameterMap, PortletDataHandlerKeys.PERMISSIONS);
232                    boolean exportLogo = MapUtil.getBoolean(
233                            parameterMap, PortletDataHandlerKeys.LOGO);
234                    boolean exportLayoutSetSettings = MapUtil.getBoolean(
235                            parameterMap, PortletDataHandlerKeys.LAYOUT_SET_SETTINGS);
236    
237                    if (_log.isDebugEnabled()) {
238                            _log.debug("Export permissions " + exportPermissions);
239                    }
240    
241                    LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
242                            portletDataContext.getGroupId(),
243                            portletDataContext.isPrivateLayout());
244    
245                    long companyId = layoutSet.getCompanyId();
246                    long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
247    
248                    ServiceContext serviceContext =
249                            ServiceContextThreadLocal.popServiceContext();
250    
251                    if (serviceContext == null) {
252                            serviceContext = new ServiceContext();
253                    }
254    
255                    serviceContext.setCompanyId(companyId);
256                    serviceContext.setSignedIn(false);
257                    serviceContext.setUserId(defaultUserId);
258    
259                    serviceContext.setAttribute("exporting", Boolean.TRUE);
260    
261                    long layoutSetBranchId = MapUtil.getLong(
262                            parameterMap, "layoutSetBranchId");
263    
264                    serviceContext.setAttribute("layoutSetBranchId", layoutSetBranchId);
265    
266                    ServiceContextThreadLocal.pushServiceContext(serviceContext);
267    
268                    if (exportIgnoreLastPublishDate) {
269                            portletDataContext.setEndDate(null);
270                            portletDataContext.setStartDate(null);
271                    }
272    
273                    StopWatch stopWatch = new StopWatch();
274    
275                    stopWatch.start();
276    
277                    Document document = SAXReaderUtil.createDocument();
278    
279                    Element rootElement = document.addElement("root");
280    
281                    portletDataContext.setExportDataRootElement(rootElement);
282    
283                    Element headerElement = rootElement.addElement("header");
284    
285                    headerElement.addAttribute(
286                            "available-locales",
287                            StringUtil.merge(
288                                    LanguageUtil.getAvailableLocales(
289                                            portletDataContext.getScopeGroupId())));
290                    headerElement.addAttribute(
291                            "build-number", String.valueOf(ReleaseInfo.getBuildNumber()));
292                    headerElement.addAttribute("export-date", Time.getRFC822());
293    
294                    if (portletDataContext.hasDateRange()) {
295                            headerElement.addAttribute(
296                                    "start-date",
297                                    String.valueOf(portletDataContext.getStartDate()));
298                            headerElement.addAttribute(
299                                    "end-date", String.valueOf(portletDataContext.getEndDate()));
300                    }
301    
302                    headerElement.addAttribute(
303                            "company-id", String.valueOf(portletDataContext.getCompanyId()));
304                    headerElement.addAttribute(
305                            "company-group-id",
306                            String.valueOf(portletDataContext.getCompanyGroupId()));
307                    headerElement.addAttribute(
308                            "group-id", String.valueOf(portletDataContext.getGroupId()));
309                    headerElement.addAttribute(
310                            "user-personal-site-group-id",
311                            String.valueOf(portletDataContext.getUserPersonalSiteGroupId()));
312                    headerElement.addAttribute(
313                            "private-layout",
314                            String.valueOf(portletDataContext.isPrivateLayout()));
315    
316                    Group group = layoutSet.getGroup();
317    
318                    String type = "layout-set";
319    
320                    if (group.isLayoutPrototype()) {
321                            type = "layout-prototype";
322    
323                            LayoutPrototype layoutPrototype =
324                                    LayoutPrototypeLocalServiceUtil.getLayoutPrototype(
325                                            group.getClassPK());
326    
327                            headerElement.addAttribute("type-uuid", layoutPrototype.getUuid());
328                    }
329                    else if (group.isLayoutSetPrototype()) {
330                            type ="layout-set-prototype";
331    
332                            LayoutSetPrototype layoutSetPrototype =
333                                    LayoutSetPrototypeLocalServiceUtil.getLayoutSetPrototype(
334                                            group.getClassPK());
335    
336                            headerElement.addAttribute(
337                                    "type-uuid", layoutSetPrototype.getUuid());
338                    }
339    
340                    headerElement.addAttribute("type", type);
341    
342                    LayoutSetBranch layoutSetBranch =
343                            LayoutSetBranchLocalServiceUtil.fetchLayoutSetBranch(
344                                    layoutSetBranchId);
345    
346                    if (exportLogo) {
347                            Image image = null;
348    
349                            if (layoutSetBranch != null) {
350                                    image = ImageLocalServiceUtil.getImage(
351                                            layoutSetBranch.getLogoId());
352                            }
353                            else {
354                                    image = ImageLocalServiceUtil.getImage(layoutSet.getLogoId());
355                            }
356    
357                            if ((image != null) && (image.getTextObj() != null)) {
358                                    String logoPath = ExportImportPathUtil.getRootPath(
359                                            portletDataContext);
360    
361                                    logoPath += "/logo";
362    
363                                    headerElement.addAttribute("logo-path", logoPath);
364    
365                                    portletDataContext.addZipEntry(logoPath, image.getTextObj());
366                            }
367                    }
368    
369                    Element missingReferencesElement = rootElement.addElement(
370                            "missing-references");
371    
372                    portletDataContext.setMissingReferencesElement(
373                            missingReferencesElement);
374    
375                    if (layoutSetBranch != null) {
376                            _themeExporter.exportTheme(portletDataContext, layoutSetBranch);
377                    }
378                    else {
379                            _themeExporter.exportTheme(portletDataContext, layoutSet);
380                    }
381    
382                    if (exportLayoutSetSettings) {
383                            Element settingsElement = headerElement.addElement("settings");
384    
385                            if (layoutSetBranch != null) {
386                                    settingsElement.addCDATA(layoutSetBranch.getSettings());
387                            }
388                            else {
389                                    settingsElement.addCDATA(layoutSet.getSettings());
390                            }
391                    }
392    
393                    Map<String, Object[]> portletIds = new LinkedHashMap<>();
394    
395                    List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(
396                            portletDataContext.getGroupId(),
397                            portletDataContext.isPrivateLayout());
398    
399                    if (group.isStagingGroup()) {
400                            group = group.getLiveGroup();
401                    }
402    
403                    // Collect data portlets
404    
405                    for (Portlet portlet : getDataSiteLevelPortlets(companyId)) {
406                            String portletId = portlet.getRootPortletId();
407    
408                            if (ExportImportThreadLocal.isStagingInProcess() &&
409                                    !group.isStagedPortlet(portletId)) {
410    
411                                    continue;
412                            }
413    
414                            // Calculate the amount of exported data
415    
416                            if (BackgroundTaskThreadLocal.hasBackgroundTask()) {
417                                    PortletDataHandler portletDataHandler =
418                                            portlet.getPortletDataHandlerInstance();
419    
420                                    portletDataHandler.prepareManifestSummary(portletDataContext);
421                            }
422    
423                            // Add portlet ID to exportable portlets list
424    
425                            portletIds.put(
426                                    PortletPermissionUtil.getPrimaryKey(0, portletId),
427                                    new Object[] {
428                                            portletId, LayoutConstants.DEFAULT_PLID,
429                                            portletDataContext.getGroupId(), StringPool.BLANK,
430                                            StringPool.BLANK
431                                    });
432    
433                            if (!portlet.isScopeable()) {
434                                    continue;
435                            }
436    
437                            // Scoped data
438    
439                            for (Layout layout : layouts) {
440                                    if (!ArrayUtil.contains(layoutIds, layout.getLayoutId()) ||
441                                            !layout.isTypePortlet() || !layout.hasScopeGroup()) {
442    
443                                            continue;
444                                    }
445    
446                                    Group scopeGroup = layout.getScopeGroup();
447    
448                                    portletIds.put(
449                                            PortletPermissionUtil.getPrimaryKey(
450                                                    layout.getPlid(), portlet.getPortletId()),
451                                            new Object[] {
452                                                    portlet.getPortletId(), layout.getPlid(),
453                                                    scopeGroup.getGroupId(), StringPool.BLANK,
454                                                    layout.getUuid()
455                                            });
456                            }
457                    }
458    
459                    portletDataContext.addDeletionSystemEventStagedModelTypes(
460                            new StagedModelType(Layout.class));
461    
462                    Element layoutsElement = portletDataContext.getExportDataGroupElement(
463                            Layout.class);
464    
465                    String layoutSetPrototypeUuid = layoutSet.getLayoutSetPrototypeUuid();
466    
467                    if (Validator.isNotNull(layoutSetPrototypeUuid)) {
468                            LayoutSetPrototype layoutSetPrototype =
469                                    LayoutSetPrototypeLocalServiceUtil.
470                                            getLayoutSetPrototypeByUuidAndCompanyId(
471                                                    layoutSetPrototypeUuid, companyId);
472    
473                            layoutsElement.addAttribute(
474                                    "layout-set-prototype-uuid", layoutSetPrototypeUuid);
475    
476                            layoutsElement.addAttribute(
477                                    "layout-set-prototype-name",
478                                    layoutSetPrototype.getName(LocaleUtil.getDefault()));
479                    }
480    
481                    for (Layout layout : layouts) {
482                            exportLayout(portletDataContext, layoutIds, portletIds, layout);
483                    }
484    
485                    if (BackgroundTaskThreadLocal.hasBackgroundTask()) {
486                            ManifestSummary manifestSummary =
487                                    portletDataContext.getManifestSummary();
488    
489                            PortletDataHandlerStatusMessageSenderUtil.sendStatusMessage(
490                                    "layout", ArrayUtil.toStringArray(portletIds.keySet()),
491                                    manifestSummary);
492    
493                            manifestSummary.resetCounters();
494                    }
495    
496                    Element portletsElement = rootElement.addElement("portlets");
497    
498                    Element servicesElement = rootElement.addElement("services");
499    
500                    long previousScopeGroupId = portletDataContext.getScopeGroupId();
501    
502                    for (Map.Entry<String, Object[]> portletIdsEntry :
503                                    portletIds.entrySet()) {
504    
505                            Object[] portletObjects = portletIdsEntry.getValue();
506    
507                            String portletId = null;
508                            long plid = LayoutConstants.DEFAULT_PLID;
509                            long scopeGroupId = 0;
510                            String scopeType = StringPool.BLANK;
511                            String scopeLayoutUuid = null;
512    
513                            if (portletObjects.length == 4) {
514                                    portletId = (String)portletIdsEntry.getValue()[0];
515                                    plid = (Long)portletIdsEntry.getValue()[1];
516                                    scopeGroupId = (Long)portletIdsEntry.getValue()[2];
517                                    scopeLayoutUuid = (String)portletIdsEntry.getValue()[3];
518                            }
519                            else {
520                                    portletId = (String)portletIdsEntry.getValue()[0];
521                                    plid = (Long)portletIdsEntry.getValue()[1];
522                                    scopeGroupId = (Long)portletIdsEntry.getValue()[2];
523                                    scopeType = (String)portletIdsEntry.getValue()[3];
524                                    scopeLayoutUuid = (String)portletIdsEntry.getValue()[4];
525                            }
526    
527                            Layout layout = LayoutLocalServiceUtil.fetchLayout(plid);
528    
529                            if (layout == null) {
530                                    layout = new LayoutImpl();
531    
532                                    layout.setCompanyId(companyId);
533                                    layout.setGroupId(portletDataContext.getGroupId());
534                            }
535    
536                            portletDataContext.setPlid(plid);
537                            portletDataContext.setOldPlid(plid);
538                            portletDataContext.setPortletId(portletId);
539                            portletDataContext.setScopeGroupId(scopeGroupId);
540                            portletDataContext.setScopeType(scopeType);
541                            portletDataContext.setScopeLayoutUuid(scopeLayoutUuid);
542    
543                            Map<String, Boolean> exportPortletControlsMap =
544                                    ExportImportHelperUtil.getExportPortletControlsMap(
545                                            companyId, portletId, parameterMap, type);
546    
547                            try {
548                                    ExportImportLifecycleManager.fireExportImportLifecycleEvent(
549                                            EVENT_PORTLET_EXPORT_STARTED, getProcessFlag(),
550                                            PortletDataContextFactoryUtil.clonePortletDataContext(
551                                                    portletDataContext));
552    
553                                    _portletExporter.exportPortlet(
554                                            portletDataContext, layout, portletsElement,
555                                            exportPermissions,
556                                            exportPortletControlsMap.get(
557                                                    PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS),
558                                            exportPortletControlsMap.get(
559                                                    PortletDataHandlerKeys.PORTLET_DATA),
560                                            exportPortletControlsMap.get(
561                                                    PortletDataHandlerKeys.PORTLET_SETUP),
562                                            exportPortletControlsMap.get(
563                                                    PortletDataHandlerKeys.PORTLET_USER_PREFERENCES));
564                                    _portletExporter.exportService(
565                                            portletDataContext, servicesElement,
566                                            exportPortletControlsMap.get(
567                                                    PortletDataHandlerKeys.PORTLET_SETUP));
568    
569                                    ExportImportLifecycleManager.fireExportImportLifecycleEvent(
570                                            EVENT_PORTLET_EXPORT_SUCCEEDED, getProcessFlag(),
571                                            PortletDataContextFactoryUtil.clonePortletDataContext(
572                                                    portletDataContext));
573                            }
574                            catch (Throwable t) {
575                                    ExportImportLifecycleManager.fireExportImportLifecycleEvent(
576                                            EVENT_PORTLET_EXPORT_FAILED, getProcessFlag(),
577                                            PortletDataContextFactoryUtil.clonePortletDataContext(
578                                                    portletDataContext),
579                                            t);
580    
581                                    throw t;
582                            }
583                    }
584    
585                    portletDataContext.setScopeGroupId(previousScopeGroupId);
586    
587                    _portletExporter.exportAssetLinks(portletDataContext);
588                    _portletExporter.exportExpandoTables(portletDataContext);
589                    _portletExporter.exportLocks(portletDataContext);
590    
591                    _deletionSystemEventExporter.exportDeletionSystemEvents(
592                            portletDataContext);
593    
594                    if (exportPermissions) {
595                            _permissionExporter.exportPortletDataPermissions(
596                                    portletDataContext);
597                    }
598    
599                    ExportImportHelperUtil.writeManifestSummary(
600                            document, portletDataContext.getManifestSummary());
601    
602                    if (_log.isInfoEnabled()) {
603                            _log.info("Exporting layouts takes " + stopWatch.getTime() + " ms");
604                    }
605    
606                    boolean updateLastPublishDate = MapUtil.getBoolean(
607                            portletDataContext.getParameterMap(),
608                            PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE);
609    
610                    if (ExportImportThreadLocal.isStagingInProcess() &&
611                            updateLastPublishDate) {
612    
613                            ExportImportProcessCallbackRegistryUtil.registerCallback(
614                                    new UpdateLayoutSetLastPublishDateCallable(
615                                            portletDataContext.getDateRange(),
616                                            portletDataContext.getGroupId(),
617                                            portletDataContext.isPrivateLayout()));
618                    }
619    
620                    portletDataContext.addZipEntry(
621                            "/manifest.xml", document.formattedString());
622    
623                    ZipWriter zipWriter = portletDataContext.getZipWriter();
624    
625                    return zipWriter.getFile();
626            }
627    
628            protected void exportLayout(
629                            PortletDataContext portletDataContext, long[] layoutIds,
630                            Map<String, Object[]> portletIds, Layout layout)
631                    throws Exception {
632    
633                    if (!ArrayUtil.contains(layoutIds, layout.getLayoutId())) {
634                            Element layoutElement = portletDataContext.getExportDataElement(
635                                    layout);
636    
637                            layoutElement.addAttribute(Constants.ACTION, Constants.SKIP);
638    
639                            return;
640                    }
641    
642                    boolean exportLAR = MapUtil.getBoolean(
643                            portletDataContext.getParameterMap(), "exportLAR");
644    
645                    if (!exportLAR && LayoutStagingUtil.isBranchingLayout(layout)) {
646                            long layoutSetBranchId = MapUtil.getLong(
647                                    portletDataContext.getParameterMap(), "layoutSetBranchId");
648    
649                            if (layoutSetBranchId <= 0) {
650                                    return;
651                            }
652    
653                            LayoutRevision layoutRevision =
654                                    LayoutRevisionLocalServiceUtil.fetchLayoutRevision(
655                                            layoutSetBranchId, true, layout.getPlid());
656    
657                            if (layoutRevision == null) {
658                                    return;
659                            }
660    
661                            LayoutStagingHandler layoutStagingHandler =
662                                    LayoutStagingUtil.getLayoutStagingHandler(layout);
663    
664                            layoutStagingHandler.setLayoutRevision(layoutRevision);
665                    }
666    
667                    StagedModelDataHandlerUtil.exportStagedModel(
668                            portletDataContext, layout);
669    
670                    if (!layout.isSupportsEmbeddedPortlets()) {
671    
672                            // Only portlet type layouts support page scoping
673    
674                            return;
675                    }
676    
677                    LayoutTypePortlet layoutTypePortlet =
678                            (LayoutTypePortlet)layout.getLayoutType();
679    
680                    // The getAllPortlets method returns all effective nonsystem portlets
681                    // for any layout type, including embedded portlets, or in the case of
682                    // panel type layout, selected portlets
683    
684                    for (Portlet portlet : layoutTypePortlet.getAllPortlets(false)) {
685                            String portletId = portlet.getPortletId();
686    
687                            Settings portletInstanceSettings = SettingsFactoryUtil.getSettings(
688                                    new PortletInstanceSettingsLocator(layout, portletId));
689    
690                            String scopeType = portletInstanceSettings.getValue(
691                                    "lfrScopeType", null);
692                            String scopeLayoutUuid = portletInstanceSettings.getValue(
693                                    "lfrScopeLayoutUuid", null);
694    
695                            long scopeGroupId = portletDataContext.getScopeGroupId();
696    
697                            if (Validator.isNotNull(scopeType)) {
698                                    Group scopeGroup = null;
699    
700                                    if (scopeType.equals("company")) {
701                                            scopeGroup = GroupLocalServiceUtil.getCompanyGroup(
702                                                    layout.getCompanyId());
703                                    }
704                                    else if (scopeType.equals("layout")) {
705                                            Layout scopeLayout = null;
706    
707                                            scopeLayout =
708                                                    LayoutLocalServiceUtil.fetchLayoutByUuidAndGroupId(
709                                                            scopeLayoutUuid, portletDataContext.getGroupId(),
710                                                            portletDataContext.isPrivateLayout());
711    
712                                            if (scopeLayout == null) {
713                                                    continue;
714                                            }
715    
716                                            scopeGroup = scopeLayout.getScopeGroup();
717                                    }
718                                    else {
719                                            throw new IllegalArgumentException(
720                                                    "Scope type " + scopeType + " is invalid");
721                                    }
722    
723                                    if (scopeGroup != null) {
724                                            scopeGroupId = scopeGroup.getGroupId();
725                                    }
726                            }
727    
728                            String key = PortletPermissionUtil.getPrimaryKey(
729                                    layout.getPlid(), portletId);
730    
731                            portletIds.put(
732                                    key,
733                                    new Object[] {
734                                            portletId, layout.getPlid(), scopeGroupId, scopeType,
735                                            scopeLayoutUuid
736                                    }
737                            );
738                    }
739            }
740    
741            protected PortletDataContext getPortletDataContext(
742                            long groupId, boolean privateLayout,
743                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
744                    throws PortalException {
745    
746                    Group group = GroupLocalServiceUtil.getGroup(groupId);
747                    ZipWriter zipWriter = ExportImportHelperUtil.getLayoutSetZipWriter(
748                            groupId);
749    
750                    PortletDataContext portletDataContext =
751                            PortletDataContextFactoryUtil.createExportPortletDataContext(
752                                    group.getCompanyId(), groupId, parameterMap, startDate, endDate,
753                                    zipWriter);
754    
755                    portletDataContext.setPrivateLayout(privateLayout);
756    
757                    return portletDataContext;
758            }
759    
760            protected int getProcessFlag() {
761                    if (ExportImportThreadLocal.isLayoutStagingInProcess()) {
762                            return PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS;
763                    }
764    
765                    return PROCESS_FLAG_LAYOUT_EXPORT_IN_PROCESS;
766            }
767    
768            private LayoutExporter() {
769                    XStreamAliasRegistryUtil.register(LayoutImpl.class, "Layout");
770            }
771    
772            private static final Log _log = LogFactoryUtil.getLog(LayoutExporter.class);
773    
774            private static final LayoutExporter _instance = new LayoutExporter();
775    
776            private final DeletionSystemEventExporter _deletionSystemEventExporter =
777                    DeletionSystemEventExporter.getInstance();
778            private final PermissionExporter _permissionExporter =
779                    PermissionExporter.getInstance();
780            private final PortletExporter _portletExporter =
781                    PortletExporter.getInstance();
782            private final ThemeExporter _themeExporter = ThemeExporter.getInstance();
783    
784            private class UpdateLayoutSetLastPublishDateCallable
785                    implements Callable<Void> {
786    
787                    public UpdateLayoutSetLastPublishDateCallable(
788                            DateRange dateRange, long groupId, boolean privateLayout) {
789    
790                            _dateRange = dateRange;
791                            _groupId = groupId;
792                            _privateLayout = privateLayout;
793                    }
794    
795                    @Override
796                    public Void call() throws PortalException {
797                            Group group = GroupLocalServiceUtil.getGroup(_groupId);
798    
799                            Date endDate = null;
800    
801                            if (_dateRange != null) {
802                                    endDate = _dateRange.getEndDate();
803                            }
804    
805                            if (group.hasStagingGroup()) {
806                                    Group stagingGroup = group.getStagingGroup();
807    
808                                    ExportImportDateUtil.updateLastPublishDate(
809                                            stagingGroup.getGroupId(), _privateLayout, _dateRange,
810                                            endDate);
811                            }
812                            else {
813                                    ExportImportDateUtil.updateLastPublishDate(
814                                            _groupId, _privateLayout, _dateRange, endDate);
815                            }
816    
817                            return null;
818                    }
819    
820                    private final DateRange _dateRange;
821                    private final long _groupId;
822                    private final boolean _privateLayout;
823    
824            }
825    
826    }