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