com.liferay.portal.lar.LayoutExporter.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.portal.lar.LayoutExporter.java

Source

/**
 * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */

package com.liferay.portal.lar;

import com.liferay.portal.NoSuchLayoutException;
import com.liferay.portal.kernel.lar.ImportExportThreadLocal;
import com.liferay.portal.kernel.lar.PortletDataContext;
import com.liferay.portal.kernel.lar.PortletDataHandler;
import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.servlet.ServletContextPool;
import com.liferay.portal.kernel.staging.LayoutStagingUtil;
import com.liferay.portal.kernel.util.CharPool;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.MapUtil;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.ReleaseInfo;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Time;
import com.liferay.portal.kernel.util.UnicodeProperties;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portal.kernel.xml.Document;
import com.liferay.portal.kernel.xml.Element;
import com.liferay.portal.kernel.xml.SAXReaderUtil;
import com.liferay.portal.kernel.zip.ZipWriter;
import com.liferay.portal.kernel.zip.ZipWriterFactoryUtil;
import com.liferay.portal.model.Group;
import com.liferay.portal.model.Image;
import com.liferay.portal.model.Layout;
import com.liferay.portal.model.LayoutConstants;
import com.liferay.portal.model.LayoutRevision;
import com.liferay.portal.model.LayoutSet;
import com.liferay.portal.model.LayoutSetPrototype;
import com.liferay.portal.model.LayoutStagingHandler;
import com.liferay.portal.model.LayoutTypePortlet;
import com.liferay.portal.model.Portlet;
import com.liferay.portal.model.PortletConstants;
import com.liferay.portal.model.Theme;
import com.liferay.portal.service.GroupLocalServiceUtil;
import com.liferay.portal.service.ImageLocalServiceUtil;
import com.liferay.portal.service.LayoutLocalServiceUtil;
import com.liferay.portal.service.LayoutSetLocalServiceUtil;
import com.liferay.portal.service.LayoutSetPrototypeLocalServiceUtil;
import com.liferay.portal.service.PortletLocalServiceUtil;
import com.liferay.portal.service.ServiceContext;
import com.liferay.portal.service.ServiceContextThreadLocal;
import com.liferay.portal.service.UserLocalServiceUtil;
import com.liferay.portal.service.permission.PortletPermissionUtil;
import com.liferay.portal.service.persistence.LayoutRevisionUtil;
import com.liferay.portal.theme.ThemeLoader;
import com.liferay.portal.theme.ThemeLoaderFactory;
import com.liferay.portal.util.PortletKeys;
import com.liferay.portal.util.PropsValues;
import com.liferay.portlet.PortletPreferencesFactoryUtil;
import com.liferay.portlet.asset.model.AssetCategory;
import com.liferay.portlet.asset.model.AssetVocabulary;
import com.liferay.portlet.asset.service.AssetVocabularyLocalServiceUtil;
import com.liferay.portlet.asset.service.persistence.AssetCategoryUtil;
import com.liferay.portlet.journal.NoSuchArticleException;
import com.liferay.portlet.journal.lar.JournalPortletDataHandlerImpl;
import com.liferay.portlet.journal.model.JournalArticle;
import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
import com.liferay.portlet.sites.util.SitesUtil;
import com.liferay.util.ContentUtil;

import de.schlichtherle.io.FileInputStream;

import java.io.File;

import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;

import org.apache.commons.lang.time.StopWatch;

/**
 * @author Brian Wing Shun Chan
 * @author Joel Kozikowski
 * @author Charles May
 * @author Raymond Aug
 * @author Jorge Ferrer
 * @author Bruno Farache
 * @author Karthik Sudarshan
 * @author Zsigmond Rab
 * @author Douglas Wong
 */
public class LayoutExporter {

    public static final String SAME_GROUP_FRIENDLY_URL = "/[$SAME_GROUP_FRIENDLY_URL$]";

