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