001    /**
002     * Copyright (c) 2000-2012 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.lar;
016    
017    import com.liferay.portal.NoSuchLayoutException;
018    import com.liferay.portal.kernel.lar.ImportExportThreadLocal;
019    import com.liferay.portal.kernel.lar.PortletDataContext;
020    import com.liferay.portal.kernel.lar.PortletDataHandler;
021    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.servlet.ServletContextPool;
025    import com.liferay.portal.kernel.staging.LayoutStagingUtil;
026    import com.liferay.portal.kernel.util.CharPool;
027    import com.liferay.portal.kernel.util.FileUtil;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.MapUtil;
030    import com.liferay.portal.kernel.util.ParamUtil;
031    import com.liferay.portal.kernel.util.ReleaseInfo;
032    import com.liferay.portal.kernel.util.StringBundler;
033    import com.liferay.portal.kernel.util.StringPool;
034    import com.liferay.portal.kernel.util.StringUtil;
035    import com.liferay.portal.kernel.util.Time;
036    import com.liferay.portal.kernel.util.UnicodeProperties;
037    import com.liferay.portal.kernel.util.Validator;
038    import com.liferay.portal.kernel.workflow.WorkflowConstants;
039    import com.liferay.portal.kernel.xml.Document;
040    import com.liferay.portal.kernel.xml.Element;
041    import com.liferay.portal.kernel.xml.SAXReaderUtil;
042    import com.liferay.portal.kernel.zip.ZipWriter;
043    import com.liferay.portal.kernel.zip.ZipWriterFactoryUtil;
044    import com.liferay.portal.model.Group;
045    import com.liferay.portal.model.Image;
046    import com.liferay.portal.model.Layout;
047    import com.liferay.portal.model.LayoutConstants;
048    import com.liferay.portal.model.LayoutRevision;
049    import com.liferay.portal.model.LayoutSet;
050    import com.liferay.portal.model.LayoutSetPrototype;
051    import com.liferay.portal.model.LayoutStagingHandler;
052    import com.liferay.portal.model.LayoutTypePortlet;
053    import com.liferay.portal.model.Portlet;
054    import com.liferay.portal.model.PortletConstants;
055    import com.liferay.portal.model.Theme;
056    import com.liferay.portal.service.GroupLocalServiceUtil;
057    import com.liferay.portal.service.ImageLocalServiceUtil;
058    import com.liferay.portal.service.LayoutLocalServiceUtil;
059    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
060    import com.liferay.portal.service.LayoutSetPrototypeLocalServiceUtil;
061    import com.liferay.portal.service.PortletLocalServiceUtil;
062    import com.liferay.portal.service.ServiceContext;
063    import com.liferay.portal.service.ServiceContextThreadLocal;
064    import com.liferay.portal.service.UserLocalServiceUtil;
065    import com.liferay.portal.service.permission.PortletPermissionUtil;
066    import com.liferay.portal.service.persistence.LayoutRevisionUtil;
067    import com.liferay.portal.theme.ThemeLoader;
068    import com.liferay.portal.theme.ThemeLoaderFactory;
069    import com.liferay.portal.util.PortletKeys;
070    import com.liferay.portal.util.PropsValues;
071    import com.liferay.portlet.PortletPreferencesFactoryUtil;
072    import com.liferay.portlet.asset.model.AssetCategory;
073    import com.liferay.portlet.asset.model.AssetVocabulary;
074    import com.liferay.portlet.asset.service.AssetVocabularyLocalServiceUtil;
075    import com.liferay.portlet.asset.service.persistence.AssetCategoryUtil;
076    import com.liferay.portlet.journal.NoSuchArticleException;
077    import com.liferay.portlet.journal.lar.JournalPortletDataHandlerImpl;
078    import com.liferay.portlet.journal.model.JournalArticle;
079    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
080    import com.liferay.portlet.sites.util.SitesUtil;
081    import com.liferay.util.ContentUtil;
082    
083    import de.schlichtherle.io.FileInputStream;
084    
085    import java.io.File;
086    
087    import java.util.Date;
088    import java.util.HashSet;
089    import java.util.Iterator;
090    import java.util.LinkedHashMap;
091    import java.util.List;
092    import java.util.Map;
093    
094    import javax.servlet.ServletContext;
095    
096    import org.apache.commons.lang.time.StopWatch;
097    
098    /**
099     * @author Brian Wing Shun Chan
100     * @author Joel Kozikowski
101     * @author Charles May
102     * @author Raymond Augé
103     * @author Jorge Ferrer
104     * @author Bruno Farache
105     * @author Karthik Sudarshan
106     * @author Zsigmond Rab
107     * @author Douglas Wong
108     */
109    public class LayoutExporter {
110    
111            public static final String SAME_GROUP_FRIENDLY_URL =
112                    "/[$SAME_GROUP_FRIENDLY_URL$]";
113    
114            public static List<Portlet> getAlwaysExportablePortlets(long companyId)
115                    throws Exception {
116    
117                    List<Portlet> portlets = PortletLocalServiceUtil.getPortlets(companyId);
118    
119                    Iterator<Portlet> itr = portlets.iterator();
120    
121                    while (itr.hasNext()) {
122                            Portlet portlet = itr.next();
123    
124                            if (!portlet.isActive()) {
125                                    itr.remove();
126    
127                                    continue;
128                            }
129    
130                            PortletDataHandler portletDataHandler =
131                                    portlet.getPortletDataHandlerInstance();
132    
133                            if ((portletDataHandler == null) ||
134                                    (!portletDataHandler.isAlwaysExportable())) {
135    
136                                    itr.remove();
137                            }
138                    }
139    
140                    return portlets;
141            }
142    
143            public static void updateLastPublishDate(
144                            LayoutSet layoutSet, long lastPublishDate)
145                    throws Exception {
146    
147                    UnicodeProperties settingsProperties =
148                            layoutSet.getSettingsProperties();
149    
150                    if (lastPublishDate <= 0) {
151                            settingsProperties.remove("last-publish-date");
152                    }
153                    else {
154                            settingsProperties.setProperty(
155                                    "last-publish-date", String.valueOf(lastPublishDate));
156                    }
157    
158                    LayoutSetLocalServiceUtil.updateSettings(
159                            layoutSet.getGroupId(), layoutSet.isPrivateLayout(),
160                            settingsProperties.toString());
161            }
162    
163            public byte[] exportLayouts(
164                            long groupId, boolean privateLayout, long[] layoutIds,
165                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
166                    throws Exception {
167    
168                    File file = exportLayoutsAsFile(
169                            groupId, privateLayout, layoutIds, parameterMap, startDate,
170                            endDate);
171    
172                    try {
173                            return FileUtil.getBytes(file);
174                    }
175                    finally {
176                            file.delete();
177                    }
178            }
179    
180            public File exportLayoutsAsFile(
181                            long groupId, boolean privateLayout, long[] layoutIds,
182                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
183                    throws Exception {
184    
185                    try {
186                            ImportExportThreadLocal.setLayoutExportInProcess(true);
187    
188                            return doExportLayoutsAsFile(
189                                    groupId, privateLayout, layoutIds, parameterMap, startDate,
190                                    endDate);
191                    }
192                    finally {
193                            ImportExportThreadLocal.setLayoutExportInProcess(false);
194                    }
195            }
196    
197            protected File doExportLayoutsAsFile(
198                            long groupId, boolean privateLayout, long[] layoutIds,
199                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
200                    throws Exception {
201    
202                    boolean exportCategories = MapUtil.getBoolean(
203                            parameterMap, PortletDataHandlerKeys.CATEGORIES);
204                    boolean exportIgnoreLastPublishDate = MapUtil.getBoolean(
205                            parameterMap, PortletDataHandlerKeys.IGNORE_LAST_PUBLISH_DATE);
206                    boolean exportPermissions = MapUtil.getBoolean(
207                            parameterMap, PortletDataHandlerKeys.PERMISSIONS);
208                    boolean exportUserPermissions = MapUtil.getBoolean(
209                            parameterMap, PortletDataHandlerKeys.USER_PERMISSIONS);
210                    boolean exportPortletArchivedSetups = MapUtil.getBoolean(
211                            parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
212                    boolean exportPortletUserPreferences = MapUtil.getBoolean(
213                            parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
214                    boolean exportTheme = MapUtil.getBoolean(
215                            parameterMap, PortletDataHandlerKeys.THEME);
216                    boolean exportThemeSettings = MapUtil.getBoolean(
217                            parameterMap, PortletDataHandlerKeys.THEME_REFERENCE);
218                    boolean publishToRemote = MapUtil.getBoolean(
219                            parameterMap, PortletDataHandlerKeys.PUBLISH_TO_REMOTE);
220                    boolean updateLastPublishDate = MapUtil.getBoolean(
221                            parameterMap, PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE);
222    
223                    if (_log.isDebugEnabled()) {
224                            _log.debug("Export categories " + exportCategories);
225                            _log.debug("Export permissions " + exportPermissions);
226                            _log.debug("Export user permissions " + exportUserPermissions);
227                            _log.debug(
228                                    "Export portlet archived setups " +
229                                            exportPortletArchivedSetups);
230                            _log.debug(
231                                    "Export portlet user preferences " +
232                                            exportPortletUserPreferences);
233                            _log.debug("Export theme " + exportTheme);
234                    }
235    
236                    LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
237                            groupId, privateLayout);
238    
239                    long companyId = layoutSet.getCompanyId();
240                    long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
241    
242                    ServiceContext serviceContext =
243                            ServiceContextThreadLocal.getServiceContext();
244    
245                    if (serviceContext == null) {
246                            serviceContext = new ServiceContext();
247    
248                            serviceContext.setCompanyId(companyId);
249                            serviceContext.setSignedIn(false);
250                            serviceContext.setUserId(defaultUserId);
251    
252                            ServiceContextThreadLocal.pushServiceContext(serviceContext);
253                    }
254    
255                    serviceContext.setAttribute("exporting", Boolean.TRUE);
256    
257                    long layoutSetBranchId = MapUtil.getLong(
258                            parameterMap, "layoutSetBranchId");
259    
260                    serviceContext.setAttribute("layoutSetBranchId", layoutSetBranchId);
261    
262                    long lastPublishDate = System.currentTimeMillis();
263    
264                    if (endDate != null) {
265                            lastPublishDate = endDate.getTime();
266                    }
267    
268                    if (exportIgnoreLastPublishDate) {
269                            endDate = null;
270                            startDate = null;
271                    }
272    
273                    StopWatch stopWatch = null;
274    
275                    if (_log.isInfoEnabled()) {
276                            stopWatch = new StopWatch();
277    
278                            stopWatch.start();
279                    }
280    
281                    LayoutCache layoutCache = new LayoutCache();
282    
283                    ZipWriter zipWriter = ZipWriterFactoryUtil.getZipWriter();
284    
285                    PortletDataContext portletDataContext = new PortletDataContextImpl(
286                            companyId, groupId, parameterMap, new HashSet<String>(), startDate,
287                            endDate, zipWriter);
288    
289                    portletDataContext.setPortetDataContextListener(
290                            new PortletDataContextListenerImpl(portletDataContext));
291    
292                    Document document = SAXReaderUtil.createDocument();
293    
294                    Element rootElement = document.addElement("root");
295    
296                    Element headerElement = rootElement.addElement("header");
297    
298                    headerElement.addAttribute(
299                            "build-number", String.valueOf(ReleaseInfo.getBuildNumber()));
300                    headerElement.addAttribute("export-date", Time.getRFC822());
301    
302                    if (portletDataContext.hasDateRange()) {
303                            headerElement.addAttribute(
304                                    "start-date",
305                                    String.valueOf(portletDataContext.getStartDate()));
306                            headerElement.addAttribute(
307                                    "end-date", String.valueOf(portletDataContext.getEndDate()));
308                    }
309    
310                    headerElement.addAttribute("type", "layout-set");
311                    headerElement.addAttribute("group-id", String.valueOf(groupId));
312                    headerElement.addAttribute(
313                            "private-layout", String.valueOf(privateLayout));
314    
315                    Group group = layoutSet.getGroup();
316    
317                    if (group.isLayoutSetPrototype()) {
318                            LayoutSetPrototype layoutSetPrototype =
319                                    LayoutSetPrototypeLocalServiceUtil.getLayoutSetPrototype(
320                                            group.getClassPK());
321    
322                            String layoutSetPrototypeUuid = layoutSetPrototype.getUuid();
323    
324                            headerElement.addAttribute(
325                                    "layout-set-prototype-uuid", layoutSetPrototypeUuid);
326    
327                            if (publishToRemote) {
328                                    String path = getLayoutSetPrototype(
329                                            portletDataContext, layoutSetPrototypeUuid);
330    
331                                    File layoutSetPrototypeFile =
332                                            SitesUtil.exportLayoutSetPrototype(
333                                                    layoutSetPrototype, serviceContext);
334    
335                                    try {
336                                            portletDataContext.addZipEntry(
337                                                    path.concat(".lar"),
338                                                    new FileInputStream(layoutSetPrototypeFile));
339                                            portletDataContext.addZipEntry(
340                                                    path.concat(".xml"), layoutSetPrototype);
341                                    }
342                                    finally {
343                                            layoutSetPrototypeFile.delete();
344                                    }
345                            }
346                    }
347    
348                    if (exportTheme || exportThemeSettings) {
349                            headerElement.addAttribute("theme-id", layoutSet.getThemeId());
350                            headerElement.addAttribute(
351                                    "color-scheme-id", layoutSet.getColorSchemeId());
352                    }
353    
354                    Element cssElement = headerElement.addElement("css");
355    
356                    cssElement.addCDATA(layoutSet.getCss());
357    
358                    Portlet layoutConfigurationPortlet =
359                            PortletLocalServiceUtil.getPortletById(
360                                    portletDataContext.getCompanyId(),
361                                    PortletKeys.LAYOUT_CONFIGURATION);
362    
363                    Map<String, Object[]> portletIds =
364                            new LinkedHashMap<String, Object[]>();
365    
366                    List<Layout> layouts = null;
367    
368                    if ((layoutIds == null) || (layoutIds.length == 0)) {
369                            layouts = LayoutLocalServiceUtil.getLayouts(groupId, privateLayout);
370                    }
371                    else {
372                            layouts = LayoutLocalServiceUtil.getLayouts(
373                                    groupId, privateLayout, layoutIds);
374                    }
375    
376                    List<Portlet> portlets = getAlwaysExportablePortlets(companyId);
377    
378                    if (!layouts.isEmpty()) {
379                            Layout firstLayout = layouts.get(0);
380    
381                            if (group.isStagingGroup()) {
382                                    group = group.getLiveGroup();
383                            }
384    
385                            for (Portlet portlet : portlets) {
386                                    String portletId = portlet.getRootPortletId();
387    
388                                    if (!group.isStagedPortlet(portletId)) {
389                                            continue;
390                                    }
391    
392                                    String key = PortletPermissionUtil.getPrimaryKey(0, portletId);
393    
394                                    if (portletIds.get(key) == null) {
395                                            portletIds.put(
396                                                    key,
397                                                    new Object[] {
398                                                            portletId, firstLayout.getPlid(), groupId,
399                                                            StringPool.BLANK, StringPool.BLANK
400                                                    });
401                                    }
402                            }
403                    }
404    
405                    Element layoutsElement = rootElement.addElement("layouts");
406    
407                    for (Layout layout : layouts) {
408                            exportLayout(
409                                    portletDataContext, layoutConfigurationPortlet, layoutCache,
410                                    portlets, portletIds, exportPermissions, exportUserPermissions,
411                                    layout, layoutsElement);
412                    }
413    
414                    if (PropsValues.PERMISSIONS_USER_CHECK_ALGORITHM < 5) {
415                            Element rolesElement = rootElement.addElement("roles");
416    
417                            if (exportPermissions) {
418                                    _permissionExporter.exportLayoutRoles(
419                                            layoutCache, companyId, groupId, rolesElement);
420                            }
421                    }
422    
423                    long previousScopeGroupId = portletDataContext.getScopeGroupId();
424    
425                    Element portletsElement = rootElement.addElement("portlets");
426    
427                    for (Map.Entry<String, Object[]> portletIdsEntry :
428                                    portletIds.entrySet()) {
429    
430                            Object[] portletObjects = portletIdsEntry.getValue();
431    
432                            String portletId = null;
433                            long plid = 0;
434                            long scopeGroupId = 0;
435                            String scopeType = StringPool.BLANK;
436                            String scopeLayoutUuid = null;
437    
438                            if (portletObjects.length == 4) {
439                                    portletId = (String)portletIdsEntry.getValue()[0];
440                                    plid = (Long)portletIdsEntry.getValue()[1];
441                                    scopeGroupId = (Long)portletIdsEntry.getValue()[2];
442                                    scopeLayoutUuid = (String)portletIdsEntry.getValue()[3];
443                            }
444                            else {
445                                    portletId = (String)portletIdsEntry.getValue()[0];
446                                    plid = (Long)portletIdsEntry.getValue()[1];
447                                    scopeGroupId = (Long)portletIdsEntry.getValue()[2];
448                                    scopeType = (String)portletIdsEntry.getValue()[3];
449                                    scopeLayoutUuid = (String)portletIdsEntry.getValue()[4];
450                            }
451    
452                            Layout layout = LayoutLocalServiceUtil.getLayout(plid);
453    
454                            portletDataContext.setPlid(layout.getPlid());
455                            portletDataContext.setOldPlid(layout.getPlid());
456                            portletDataContext.setScopeGroupId(scopeGroupId);
457                            portletDataContext.setScopeType(scopeType);
458                            portletDataContext.setScopeLayoutUuid(scopeLayoutUuid);
459    
460                            boolean[] exportPortletControls = getExportPortletControls(
461                                    companyId, portletId, portletDataContext, parameterMap);
462    
463                            _portletExporter.exportPortlet(
464                                    portletDataContext, layoutCache, portletId, layout,
465                                    portletsElement, defaultUserId, exportPermissions,
466                                    exportPortletArchivedSetups, exportPortletControls[0],
467                                    exportPortletControls[1], exportPortletUserPreferences,
468                                    exportUserPermissions);
469                    }
470    
471                    portletDataContext.setScopeGroupId(previousScopeGroupId);
472    
473                    if (exportCategories) {
474                            exportAssetCategories(portletDataContext);
475                    }
476    
477                    _portletExporter.exportAssetLinks(portletDataContext);
478                    _portletExporter.exportAssetTags(portletDataContext);
479                    _portletExporter.exportComments(portletDataContext);
480                    _portletExporter.exportExpandoTables(portletDataContext);
481                    _portletExporter.exportLocks(portletDataContext);
482    
483                    if (exportPermissions) {
484                            _permissionExporter.exportPortletDataPermissions(
485                                    portletDataContext);
486                    }
487    
488                    _portletExporter.exportRatingsEntries(portletDataContext, rootElement);
489    
490                    if (exportTheme && !portletDataContext.isPerformDirectBinaryImport()) {
491                            exportTheme(layoutSet, zipWriter);
492                    }
493    
494                    if (_log.isInfoEnabled()) {
495                            if (stopWatch != null) {
496                                    _log.info(
497                                            "Exporting layouts takes " + stopWatch.getTime() + " ms");
498                            }
499                            else {
500                                    _log.info("Exporting layouts is finished");
501                            }
502                    }
503    
504                    portletDataContext.addZipEntry(
505                            "/manifest.xml", document.formattedString());
506    
507                    try {
508                            return zipWriter.getFile();
509                    }
510                    finally {
511                            if (updateLastPublishDate) {
512                                    updateLastPublishDate(layoutSet, lastPublishDate);
513                            }
514                    }
515            }
516    
517            protected void exportAssetCategories(PortletDataContext portletDataContext)
518                    throws Exception {
519    
520                    Document document = SAXReaderUtil.createDocument();
521    
522                    Element rootElement = document.addElement("categories-hierarchy");
523    
524                    Element assetVocabulariesElement = rootElement.addElement(
525                            "vocabularies");
526    
527                    List<AssetVocabulary> assetVocabularies =
528                            AssetVocabularyLocalServiceUtil.getGroupVocabularies(
529                                    portletDataContext.getGroupId());
530    
531                    for (AssetVocabulary assetVocabulary : assetVocabularies) {
532                            _portletExporter.exportAssetVocabulary(
533                                    portletDataContext, assetVocabulariesElement, assetVocabulary);
534                    }
535    
536                    Element categoriesElement = rootElement.addElement("categories");
537    
538                    List<AssetCategory> assetCategories =
539                            AssetCategoryUtil.findByGroupId(portletDataContext.getGroupId());
540    
541                    for (AssetCategory assetCategory : assetCategories) {
542                            _portletExporter.exportAssetCategory(
543                                    portletDataContext, assetVocabulariesElement, categoriesElement,
544                                    assetCategory);
545                    }
546    
547                    _portletExporter.exportAssetCategories(portletDataContext, rootElement);
548    
549                    portletDataContext.addZipEntry(
550                            portletDataContext.getRootPath() + "/categories-hierarchy.xml",
551                            document.formattedString());
552            }
553    
554            protected void exportJournalArticle(
555                            PortletDataContext portletDataContext, Layout layout,
556                            Element layoutElement)
557                    throws Exception {
558    
559                    UnicodeProperties typeSettingsProperties =
560                            layout.getTypeSettingsProperties();
561    
562                    String articleId = typeSettingsProperties.getProperty(
563                            "article-id", StringPool.BLANK);
564    
565                    long articleGroupId = layout.getGroupId();
566    
567                    if (Validator.isNull(articleId)) {
568                            if (_log.isWarnEnabled()) {
569                                    _log.warn(
570                                            "No article id found in typeSettings of layout " +
571                                                    layout.getPlid());
572                            }
573                    }
574    
575                    JournalArticle article = null;
576    
577                    try {
578                            article = JournalArticleLocalServiceUtil.getLatestArticle(
579                                    articleGroupId, articleId,
580                                    WorkflowConstants.STATUS_APPROVED);
581                    }
582                    catch (NoSuchArticleException nsae) {
583                            if (_log.isWarnEnabled()) {
584                                    _log.warn(
585                                            "No approved article found with group id " +
586                                                    articleGroupId + " and article id " + articleId);
587                            }
588                    }
589    
590                    if (article == null) {
591                            return;
592                    }
593    
594                    String path = JournalPortletDataHandlerImpl.getArticlePath(
595                            portletDataContext, article);
596    
597                    Element articleElement = layoutElement.addElement("article");
598    
599                    articleElement.addAttribute("path", path);
600    
601                    Element dlFileEntryTypesElement = layoutElement.addElement(
602                            "dl-file-entry-types");
603                    Element dlFoldersElement = layoutElement.addElement("dl-folders");
604                    Element dlFilesElement = layoutElement.addElement("dl-file-entries");
605                    Element dlFileRanksElement = layoutElement.addElement("dl-file-ranks");
606    
607                    JournalPortletDataHandlerImpl.exportArticle(
608                            portletDataContext, layoutElement, layoutElement, layoutElement,
609                            dlFileEntryTypesElement, dlFoldersElement, dlFilesElement,
610                            dlFileRanksElement, article, false);
611            }
612    
613            protected void exportLayout(
614                            PortletDataContext portletDataContext,
615                            Portlet layoutConfigurationPortlet, LayoutCache layoutCache,
616                            List<Portlet> portlets, Map<String, Object[]> portletIds,
617                            boolean exportPermissions, boolean exportUserPermissions,
618                            Layout layout, Element layoutsElement)
619                    throws Exception {
620    
621                    String path = portletDataContext.getLayoutPath(
622                            layout.getLayoutId()) + "/layout.xml";
623    
624                    if (!portletDataContext.isPathNotProcessed(path)) {
625                            return;
626                    }
627    
628                    LayoutRevision layoutRevision = null;
629    
630                    if (LayoutStagingUtil.isBranchingLayout(layout)) {
631                            ServiceContext serviceContext =
632                                    ServiceContextThreadLocal.getServiceContext();
633    
634                            long layoutSetBranchId = ParamUtil.getLong(
635                                    serviceContext, "layoutSetBranchId");
636    
637                            if (layoutSetBranchId <= 0) {
638                                    return;
639                            }
640    
641                            layoutRevision = LayoutRevisionUtil.fetchByL_H_P(
642                                    layoutSetBranchId, true, layout.getPlid());
643    
644                            if (layoutRevision == null) {
645                                            return;
646                            }
647    
648                            LayoutStagingHandler layoutStagingHandler =
649                                    LayoutStagingUtil.getLayoutStagingHandler(layout);
650    
651                            layoutStagingHandler.setLayoutRevision(layoutRevision);
652                    }
653    
654                    Element layoutElement = layoutsElement.addElement("layout");
655    
656                    if (layoutRevision != null) {
657                            layoutElement.addAttribute(
658                                    "layout-revision-id",
659                                    String.valueOf(layoutRevision.getLayoutRevisionId()));
660                            layoutElement.addAttribute(
661                                    "layout-branch-id",
662                                    String.valueOf(layoutRevision.getLayoutBranchId()));
663                            layoutElement.addAttribute(
664                                    "layout-branch-name",
665                                    String.valueOf(layoutRevision.getLayoutBranch().getName()));
666                    }
667    
668                    layoutElement.addAttribute("layout-uuid", layout.getUuid());
669                    layoutElement.addAttribute(
670                            "layout-id", String.valueOf(layout.getLayoutId()));
671    
672                    long parentLayoutId = layout.getParentLayoutId();
673    
674                    if (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
675                            Layout parentLayout = LayoutLocalServiceUtil.getLayout(
676                                    layout.getGroupId(), layout.isPrivateLayout(), parentLayoutId);
677    
678                            if (parentLayout != null) {
679                                    layoutElement.addAttribute(
680                                            "parent-layout-uuid", parentLayout.getUuid());
681                            }
682                    }
683    
684                    boolean deleteLayout = MapUtil.getBoolean(
685                            portletDataContext.getParameterMap(), "delete_" + layout.getPlid());
686    
687                    if (deleteLayout) {
688                            layoutElement.addAttribute("delete", String.valueOf(true));
689    
690                            return;
691                    }
692    
693                    portletDataContext.setPlid(layout.getPlid());
694    
695                    if (layout.isIconImage()) {
696                            Image image = ImageLocalServiceUtil.getImage(
697                                    layout.getIconImageId());
698    
699                            if (image != null) {
700                                    String iconPath = getLayoutIconPath(
701                                            portletDataContext, layout, image);
702    
703                                    layoutElement.addElement("icon-image-path").addText(iconPath);
704    
705                                    portletDataContext.addZipEntry(iconPath, image.getTextObj());
706                            }
707                    }
708    
709                    _portletExporter.exportPortletData(
710                            portletDataContext, layoutConfigurationPortlet, layout, null,
711                            layoutElement);
712    
713                    // Layout permissions
714    
715                    if (exportPermissions) {
716                            _permissionExporter.exportLayoutPermissions(
717                                    portletDataContext, layoutCache,
718                                    portletDataContext.getCompanyId(),
719                                    portletDataContext.getScopeGroupId(), layout, layoutElement,
720                                    exportUserPermissions);
721                    }
722    
723                    if (layout.isTypeArticle()) {
724                            exportJournalArticle(portletDataContext, layout, layoutElement);
725                    }
726                    else if (layout.isTypeLinkToLayout()) {
727                            UnicodeProperties typeSettingsProperties =
728                                    layout.getTypeSettingsProperties();
729    
730                            long linkToLayoutId = GetterUtil.getLong(
731                                    typeSettingsProperties.getProperty(
732                                            "linkToLayoutId", StringPool.BLANK));
733    
734                            if (linkToLayoutId > 0) {
735                                    try {
736                                            Layout linkedToLayout = LayoutLocalServiceUtil.getLayout(
737                                                    portletDataContext.getScopeGroupId(),
738                                                    layout.isPrivateLayout(), linkToLayoutId);
739    
740                                            exportLayout(
741                                                    portletDataContext, layoutConfigurationPortlet,
742                                                    layoutCache, portlets, portletIds, exportPermissions,
743                                                    exportUserPermissions, linkedToLayout, layoutsElement);
744                                    }
745                                    catch (NoSuchLayoutException nsle) {
746                                    }
747                            }
748                    }
749                    else if (layout.isTypePortlet()) {
750                            for (Portlet portlet : portlets) {
751                                    if (portlet.isScopeable() && layout.hasScopeGroup()) {
752                                            String key = PortletPermissionUtil.getPrimaryKey(
753                                                    layout.getPlid(), portlet.getPortletId());
754    
755                                            portletIds.put(
756                                                    key,
757                                                    new Object[] {
758                                                            portlet.getPortletId(), layout.getPlid(),
759                                                            layout.getScopeGroup().getGroupId(),
760                                                            StringPool.BLANK, layout.getUuid()
761                                                    });
762                                    }
763                            }
764    
765                            LayoutTypePortlet layoutTypePortlet =
766                                    (LayoutTypePortlet)layout.getLayoutType();
767    
768                            for (String portletId : layoutTypePortlet.getPortletIds()) {
769                                    javax.portlet.PortletPreferences jxPreferences =
770                                            PortletPreferencesFactoryUtil.getLayoutPortletSetup(
771                                                    layout, portletId);
772    
773                                    String scopeType = GetterUtil.getString(
774                                            jxPreferences.getValue("lfrScopeType", null));
775                                    String scopeLayoutUuid = GetterUtil.getString(
776                                            jxPreferences.getValue("lfrScopeLayoutUuid", null));
777    
778                                    long scopeGroupId = portletDataContext.getScopeGroupId();
779    
780                                    if (Validator.isNotNull(scopeType)) {
781                                            Group scopeGroup = null;
782    
783                                            if (scopeType.equals("company")) {
784                                                    scopeGroup = GroupLocalServiceUtil.getCompanyGroup(
785                                                            layout.getCompanyId());
786                                            }
787                                            else if (scopeType.equals("layout")) {
788                                                    Layout scopeLayout = null;
789    
790                                                    scopeLayout = LayoutLocalServiceUtil.
791                                                            fetchLayoutByUuidAndGroupId(
792                                                                    scopeLayoutUuid,
793                                                                    portletDataContext.getGroupId());
794    
795                                                    if (scopeLayout == null) {
796                                                            continue;
797                                                    }
798    
799                                                    scopeGroup = scopeLayout.getScopeGroup();
800                                            }
801                                            else {
802                                                    throw new IllegalArgumentException(
803                                                            "Scope type " + scopeType + " is invalid");
804                                            }
805    
806                                            if (scopeGroup != null) {
807                                                    scopeGroupId = scopeGroup.getGroupId();
808                                            }
809                                    }
810    
811                                    String key = PortletPermissionUtil.getPrimaryKey(
812                                            layout.getPlid(), portletId);
813    
814                                    portletIds.put(
815                                            key,
816                                            new Object[] {
817                                                    portletId, layout.getPlid(), scopeGroupId, scopeType,
818                                                    scopeLayoutUuid
819                                            }
820                                    );
821                            }
822                    }
823    
824                    fixTypeSettings(layout);
825    
826                    layoutElement.addAttribute("path", path);
827    
828                    portletDataContext.addExpando(layoutElement, path, layout);
829    
830                    portletDataContext.addZipEntry(path, layout);
831            }
832    
833            protected void exportTheme(LayoutSet layoutSet, ZipWriter zipWriter)
834                    throws Exception {
835    
836                    Theme theme = layoutSet.getTheme();
837    
838                    String lookAndFeelXML = ContentUtil.get(
839                            "com/liferay/portal/dependencies/liferay-look-and-feel.xml.tmpl");
840    
841                    lookAndFeelXML = StringUtil.replace(
842                            lookAndFeelXML,
843                            new String[] {
844                                    "[$TEMPLATE_EXTENSION$]", "[$VIRTUAL_PATH$]"
845                            },
846                            new String[] {
847                                    theme.getTemplateExtension(), theme.getVirtualPath()
848                            }
849                    );
850    
851                    String servletContextName = theme.getServletContextName();
852    
853                    ServletContext servletContext = ServletContextPool.get(
854                            servletContextName);
855    
856                    if (servletContext == null) {
857                            if (_log.isWarnEnabled()) {
858                                    _log.warn(
859                                            "Servlet context not found for theme " +
860                                                    theme.getThemeId());
861                            }
862    
863                            return;
864                    }
865    
866                    File themeZip = new File(zipWriter.getPath() + "/theme.zip");
867    
868                    ZipWriter themeZipWriter = ZipWriterFactoryUtil.getZipWriter(themeZip);
869    
870                    themeZipWriter.addEntry("liferay-look-and-feel.xml", lookAndFeelXML);
871    
872                    File cssPath = null;
873                    File imagesPath = null;
874                    File javaScriptPath = null;
875                    File templatesPath = null;
876    
877                    if (!theme.isLoadFromServletContext()) {
878                            ThemeLoader themeLoader = ThemeLoaderFactory.getThemeLoader(
879                                    servletContextName);
880    
881                            if (themeLoader == null) {
882                                    _log.error(
883                                            servletContextName + " does not map to a theme loader");
884                            }
885                            else {
886                                    String realPath =
887                                            themeLoader.getFileStorage().getPath() + StringPool.SLASH +
888                                                    theme.getName();
889    
890                                    cssPath = new File(realPath + "/css");
891                                    imagesPath = new File(realPath + "/images");
892                                    javaScriptPath = new File(realPath + "/javascript");
893                                    templatesPath = new File(realPath + "/templates");
894                            }
895                    }
896                    else {
897                            cssPath = new File(servletContext.getRealPath(theme.getCssPath()));
898                            imagesPath = new File(
899                                    servletContext.getRealPath(theme.getImagesPath()));
900                            javaScriptPath = new File(
901                                    servletContext.getRealPath(theme.getJavaScriptPath()));
902                            templatesPath = new File(
903                                    servletContext.getRealPath(theme.getTemplatesPath()));
904                    }
905    
906                    exportThemeFiles("css", cssPath, themeZipWriter);
907                    exportThemeFiles("images", imagesPath, themeZipWriter);
908                    exportThemeFiles("javascript", javaScriptPath, themeZipWriter);
909                    exportThemeFiles("templates", templatesPath, themeZipWriter);
910            }
911    
912            protected void exportThemeFiles(String path, File dir, ZipWriter zipWriter)
913                    throws Exception {
914    
915                    if ((dir == null) || (!dir.exists())) {
916                            return;
917                    }
918    
919                    File[] files = dir.listFiles();
920    
921                    for (File file : files) {
922                            if (file.isDirectory()) {
923                                    exportThemeFiles(
924                                            path + StringPool.SLASH + file.getName(), file, zipWriter);
925                            }
926                            else {
927                                    zipWriter.addEntry(
928                                            path + StringPool.SLASH + file.getName(),
929                                            FileUtil.getBytes(file));
930                            }
931                    }
932            }
933    
934            protected void fixTypeSettings(Layout layout)
935                    throws Exception {
936    
937                    if (!layout.isTypeURL()) {
938                            return;
939                    }
940    
941                    UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
942    
943                    String url = GetterUtil.getString(typeSettings.getProperty("url"));
944    
945                    String friendlyURLPrivateGroupPath =
946                            PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
947                    String friendlyURLPrivateUserPath =
948                            PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
949                    String friendlyURLPublicPath =
950                            PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
951    
952                    if (!url.startsWith(friendlyURLPrivateGroupPath) &&
953                            !url.startsWith(friendlyURLPrivateUserPath) &&
954                            !url.startsWith(friendlyURLPublicPath)) {
955    
956                            return;
957                    }
958    
959                    int x = url.indexOf(CharPool.SLASH, 1);
960    
961                    if (x == -1) {
962                            return;
963                    }
964    
965                    int y = url.indexOf(CharPool.SLASH, x + 1);
966    
967                    if (y == -1) {
968                            return;
969                    }
970    
971                    String friendlyURL = url.substring(x, y);
972                    String groupFriendlyURL = layout.getGroup().getFriendlyURL();
973    
974                    if (!friendlyURL.equals(groupFriendlyURL)) {
975                            return;
976                    }
977    
978                    typeSettings.setProperty(
979                            "url",
980                            url.substring(0, x) + SAME_GROUP_FRIENDLY_URL + url.substring(y));
981            }
982    
983            protected boolean[] getExportPortletControls(
984                            long companyId, String portletId,
985                            PortletDataContext portletDataContext,
986                            Map<String, String[]> parameterMap)
987                    throws Exception {
988    
989                    boolean exportPortletData = MapUtil.getBoolean(
990                            parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
991                    boolean exportPortletDataAll = MapUtil.getBoolean(
992                            parameterMap, PortletDataHandlerKeys.PORTLET_DATA_ALL);
993                    boolean exportPortletSetup = MapUtil.getBoolean(
994                            parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
995                    boolean exportPortletSetupAll = MapUtil.getBoolean(
996                            parameterMap, PortletDataHandlerKeys.PORTLET_SETUP_ALL);
997    
998                    if (_log.isDebugEnabled()) {
999                            _log.debug("Export portlet data " + exportPortletData);
1000                            _log.debug("Export all portlet data " + exportPortletDataAll);
1001                            _log.debug("Export portlet setup " + exportPortletSetup);
1002                    }
1003    
1004                    boolean exportCurPortletData = exportPortletData;
1005                    boolean exportCurPortletSetup = exportPortletSetup;
1006    
1007                    // If PORTLET_DATA_ALL is true, this means that staging has just been
1008                    // activated and all data and setup must be exported. There is no
1009                    // portlet export control to check in this case.
1010    
1011                    if (exportPortletDataAll) {
1012                            exportCurPortletData = true;
1013                            exportCurPortletSetup = true;
1014                    }
1015                    else {
1016                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
1017                                    companyId, portletId);
1018    
1019                            if (portlet != null) {
1020                                    String portletDataHandlerClass =
1021                                            portlet.getPortletDataHandlerClass();
1022    
1023                                    // Checking if the portlet has a data handler, if it doesn't,
1024                                    // the default values are the ones set in PORTLET_DATA and
1025                                    // PORTLET_SETUP. If it has a data handler, iterate over each
1026                                    // portlet export control.
1027    
1028                                    if (portletDataHandlerClass != null) {
1029                                            String rootPortletId = PortletConstants.getRootPortletId(
1030                                                    portletId);
1031    
1032                                            // PORTLET_DATA and the PORTLET_DATA for this specific
1033                                            // data handler must be true
1034    
1035                                            exportCurPortletData =
1036                                                    exportPortletData &&
1037                                                    MapUtil.getBoolean(
1038                                                            parameterMap,
1039                                                            PortletDataHandlerKeys.PORTLET_DATA +
1040                                                                    StringPool.UNDERLINE + rootPortletId);
1041    
1042                                            // PORTLET_SETUP and the PORTLET_SETUP for this specific
1043                                            // data handler must be true
1044    
1045                                            exportCurPortletSetup =
1046                                                    exportPortletSetup &&
1047                                                    MapUtil.getBoolean(
1048                                                            parameterMap,
1049                                                            PortletDataHandlerKeys.PORTLET_SETUP +
1050                                                                    StringPool.UNDERLINE + rootPortletId);
1051                                    }
1052                            }
1053                    }
1054    
1055                    if (exportPortletSetupAll) {
1056                            exportCurPortletSetup = true;
1057                    }
1058    
1059                    return new boolean[] {exportCurPortletData, exportCurPortletSetup};
1060            }
1061    
1062            protected String getLayoutIconPath(
1063                    PortletDataContext portletDataContext, Layout layout, Image image) {
1064    
1065                    StringBundler sb = new StringBundler(5);
1066    
1067                    sb.append(portletDataContext.getLayoutPath(layout.getLayoutId()));
1068                    sb.append("/icons/");
1069                    sb.append(image.getImageId());
1070                    sb.append(StringPool.PERIOD);
1071                    sb.append(image.getType());
1072    
1073                    return sb.toString();
1074            }
1075    
1076            protected String getLayoutSetPrototype(
1077                    PortletDataContext portletDataContext, String layoutSetPrototypeUuid) {
1078    
1079                    StringBundler sb = new StringBundler(3);
1080    
1081                    sb.append(portletDataContext.getRootPath());
1082                    sb.append("/layout-set-prototype/");
1083                    sb.append(layoutSetPrototypeUuid);
1084    
1085                    return sb.toString();
1086            }
1087    
1088            private static Log _log = LogFactoryUtil.getLog(LayoutExporter.class);
1089    
1090            private PermissionExporter _permissionExporter = new PermissionExporter();
1091            private PortletExporter _portletExporter = new PortletExporter();
1092    
1093    }