    public static List<Portlet> getAlwaysExportablePortlets(long companyId) throws Exception {

        List<Portlet> portlets = PortletLocalServiceUtil.getPortlets(companyId);

        Iterator<Portlet> itr = portlets.iterator();

        while (itr.hasNext()) {
            Portlet portlet = itr.next();

            if (!portlet.isActive()) {
                itr.remove();

                continue;
            }

            PortletDataHandler portletDataHandler = portlet.getPortletDataHandlerInstance();

            if ((portletDataHandler == null) || (!portletDataHandler.isAlwaysExportable())) {

                itr.remove();
            }
        }

        return portlets;
    }

    public static void updateLastPublishDate(LayoutSet layoutSet, long lastPublishDate) throws Exception {

        UnicodeProperties settingsProperties = layoutSet.getSettingsProperties();

        if (lastPublishDate <= 0) {
            settingsProperties.remove("last-publish-date");
        } else {
            settingsProperties.setProperty("last-publish-date", String.valueOf(lastPublishDate));
        }

        LayoutSetLocalServiceUtil.updateSettings(layoutSet.getGroupId(), layoutSet.isPrivateLayout(),
                settingsProperties.toString());
    }

    public byte[] exportLayouts(long groupId, boolean privateLayout, long[] layoutIds,
            Map<String, String[]> parameterMap, Date startDate, Date endDate) throws Exception {

        File file = exportLayoutsAsFile(groupId, privateLayout, layoutIds, parameterMap, startDate, endDate);

        try {
            return FileUtil.getBytes(file);
        } finally {
            file.delete();
        }
    }

    public File exportLayoutsAsFile(long groupId, boolean privateLayout, long[] layoutIds,
            Map<String, String[]> parameterMap, Date startDate, Date endDate) throws Exception {

        try {
            ImportExportThreadLocal.setLayoutExportInProcess(true);

            return doExportLayoutsAsFile(groupId, privateLayout, layoutIds, parameterMap, startDate, endDate);
        } finally {
            ImportExportThreadLocal.setLayoutExportInProcess(false);
        }
    }

