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