    protected File doExportLayoutsAsFile(long groupId, boolean privateLayout, long[] layoutIds,
            Map<String, String[]> parameterMap, Date startDate, Date endDate) throws Exception {

        boolean exportCategories = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.CATEGORIES);
        boolean exportIgnoreLastPublishDate = MapUtil.getBoolean(parameterMap,
                PortletDataHandlerKeys.IGNORE_LAST_PUBLISH_DATE);
        boolean exportPermissions = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.PERMISSIONS);
        boolean exportUserPermissions = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.USER_PERMISSIONS);
        boolean exportPortletArchivedSetups = MapUtil.getBoolean(parameterMap,
                PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
        boolean exportPortletUserPreferences = MapUtil.getBoolean(parameterMap,
                PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
        boolean exportTheme = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.THEME);
        boolean exportThemeSettings = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.THEME_REFERENCE);
        boolean publishToRemote = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.PUBLISH_TO_REMOTE);
        boolean updateLastPublishDate = MapUtil.getBoolean(parameterMap,
                PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE);

        if (_log.isDebugEnabled()) {
            _log.debug("Export categories " + exportCategories);
            _log.debug("Export permissions " + exportPermissions);
            _log.debug("Export user permissions " + exportUserPermissions);
            _log.debug("Export portlet archived setups " + exportPortletArchivedSetups);
            _log.debug("Export portlet user preferences " + exportPortletUserPreferences);
            _log.debug("Export theme " + exportTheme);
        }

        LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(groupId, privateLayout);

        long companyId = layoutSet.getCompanyId();
        long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);

        ServiceContext serviceContext = ServiceContextThreadLocal.getServiceContext();

        if (serviceContext == null) {
            serviceContext = new ServiceContext();

            serviceContext.setCompanyId(companyId);
            serviceContext.setSignedIn(false);
            serviceContext.setUserId(defaultUserId);

            ServiceContextThreadLocal.pushServiceContext(serviceContext);
        }

        serviceContext.setAttribute("exporting", Boolean.TRUE);

        long layoutSetBranchId = MapUtil.getLong(parameterMap, "layoutSetBranchId");

        serviceContext.setAttribute("layoutSetBranchId", layoutSetBranchId);

        long lastPublishDate = System.currentTimeMillis();

        if (endDate != null) {
            lastPublishDate = endDate.getTime();
        }

        if (exportIgnoreLastPublishDate) {
            endDate = null;
            startDate = null;
        }

        StopWatch stopWatch = null;

        if (_log.isInfoEnabled()) {
            stopWatch = new StopWatch();

            stopWatch.start();
        }

        LayoutCache layoutCache = new LayoutCache();

        ZipWriter zipWriter = ZipWriterFactoryUtil.getZipWriter();

        PortletDataContext portletDataContext = new PortletDataContextImpl(companyId, groupId, parameterMap,
                new HashSet<String>(), startDate, endDate, zipWriter);

        portletDataContext.setPortetDataContextListener(new PortletDataContextListenerImpl(portletDataContext));

        Document document = SAXReaderUtil.createDocument();

        Element rootElement = document.addElement("root");

        Element headerElement = rootElement.addElement("header");

        headerElement.addAttribute("build-number", String.valueOf(ReleaseInfo.getBuildNumber()));
        headerElement.addAttribute("export-date", Time.getRFC822());

        if (portletDataContext.hasDateRange()) {
            headerElement.addAttribute("start-date", String.valueOf(portletDataContext.getStartDate()));
            headerElement.addAttribute("end-date", String.valueOf(portletDataContext.getEndDate()));
        }

        headerElement.addAttribute("type", "layout-set");
        headerElement.addAttribute("group-id", String.valueOf(groupId));
        headerElement.addAttribute("private-layout", String.valueOf(privateLayout));

        Group group = layoutSet.getGroup();

        if (group.isLayoutSetPrototype()) {
            LayoutSetPrototype layoutSetPrototype = LayoutSetPrototypeLocalServiceUtil
                    .getLayoutSetPrototype(group.getClassPK());

            String layoutSetPrototypeUuid = layoutSetPrototype.getUuid();

            headerElement.addAttribute("layout-set-prototype-uuid", layoutSetPrototypeUuid);

            if (publishToRemote) {
                String path = getLayoutSetPrototype(portletDataContext, layoutSetPrototypeUuid);

                File layoutSetPrototypeFile = SitesUtil.exportLayoutSetPrototype(layoutSetPrototype,
                        serviceContext);

                try {
                    portletDataContext.addZipEntry(path.concat(".lar"),
                            new FileInputStream(layoutSetPrototypeFile));
                    portletDataContext.addZipEntry(path.concat(".xml"), layoutSetPrototype);
                } finally {
                    layoutSetPrototypeFile.delete();
                }
            }
        }

        if (exportTheme || exportThemeSettings) {
            headerElement.addAttribute("theme-id", layoutSet.getThemeId());
            headerElement.addAttribute("color-scheme-id", layoutSet.getColorSchemeId());
        }

        Element cssElement = headerElement.addElement("css");

        cssElement.addCDATA(layoutSet.getCss());

        Portlet layoutConfigurationPortlet = PortletLocalServiceUtil
                .getPortletById(portletDataContext.getCompanyId(), PortletKeys.LAYOUT_CONFIGURATION);

        Map<String, Object[]> portletIds = new LinkedHashMap<String, Object[]>();

        List<Layout> layouts = null;

        if ((layoutIds == null) || (layoutIds.length == 0)) {
            layouts = LayoutLocalServiceUtil.getLayouts(groupId, privateLayout);
        } else {
            layouts = LayoutLocalServiceUtil.getLayouts(groupId, privateLayout, layoutIds);
        }

        List<Portlet> portlets = getAlwaysExportablePortlets(companyId);

        if (!layouts.isEmpty()) {
            Layout firstLayout = layouts.get(0);

            if (group.isStagingGroup()) {
                group = group.getLiveGroup();
            }

            for (Portlet portlet : portlets) {
                String portletId = portlet.getRootPortletId();

                if (!group.isStagedPortlet(portletId)) {
                    continue;
                }

                String key = PortletPermissionUtil.getPrimaryKey(0, portletId);

                if (portletIds.get(key) == null) {
                    portletIds.put(key, new Object[] { portletId, firstLayout.getPlid(), groupId, StringPool.BLANK,
                            StringPool.BLANK });
                }
            }
        }

        Element layoutsElement = rootElement.addElement("layouts");

        for (Layout layout : layouts) {
            exportLayout(portletDataContext, layoutConfigurationPortlet, layoutCache, portlets, portletIds,
                    exportPermissions, exportUserPermissions, layout, layoutsElement);
        }

        if (PropsValues.PERMISSIONS_USER_CHECK_ALGORITHM < 5) {
            Element rolesElement = rootElement.addElement("roles");

            if (exportPermissions) {
                _permissionExporter.exportLayoutRoles(layoutCache, companyId, groupId, rolesElement);
            }
        }

        long previousScopeGroupId = portletDataContext.getScopeGroupId();

        Element portletsElement = rootElement.addElement("portlets");

        for (Map.Entry<String, Object[]> portletIdsEntry : portletIds.entrySet()) {

            Object[] portletObjects = portletIdsEntry.getValue();

            String portletId = null;
            long plid = 0;
            long scopeGroupId = 0;
            String scopeType = StringPool.BLANK;
            String scopeLayoutUuid = null;

            if (portletObjects.length == 4) {
                portletId = (String) portletIdsEntry.getValue()[0];
                plid = (Long) portletIdsEntry.getValue()[1];
                scopeGroupId = (Long) portletIdsEntry.getValue()[2];
                scopeLayoutUuid = (String) portletIdsEntry.getValue()[3];
            } else {
                portletId = (String) portletIdsEntry.getValue()[0];
                plid = (Long) portletIdsEntry.getValue()[1];
                scopeGroupId = (Long) portletIdsEntry.getValue()[2];
                scopeType = (String) portletIdsEntry.getValue()[3];
                scopeLayoutUuid = (String) portletIdsEntry.getValue()[4];
            }

            Layout layout = LayoutLocalServiceUtil.getLayout(plid);

            portletDataContext.setPlid(layout.getPlid());
            portletDataContext.setOldPlid(layout.getPlid());
            portletDataContext.setScopeGroupId(scopeGroupId);
            portletDataContext.setScopeType(scopeType);
            portletDataContext.setScopeLayoutUuid(scopeLayoutUuid);

            boolean[] exportPortletControls = getExportPortletControls(companyId, portletId, portletDataContext,
                    parameterMap);

            _portletExporter.exportPortlet(portletDataContext, layoutCache, portletId, layout, portletsElement,
                    defaultUserId, exportPermissions, exportPortletArchivedSetups, exportPortletControls[0],
                    exportPortletControls[1], exportPortletUserPreferences, exportUserPermissions);
        }

        portletDataContext.setScopeGroupId(previousScopeGroupId);

        if (exportCategories) {
            exportAssetCategories(portletDataContext);
        }

        _portletExporter.exportAssetLinks(portletDataContext);
        _portletExporter.exportAssetTags(portletDataContext);
        _portletExporter.exportComments(portletDataContext);
        _portletExporter.exportExpandoTables(portletDataContext);
        _portletExporter.exportLocks(portletDataContext);

        if (exportPermissions) {
            _permissionExporter.exportPortletDataPermissions(portletDataContext);
        }

        _portletExporter.exportRatingsEntries(portletDataContext, rootElement);

        if (exportTheme && !portletDataContext.isPerformDirectBinaryImport()) {
            exportTheme(layoutSet, zipWriter);
        }

        if (_log.isInfoEnabled()) {
            if (stopWatch != null) {
                _log.info("Exporting layouts takes " + stopWatch.getTime() + " ms");
            } else {
                _log.info("Exporting layouts is finished");
            }
        }

        portletDataContext.addZipEntry("/manifest.xml", document.formattedString());

        try {
            return zipWriter.getFile();
        } finally {
            if (updateLastPublishDate) {
                updateLastPublishDate(layoutSet, lastPublishDate);
            }
        }
    }

    protected void exportAssetCategories(PortletDataContext portletDataContext) throws Exception {

        Document document = SAXReaderUtil.createDocument();

        Element rootElement = document.addElement("categories-hierarchy");

        Element assetVocabulariesElement = rootElement.addElement("vocabularies");

        List<AssetVocabulary> assetVocabularies = AssetVocabularyLocalServiceUtil
                .getGroupVocabularies(portletDataContext.getGroupId());

        for (AssetVocabulary assetVocabulary : assetVocabularies) {
            _portletExporter.exportAssetVocabulary(portletDataContext, assetVocabulariesElement, assetVocabulary);
        }

        Element categoriesElement = rootElement.addElement("categories");

        List<AssetCategory> assetCategories = AssetCategoryUtil.findByGroupId(portletDataContext.getGroupId());

        for (AssetCategory assetCategory : assetCategories) {
            _portletExporter.exportAssetCategory(portletDataContext, assetVocabulariesElement, categoriesElement,
                    assetCategory);
        }

        _portletExporter.exportAssetCategories(portletDataContext, rootElement);

        portletDataContext.addZipEntry(portletDataContext.getRootPath() + "/categories-hierarchy.xml",
                document.formattedString());
    }

    protected void exportJournalArticle(PortletDataContext portletDataContext, Layout layout, Element layoutElement)
            throws Exception {

        UnicodeProperties typeSettingsProperties = layout.getTypeSettingsProperties();

        String articleId = typeSettingsProperties.getProperty("article-id", StringPool.BLANK);

        long articleGroupId = layout.getGroupId();

        if (Validator.isNull(articleId)) {
            if (_log.isWarnEnabled()) {
                _log.warn("No article id found in typeSettings of layout " + layout.getPlid());
            }
        }

        JournalArticle article = null;

        try {
            article = JournalArticleLocalServiceUtil.getLatestArticle(articleGroupId, articleId,
                    WorkflowConstants.STATUS_APPROVED);
        } catch (NoSuchArticleException nsae) {
            if (_log.isWarnEnabled()) {
                _log.warn("No approved article found with group id " + articleGroupId + " and article id "
                        + articleId);
            }
        }

        if (article == null) {
            return;
        }

        String path = JournalPortletDataHandlerImpl.getArticlePath(portletDataContext, article);

        Element articleElement = layoutElement.addElement("article");

        articleElement.addAttribute("path", path);

        Element dlFileEntryTypesElement = layoutElement.addElement("dl-file-entry-types");
        Element dlFoldersElement = layoutElement.addElement("dl-folders");
        Element dlFilesElement = layoutElement.addElement("dl-file-entries");
        Element dlFileRanksElement = layoutElement.addElement("dl-file-ranks");

        JournalPortletDataHandlerImpl.exportArticle(portletDataContext, layoutElement, layoutElement, layoutElement,
                dlFileEntryTypesElement, dlFoldersElement, dlFilesElement, dlFileRanksElement, article, false);
    }

    protected void exportLayout(PortletDataContext portletDataContext, Portlet layoutConfigurationPortlet,
            LayoutCache layoutCache, List<Portlet> portlets, Map<String, Object[]> portletIds,
            boolean exportPermissions, boolean exportUserPermissions, Layout layout, Element layoutsElement)
            throws Exception {

        String path = portletDataContext.getLayoutPath(layout.getLayoutId()) + "/layout.xml";

        if (!portletDataContext.isPathNotProcessed(path)) {
            return;
        }

        LayoutRevision layoutRevision = null;

        if (LayoutStagingUtil.isBranchingLayout(layout)) {
            ServiceContext serviceContext = ServiceContextThreadLocal.getServiceContext();

            long layoutSetBranchId = ParamUtil.getLong(serviceContext, "layoutSetBranchId");

            if (layoutSetBranchId <= 0) {
                return;
            }

            layoutRevision = LayoutRevisionUtil.fetchByL_H_P(layoutSetBranchId, true, layout.getPlid());

            if (layoutRevision == null) {
                return;
            }

            LayoutStagingHandler layoutStagingHandler = LayoutStagingUtil.getLayoutStagingHandler(layout);

            layoutStagingHandler.setLayoutRevision(layoutRevision);
        }

        Element layoutElement = layoutsElement.addElement("layout");

        if (layoutRevision != null) {
            layoutElement.addAttribute("layout-revision-id", String.valueOf(layoutRevision.getLayoutRevisionId()));
            layoutElement.addAttribute("layout-branch-id", String.valueOf(layoutRevision.getLayoutBranchId()));
            layoutElement.addAttribute("layout-branch-name",
                    String.valueOf(layoutRevision.getLayoutBranch().getName()));
        }

        layoutElement.addAttribute("layout-uuid", layout.getUuid());
        layoutElement.addAttribute("layout-id", String.valueOf(layout.getLayoutId()));

        long parentLayoutId = layout.getParentLayoutId();

        if (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
            Layout parentLayout = LayoutLocalServiceUtil.getLayout(layout.getGroupId(), layout.isPrivateLayout(),
                    parentLayoutId);

            if (parentLayout != null) {
                layoutElement.addAttribute("parent-layout-uuid", parentLayout.getUuid());
            }
        }

        boolean deleteLayout = MapUtil.getBoolean(portletDataContext.getParameterMap(),
                "delete_" + layout.getPlid());

        if (deleteLayout) {
            layoutElement.addAttribute("delete", String.valueOf(true));

            return;
        }

        portletDataContext.setPlid(layout.getPlid());

        if (layout.isIconImage()) {
            Image image = ImageLocalServiceUtil.getImage(layout.getIconImageId());

            if (image != null) {
                String iconPath = getLayoutIconPath(portletDataContext, layout, image);

                layoutElement.addElement("icon-image-path").addText(iconPath);

                portletDataContext.addZipEntry(iconPath, image.getTextObj());
            }
        }

        _portletExporter.exportPortletData(portletDataContext, layoutConfigurationPortlet, layout, null,
                layoutElement);

        // Layout permissions

        if (exportPermissions) {
            _permissionExporter.exportLayoutPermissions(portletDataContext, layoutCache,
                    portletDataContext.getCompanyId(), portletDataContext.getScopeGroupId(), layout, layoutElement,
                    exportUserPermissions);
        }

        if (layout.isTypeArticle()) {
            exportJournalArticle(portletDataContext, layout, layoutElement);
        } else if (layout.isTypeLinkToLayout()) {
            UnicodeProperties typeSettingsProperties = layout.getTypeSettingsProperties();

            long linkToLayoutId = GetterUtil
                    .getLong(typeSettingsProperties.getProperty("linkToLayoutId", StringPool.BLANK));

            if (linkToLayoutId > 0) {
                try {
                    Layout linkedToLayout = LayoutLocalServiceUtil.getLayout(portletDataContext.getScopeGroupId(),
                            layout.isPrivateLayout(), linkToLayoutId);

                    exportLayout(portletDataContext, layoutConfigurationPortlet, layoutCache, portlets, portletIds,
                            exportPermissions, exportUserPermissions, linkedToLayout, layoutsElement);
                } catch (NoSuchLayoutException nsle) {
                }
            }
        } else if (layout.isTypePortlet()) {
            for (Portlet portlet : portlets) {
                if (portlet.isScopeable() && layout.hasScopeGroup()) {
                    String key = PortletPermissionUtil.getPrimaryKey(layout.getPlid(), portlet.getPortletId());

                    portletIds.put(key, new Object[] { portlet.getPortletId(), layout.getPlid(),
                            layout.getScopeGroup().getGroupId(), StringPool.BLANK, layout.getUuid() });
                }
            }

            LayoutTypePortlet layoutTypePortlet = (LayoutTypePortlet) layout.getLayoutType();

            for (String portletId : layoutTypePortlet.getPortletIds()) {
                javax.portlet.PortletPreferences jxPreferences = PortletPreferencesFactoryUtil
                        .getLayoutPortletSetup(layout, portletId);

                String scopeType = GetterUtil.getString(jxPreferences.getValue("lfrScopeType", null));
                String scopeLayoutUuid = GetterUtil.getString(jxPreferences.getValue("lfrScopeLayoutUuid", null));

                long scopeGroupId = portletDataContext.getScopeGroupId();

                if (Validator.isNotNull(scopeType)) {
                    Group scopeGroup = null;

                    if (scopeType.equals("company")) {
                        scopeGroup = GroupLocalServiceUtil.getCompanyGroup(layout.getCompanyId());
                    } else if (scopeType.equals("layout")) {
                        Layout scopeLayout = null;

                        scopeLayout = LayoutLocalServiceUtil.fetchLayoutByUuidAndGroupId(scopeLayoutUuid,
                                portletDataContext.getGroupId());

                        if (scopeLayout == null) {
                            continue;
                        }

                        scopeGroup = scopeLayout.getScopeGroup();
                    } else {
                        throw new IllegalArgumentException("Scope type " + scopeType + " is invalid");
                    }

                    if (scopeGroup != null) {
                        scopeGroupId = scopeGroup.getGroupId();
                    }
                }

                String key = PortletPermissionUtil.getPrimaryKey(layout.getPlid(), portletId);

                portletIds.put(key,
                        new Object[] { portletId, layout.getPlid(), scopeGroupId, scopeType, scopeLayoutUuid });
            }
        }

        fixTypeSettings(layout);

        layoutElement.addAttribute("path", path);

        portletDataContext.addExpando(layoutElement, path, layout);

        portletDataContext.addZipEntry(path, layout);
    }

    protected void exportTheme(LayoutSet layoutSet, ZipWriter zipWriter) throws Exception {

        Theme theme = layoutSet.getTheme();

        String lookAndFeelXML = ContentUtil.get("com/liferay/portal/dependencies/liferay-look-and-feel.xml.tmpl");

        lookAndFeelXML = StringUtil.replace(lookAndFeelXML,
                new String[] { "[$TEMPLATE_EXTENSION$]", "[$VIRTUAL_PATH$]" },
                new String[] { theme.getTemplateExtension(), theme.getVirtualPath() });

        String servletContextName = theme.getServletContextName();

        ServletContext servletContext = ServletContextPool.get(servletContextName);

        if (servletContext == null) {
            if (_log.isWarnEnabled()) {
                _log.warn("Servlet context not found for theme " + theme.getThemeId());
            }

            return;
        }

        File themeZip = new File(zipWriter.getPath() + "/theme.zip");

        ZipWriter themeZipWriter = ZipWriterFactoryUtil.getZipWriter(themeZip);

        themeZipWriter.addEntry("liferay-look-and-feel.xml", lookAndFeelXML);

        File cssPath = null;
        File imagesPath = null;
        File javaScriptPath = null;
        File templatesPath = null;

        if (!theme.isLoadFromServletContext()) {
            ThemeLoader themeLoader = ThemeLoaderFactory.getThemeLoader(servletContextName);

            if (themeLoader == null) {
                _log.error(servletContextName + " does not map to a theme loader");
            } else {
                String realPath = themeLoader.getFileStorage().getPath() + StringPool.SLASH + theme.getName();

                cssPath = new File(realPath + "/css");
                imagesPath = new File(realPath + "/images");
                javaScriptPath = new File(realPath + "/javascript");
                templatesPath = new File(realPath + "/templates");
            }
        } else {
            cssPath = new File(servletContext.getRealPath(theme.getCssPath()));
            imagesPath = new File(servletContext.getRealPath(theme.getImagesPath()));
            javaScriptPath = new File(servletContext.getRealPath(theme.getJavaScriptPath()));
            templatesPath = new File(servletContext.getRealPath(theme.getTemplatesPath()));
        }

        exportThemeFiles("css", cssPath, themeZipWriter);
        exportThemeFiles("images", imagesPath, themeZipWriter);
        exportThemeFiles("javascript", javaScriptPath, themeZipWriter);
        exportThemeFiles("templates", templatesPath, themeZipWriter);
    }

    protected void exportThemeFiles(String path, File dir, ZipWriter zipWriter) throws Exception {

        if ((dir == null) || (!dir.exists())) {
            return;
        }

        File[] files = dir.listFiles();

        for (File file : files) {
            if (file.isDirectory()) {
                exportThemeFiles(path + StringPool.SLASH + file.getName(), file, zipWriter);
            } else {
                zipWriter.addEntry(path + StringPool.SLASH + file.getName(), FileUtil.getBytes(file));
            }
        }
    }

    protected void fixTypeSettings(Layout layout) throws Exception {

        if (!layout.isTypeURL()) {
            return;
        }

        UnicodeProperties typeSettings = layout.getTypeSettingsProperties();

        String url = GetterUtil.getString(typeSettings.getProperty("url"));

        String friendlyURLPrivateGroupPath = PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
        String friendlyURLPrivateUserPath = PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
        String friendlyURLPublicPath = PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;

        if (!url.startsWith(friendlyURLPrivateGroupPath) && !url.startsWith(friendlyURLPrivateUserPath)
                && !url.startsWith(friendlyURLPublicPath)) {

            return;
        }

        int x = url.indexOf(CharPool.SLASH, 1);

        if (x == -1) {
            return;
        }

        int y = url.indexOf(CharPool.SLASH, x + 1);

        if (y == -1) {
            return;
        }

        String friendlyURL = url.substring(x, y);
        String groupFriendlyURL = layout.getGroup().getFriendlyURL();

        if (!friendlyURL.equals(groupFriendlyURL)) {
            return;
        }

        typeSettings.setProperty("url", url.substring(0, x) + SAME_GROUP_FRIENDLY_URL + url.substring(y));
    }

    protected boolean[] getExportPortletControls(long companyId, String portletId,
            PortletDataContext portletDataContext, Map<String, String[]> parameterMap) throws Exception {

        boolean exportPortletData = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
        boolean exportPortletDataAll = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.PORTLET_DATA_ALL);
        boolean exportPortletSetup = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
        boolean exportPortletSetupAll = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.PORTLET_SETUP_ALL);

        if (_log.isDebugEnabled()) {
            _log.debug("Export portlet data " + exportPortletData);
            _log.debug("Export all portlet data " + exportPortletDataAll);
            _log.debug("Export portlet setup " + exportPortletSetup);
        }

        boolean exportCurPortletData = exportPortletData;
        boolean exportCurPortletSetup = exportPortletSetup;

        // If PORTLET_DATA_ALL is true, this means that staging has just been
        // activated and all data and setup must be exported. There is no
        // portlet export control to check in this case.

        if (exportPortletDataAll) {
            exportCurPortletData = true;
            exportCurPortletSetup = true;
        } else {
            Portlet portlet = PortletLocalServiceUtil.getPortletById(companyId, portletId);

            if (portlet != null) {
                String portletDataHandlerClass = portlet.getPortletDataHandlerClass();

                // Checking if the portlet has a data handler, if it doesn't,
                // the default values are the ones set in PORTLET_DATA and
                // PORTLET_SETUP. If it has a data handler, iterate over each
                // portlet export control.

                if (portletDataHandlerClass != null) {
                    String rootPortletId = PortletConstants.getRootPortletId(portletId);

                    // PORTLET_DATA and the PORTLET_DATA for this specific
                    // data handler must be true

                    exportCurPortletData = exportPortletData && MapUtil.getBoolean(parameterMap,
                            PortletDataHandlerKeys.PORTLET_DATA + StringPool.UNDERLINE + rootPortletId);

                    // PORTLET_SETUP and the PORTLET_SETUP for this specific
                    // data handler must be true

                    exportCurPortletSetup = exportPortletSetup && MapUtil.getBoolean(parameterMap,
                            PortletDataHandlerKeys.PORTLET_SETUP + StringPool.UNDERLINE + rootPortletId);
                }
            }
        }

        if (exportPortletSetupAll) {
            exportCurPortletSetup = true;
        }

        return new boolean[] { exportCurPortletData, exportCurPortletSetup };
    }

    protected String getLayoutIconPath(PortletDataContext portletDataContext, Layout layout, Image image) {

        StringBundler sb = new StringBundler(5);

        sb.append(portletDataContext.getLayoutPath(layout.getLayoutId()));
        sb.append("/icons/");
        sb.append(image.getImageId());
        sb.append(StringPool.PERIOD);
        sb.append(image.getType());

        return sb.toString();
    }

    protected String getLayoutSetPrototype(PortletDataContext portletDataContext, String layoutSetPrototypeUuid) {

        StringBundler sb = new StringBundler(3);

        sb.append(portletDataContext.getRootPath());
        sb.append("/layout-set-prototype/");
        sb.append(layoutSetPrototypeUuid);

        return sb.toString();
    }

    private static Log _log = LogFactoryUtil.getLog(LayoutExporter.class);

    private PermissionExporter _permissionExporter = new PermissionExporter();
    private PortletExporter _portletExporter = new PortletExporter();

}