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.ImportExportThreadLocal;
020    import com.liferay.portal.kernel.lar.PortletDataContext;
021    import com.liferay.portal.kernel.lar.PortletDataHandler;
022    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.servlet.ServletContextPool;
026    import com.liferay.portal.kernel.staging.LayoutStagingUtil;
027    import com.liferay.portal.kernel.util.CharPool;
028    import com.liferay.portal.kernel.util.FileUtil;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.LocaleUtil;
031    import com.liferay.portal.kernel.util.MapUtil;
032    import com.liferay.portal.kernel.util.ParamUtil;
033    import com.liferay.portal.kernel.util.ReleaseInfo;
034    import com.liferay.portal.kernel.util.StringBundler;
035    import com.liferay.portal.kernel.util.StringPool;
036    import com.liferay.portal.kernel.util.StringUtil;
037    import com.liferay.portal.kernel.util.Time;
038    import com.liferay.portal.kernel.util.UnicodeProperties;
039    import com.liferay.portal.kernel.util.Validator;
040    import com.liferay.portal.kernel.workflow.WorkflowConstants;
041    import com.liferay.portal.kernel.xml.Document;
042    import com.liferay.portal.kernel.xml.Element;
043    import com.liferay.portal.kernel.xml.SAXReaderUtil;
044    import com.liferay.portal.kernel.zip.ZipWriter;
045    import com.liferay.portal.kernel.zip.ZipWriterFactoryUtil;
046    import com.liferay.portal.model.Group;
047    import com.liferay.portal.model.Image;
048    import com.liferay.portal.model.Layout;
049    import com.liferay.portal.model.LayoutConstants;
050    import com.liferay.portal.model.LayoutPrototype;
051    import com.liferay.portal.model.LayoutRevision;
052    import com.liferay.portal.model.LayoutSet;
053    import com.liferay.portal.model.LayoutSetPrototype;
054    import com.liferay.portal.model.LayoutStagingHandler;
055    import com.liferay.portal.model.LayoutTypePortlet;
056    import com.liferay.portal.model.Portlet;
057    import com.liferay.portal.model.PortletConstants;
058    import com.liferay.portal.model.Theme;
059    import com.liferay.portal.model.impl.LayoutImpl;
060    import com.liferay.portal.service.GroupLocalServiceUtil;
061    import com.liferay.portal.service.ImageLocalServiceUtil;
062    import com.liferay.portal.service.LayoutLocalServiceUtil;
063    import com.liferay.portal.service.LayoutPrototypeLocalServiceUtil;
064    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
065    import com.liferay.portal.service.LayoutSetPrototypeLocalServiceUtil;
066    import com.liferay.portal.service.PortletLocalServiceUtil;
067    import com.liferay.portal.service.ServiceContext;
068    import com.liferay.portal.service.ServiceContextThreadLocal;
069    import com.liferay.portal.service.UserLocalServiceUtil;
070    import com.liferay.portal.service.permission.PortletPermissionUtil;
071    import com.liferay.portal.service.persistence.LayoutRevisionUtil;
072    import com.liferay.portal.theme.ThemeLoader;
073    import com.liferay.portal.theme.ThemeLoaderFactory;
074    import com.liferay.portal.util.PortletKeys;
075    import com.liferay.portal.util.PropsValues;
076    import com.liferay.portlet.PortletPreferencesFactoryUtil;
077    import com.liferay.portlet.asset.model.AssetCategory;
078    import com.liferay.portlet.asset.model.AssetVocabulary;
079    import com.liferay.portlet.asset.service.AssetVocabularyLocalServiceUtil;
080    import com.liferay.portlet.asset.service.persistence.AssetCategoryUtil;
081    import com.liferay.portlet.journal.NoSuchArticleException;
082    import com.liferay.portlet.journal.lar.JournalPortletDataHandlerImpl;
083    import com.liferay.portlet.journal.model.JournalArticle;
084    import com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil;
085    import com.liferay.util.ContentUtil;
086    
087    import java.io.File;
088    
089    import java.util.Date;
090    import java.util.HashSet;
091    import java.util.Iterator;
092    import java.util.LinkedHashMap;
093    import java.util.List;
094    import java.util.Map;
095    
096    import javax.servlet.ServletContext;
097    
098    import org.apache.commons.lang.time.StopWatch;
099    
100    /**
101     * @author Brian Wing Shun Chan
102     * @author Joel Kozikowski
103     * @author Charles May
104     * @author Raymond Aug??
105     * @author Jorge Ferrer
106     * @author Bruno Farache
107     * @author Karthik Sudarshan
108     * @author Zsigmond Rab
109     * @author Douglas Wong
110     * @author Mate Thurzo
111     */
112    public class LayoutExporter {
113    
114            public static final String SAME_GROUP_FRIENDLY_URL =
115                    "/[$SAME_GROUP_FRIENDLY_URL$]";
116    
117            public static List<Portlet> getAlwaysExportablePortlets(long companyId)
118                    throws Exception {
119    
120                    List<Portlet> portlets = PortletLocalServiceUtil.getPortlets(companyId);
121    
122                    Iterator<Portlet> itr = portlets.iterator();
123    
124                    while (itr.hasNext()) {
125                            Portlet portlet = itr.next();
126    
127                            if (!portlet.isActive()) {
128                                    itr.remove();
129    
130                                    continue;
131                            }
132    
133                            PortletDataHandler portletDataHandler =
134                                    portlet.getPortletDataHandlerInstance();
135    
136                            if ((portletDataHandler == null) ||
137                                    !portletDataHandler.isAlwaysExportable()) {
138    
139                                    itr.remove();
140                            }
141                    }
142    
143                    return portlets;
144            }
145    
146            public static void updateLastPublishDate(
147                            LayoutSet layoutSet, long lastPublishDate)
148                    throws Exception {
149    
150                    UnicodeProperties settingsProperties =
151                            layoutSet.getSettingsProperties();
152    
153                    if (lastPublishDate <= 0) {
154                            settingsProperties.remove("last-publish-date");
155                    }
156                    else {
157                            settingsProperties.setProperty(
158                                    "last-publish-date", String.valueOf(lastPublishDate));
159                    }
160    
161                    LayoutSetLocalServiceUtil.updateSettings(
162                            layoutSet.getGroupId(), layoutSet.isPrivateLayout(),
163                            settingsProperties.toString());
164            }
165    
166            public byte[] exportLayouts(
167                            long groupId, boolean privateLayout, long[] layoutIds,
168                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
169                    throws Exception {
170    
171                    File file = exportLayoutsAsFile(
172                            groupId, privateLayout, layoutIds, parameterMap, startDate,
173                            endDate);
174    
175                    try {
176                            return FileUtil.getBytes(file);
177                    }
178                    finally {
179                            file.delete();
180                    }
181            }
182    
183            public File exportLayoutsAsFile(
184                            long groupId, boolean privateLayout, long[] layoutIds,
185                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
186                    throws Exception {
187    
188                    try {
189                            ImportExportThreadLocal.setLayoutExportInProcess(true);
190    
191                            return doExportLayoutsAsFile(
192                                    groupId, privateLayout, layoutIds, parameterMap, startDate,
193                                    endDate);
194                    }
195                    finally {
196                            ImportExportThreadLocal.setLayoutExportInProcess(false);
197                    }
198            }
199    
200            protected File doExportLayoutsAsFile(
201                            long groupId, boolean privateLayout, long[] layoutIds,
202                            Map<String, String[]> parameterMap, Date startDate, Date endDate)
203                    throws Exception {
204    
205                    boolean exportCategories = MapUtil.getBoolean(
206                            parameterMap, PortletDataHandlerKeys.CATEGORIES);
207                    boolean exportIgnoreLastPublishDate = MapUtil.getBoolean(
208                            parameterMap, PortletDataHandlerKeys.IGNORE_LAST_PUBLISH_DATE);
209                    boolean exportPermissions = MapUtil.getBoolean(
210                            parameterMap, PortletDataHandlerKeys.PERMISSIONS);
211                    boolean exportUserPermissions = MapUtil.getBoolean(
212                            parameterMap, PortletDataHandlerKeys.USER_PERMISSIONS);
213                    boolean exportPortletArchivedSetups = MapUtil.getBoolean(
214                            parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
215                    boolean exportPortletUserPreferences = MapUtil.getBoolean(
216                            parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
217                    boolean exportTheme = MapUtil.getBoolean(
218                            parameterMap, PortletDataHandlerKeys.THEME);
219                    boolean exportThemeSettings = MapUtil.getBoolean(
220                            parameterMap, PortletDataHandlerKeys.THEME_REFERENCE);
221                    boolean exportLogo = MapUtil.getBoolean(
222                            parameterMap, PortletDataHandlerKeys.LOGO);
223                    boolean exportLayoutSetSettings = MapUtil.getBoolean(
224                            parameterMap, PortletDataHandlerKeys.LAYOUT_SET_SETTINGS);
225                    //boolean publishToRemote = MapUtil.getBoolean(
226                    //        parameterMap, PortletDataHandlerKeys.PUBLISH_TO_REMOTE);
227                    boolean updateLastPublishDate = MapUtil.getBoolean(
228                            parameterMap, PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE);
229    
230                    if (_log.isDebugEnabled()) {
231                            _log.debug("Export categories " + exportCategories);
232                            _log.debug("Export permissions " + exportPermissions);
233                            _log.debug("Export user permissions " + exportUserPermissions);
234                            _log.debug(
235                                    "Export portlet archived setups " +
236                                            exportPortletArchivedSetups);
237                            _log.debug(
238                                    "Export portlet user preferences " +
239                                            exportPortletUserPreferences);
240                            _log.debug("Export theme " + exportTheme);
241                    }
242    
243                    LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
244                            groupId, privateLayout);
245    
246                    long companyId = layoutSet.getCompanyId();
247                    long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
248    
249                    ServiceContext serviceContext =
250                            ServiceContextThreadLocal.getServiceContext();
251    
252                    if (serviceContext == null) {
253                            serviceContext = new ServiceContext();
254    
255                            serviceContext.setCompanyId(companyId);
256                            serviceContext.setSignedIn(false);
257                            serviceContext.setUserId(defaultUserId);
258    
259                            ServiceContextThreadLocal.pushServiceContext(serviceContext);
260                    }
261    
262                    serviceContext.setAttribute("exporting", Boolean.TRUE);
263    
264                    long layoutSetBranchId = MapUtil.getLong(
265                            parameterMap, "layoutSetBranchId");
266    
267                    serviceContext.setAttribute("layoutSetBranchId", layoutSetBranchId);
268    
269                    long lastPublishDate = System.currentTimeMillis();
270    
271                    if (endDate != null) {
272                            lastPublishDate = endDate.getTime();
273                    }
274    
275                    if (exportIgnoreLastPublishDate) {
276                            endDate = null;
277                            startDate = null;
278                    }
279    
280                    StopWatch stopWatch = null;
281    
282                    if (_log.isInfoEnabled()) {
283                            stopWatch = new StopWatch();
284    
285                            stopWatch.start();
286                    }
287    
288                    LayoutCache layoutCache = new LayoutCache();
289    
290                    ZipWriter zipWriter = ZipWriterFactoryUtil.getZipWriter();
291    
292                    PortletDataContext portletDataContext = new PortletDataContextImpl(
293                            companyId, groupId, parameterMap, new HashSet<String>(), startDate,
294                            endDate, zipWriter);
295    
296                    portletDataContext.setPortetDataContextListener(
297                            new PortletDataContextListenerImpl(portletDataContext));
298    
299                    Document document = SAXReaderUtil.createDocument();
300    
301                    Element rootElement = document.addElement("root");
302    
303                    Element headerElement = rootElement.addElement("header");
304    
305                    headerElement.addAttribute(
306                            "available-locales",
307                            StringUtil.merge(LanguageUtil.getAvailableLocales()));
308                    headerElement.addAttribute(
309                            "build-number", String.valueOf(ReleaseInfo.getBuildNumber()));
310                    headerElement.addAttribute("export-date", Time.getRFC822());
311    
312                    if (portletDataContext.hasDateRange()) {
313                            headerElement.addAttribute(
314                                    "start-date",
315                                    String.valueOf(portletDataContext.getStartDate()));
316                            headerElement.addAttribute(
317                                    "end-date", String.valueOf(portletDataContext.getEndDate()));
318                    }
319    
320                    Group companyGroup = GroupLocalServiceUtil.getCompanyGroup(companyId);
321    
322                    headerElement.addAttribute(
323                            "company-group-id", String.valueOf(companyGroup.getGroupId()));
324    
325                    headerElement.addAttribute("group-id", String.valueOf(groupId));
326                    headerElement.addAttribute(
327                            "private-layout", String.valueOf(privateLayout));
328    
329                    Group group = layoutSet.getGroup();
330    
331                    String type = "layout-set";
332    
333                    if (group.isLayoutPrototype()) {
334                            type = "layout-prototype";
335    
336                            LayoutPrototype layoutPrototype =
337                                    LayoutPrototypeLocalServiceUtil.getLayoutPrototype(
338                                            group.getClassPK());
339    
340                            headerElement.addAttribute("type-uuid", layoutPrototype.getUuid());
341                    }
342                    else if (group.isLayoutSetPrototype()) {
343                            type ="layout-set-prototype";
344    
345                            LayoutSetPrototype layoutSetPrototype =
346                                    LayoutSetPrototypeLocalServiceUtil.getLayoutSetPrototype(
347                                            group.getClassPK());
348    
349                            headerElement.addAttribute(
350                                    "type-uuid", layoutSetPrototype.getUuid());
351                    }
352    
353                    headerElement.addAttribute("type", type);
354    
355                    if (exportTheme || exportThemeSettings) {
356                            headerElement.addAttribute("theme-id", layoutSet.getThemeId());
357                            headerElement.addAttribute(
358                                    "color-scheme-id", layoutSet.getColorSchemeId());
359                    }
360    
361                    if (exportLogo) {
362                            Image image = ImageLocalServiceUtil.getImage(layoutSet.getLogoId());
363    
364                            if ((image != null) && (image.getTextObj() != null)) {
365                                    String logoPath = getLayoutSetLogoPath(portletDataContext);
366    
367                                    headerElement.addAttribute("logo-path", logoPath);
368    
369                                    portletDataContext.addZipEntry(logoPath, image.getTextObj());
370                            }
371                    }
372    
373                    if (exportLayoutSetSettings) {
374                            Element settingsElement = headerElement.addElement("settings");
375    
376                            settingsElement.addCDATA(layoutSet.getSettings());
377                    }
378    
379                    Element cssElement = headerElement.addElement("css");
380    
381                    cssElement.addCDATA(layoutSet.getCss());
382    
383                    Portlet layoutConfigurationPortlet =
384                            PortletLocalServiceUtil.getPortletById(
385                                    portletDataContext.getCompanyId(),
386                                    PortletKeys.LAYOUT_CONFIGURATION);
387    
388                    Map<String, Object[]> portletIds =
389                            new LinkedHashMap<String, Object[]>();
390    
391                    List<Layout> layouts = null;
392    
393                    if ((layoutIds == null) || (layoutIds.length == 0)) {
394                            layouts = LayoutLocalServiceUtil.getLayouts(groupId, privateLayout);
395                    }
396                    else {
397                            layouts = LayoutLocalServiceUtil.getLayouts(
398                                    groupId, privateLayout, layoutIds);
399                    }
400    
401                    List<Portlet> portlets = getAlwaysExportablePortlets(companyId);
402    
403                    long plid = LayoutConstants.DEFAULT_PLID;
404    
405                    if (!layouts.isEmpty()) {
406                            Layout firstLayout = layouts.get(0);
407    
408                            plid = firstLayout.getPlid();
409                    }
410    
411                    if (group.isStagingGroup()) {
412                            group = group.getLiveGroup();
413                    }
414    
415                    for (Portlet portlet : portlets) {
416                            String portletId = portlet.getRootPortletId();
417    
418                            if (!group.isStagedPortlet(portletId)) {
419                                    continue;
420                            }
421    
422                            String key = PortletPermissionUtil.getPrimaryKey(0, portletId);
423    
424                            if (portletIds.get(key) == null) {
425                                    portletIds.put(
426                                            key,
427                                            new Object[] {
428                                                    portletId, plid, groupId, StringPool.BLANK,
429                                                    StringPool.BLANK
430                                            });
431                            }
432                    }
433    
434                    Element layoutsElement = rootElement.addElement("layouts");
435    
436                    String layoutSetPrototypeUuid = layoutSet.getLayoutSetPrototypeUuid();
437    
438                    if (Validator.isNotNull(layoutSetPrototypeUuid)) {
439                            LayoutSetPrototype layoutSetPrototype =
440                                    LayoutSetPrototypeLocalServiceUtil.getLayoutSetPrototypeByUuid(
441                                            layoutSetPrototypeUuid);
442    
443                            layoutsElement.addAttribute(
444                                    "layout-set-prototype-uuid", layoutSetPrototypeUuid);
445    
446                            layoutsElement.addAttribute(
447                                    "layout-set-prototype-name",
448                                    layoutSetPrototype.getName(LocaleUtil.getDefault()));
449                    }
450    
451                    for (Layout layout : layouts) {
452                            exportLayout(
453                                    portletDataContext, layoutConfigurationPortlet, layoutCache,
454                                    portlets, portletIds, exportPermissions, exportUserPermissions,
455                                    layout, layoutsElement);
456                    }
457    
458                    if (PropsValues.PERMISSIONS_USER_CHECK_ALGORITHM < 5) {
459                            Element rolesElement = rootElement.addElement("roles");
460    
461                            if (exportPermissions) {
462                                    _permissionExporter.exportLayoutRoles(
463                                            layoutCache, companyId, groupId, rolesElement);
464                            }
465                    }
466    
467                    long previousScopeGroupId = portletDataContext.getScopeGroupId();
468    
469                    Element portletsElement = rootElement.addElement("portlets");
470    
471                    for (Map.Entry<String, Object[]> portletIdsEntry :
472                                    portletIds.entrySet()) {
473    
474                            Object[] portletObjects = portletIdsEntry.getValue();
475    
476                            String portletId = null;
477                            plid = LayoutConstants.DEFAULT_PLID;
478                            long scopeGroupId = 0;
479                            String scopeType = StringPool.BLANK;
480                            String scopeLayoutUuid = null;
481    
482                            if (portletObjects.length == 4) {
483                                    portletId = (String)portletIdsEntry.getValue()[0];
484                                    plid = (Long)portletIdsEntry.getValue()[1];
485                                    scopeGroupId = (Long)portletIdsEntry.getValue()[2];
486                                    scopeLayoutUuid = (String)portletIdsEntry.getValue()[3];
487                            }
488                            else {
489                                    portletId = (String)portletIdsEntry.getValue()[0];
490                                    plid = (Long)portletIdsEntry.getValue()[1];
491                                    scopeGroupId = (Long)portletIdsEntry.getValue()[2];
492                                    scopeType = (String)portletIdsEntry.getValue()[3];
493                                    scopeLayoutUuid = (String)portletIdsEntry.getValue()[4];
494                            }
495    
496                            Layout layout = LayoutLocalServiceUtil.fetchLayout(plid);
497    
498                            if (layout == null) {
499                                    if (!group.isCompany() &&
500                                            (plid <= LayoutConstants.DEFAULT_PLID)) {
501    
502                                            continue;
503                                    }
504    
505                                    if (_log.isWarnEnabled()) {
506                                            _log.warn(
507                                                    "Assuming global scope because no layout was found");
508                                    }
509    
510                                    layout = new LayoutImpl();
511    
512                                    layout.setGroupId(groupId);
513                                    layout.setCompanyId(companyId);
514                            }
515    
516                            portletDataContext.setPlid(plid);
517                            portletDataContext.setOldPlid(plid);
518                            portletDataContext.setScopeGroupId(scopeGroupId);
519                            portletDataContext.setScopeType(scopeType);
520                            portletDataContext.setScopeLayoutUuid(scopeLayoutUuid);
521    
522                            boolean[] exportPortletControls = getExportPortletControls(
523                                    companyId, portletId, portletDataContext, parameterMap, type);
524    
525                            _portletExporter.exportPortlet(
526                                    portletDataContext, layoutCache, portletId, layout,
527                                    portletsElement, defaultUserId, exportPermissions,
528                                    exportPortletArchivedSetups, exportPortletControls[0],
529                                    exportPortletControls[1], exportPortletUserPreferences,
530                                    exportUserPermissions);
531                    }
532    
533                    portletDataContext.setScopeGroupId(previousScopeGroupId);
534    
535                    if (exportCategories || group.isCompany()) {
536                            exportAssetCategories(portletDataContext);
537                    }
538    
539                    _portletExporter.exportAssetLinks(portletDataContext);
540                    _portletExporter.exportAssetTags(portletDataContext);
541                    _portletExporter.exportComments(portletDataContext);
542                    _portletExporter.exportExpandoTables(portletDataContext);
543                    _portletExporter.exportLocks(portletDataContext);
544    
545                    if (exportPermissions) {
546                            _permissionExporter.exportPortletDataPermissions(
547                                    portletDataContext);
548                    }
549    
550                    _portletExporter.exportRatingsEntries(portletDataContext, rootElement);
551    
552                    if (exportTheme && !portletDataContext.isPerformDirectBinaryImport()) {
553                            exportTheme(layoutSet, zipWriter);
554                    }
555    
556                    if (_log.isInfoEnabled()) {
557                            if (stopWatch != null) {
558                                    _log.info(
559                                            "Exporting layouts takes " + stopWatch.getTime() + " ms");
560                            }
561                            else {
562                                    _log.info("Exporting layouts is finished");
563                            }
564                    }
565    
566                    portletDataContext.addZipEntry(
567                            "/manifest.xml", document.formattedString());
568    
569                    try {
570                            return zipWriter.getFile();
571                    }
572                    finally {
573                            if (updateLastPublishDate) {
574                                    updateLastPublishDate(layoutSet, lastPublishDate);
575                            }
576                    }
577            }
578    
579            protected void exportAssetCategories(PortletDataContext portletDataContext)
580                    throws Exception {
581    
582                    Document document = SAXReaderUtil.createDocument();
583    
584                    Element rootElement = document.addElement("categories-hierarchy");
585    
586                    Element assetVocabulariesElement = rootElement.addElement(
587                            "vocabularies");
588    
589                    List<AssetVocabulary> assetVocabularies =
590                            AssetVocabularyLocalServiceUtil.getGroupVocabularies(
591                                    portletDataContext.getGroupId());
592    
593                    for (AssetVocabulary assetVocabulary : assetVocabularies) {
594                            _portletExporter.exportAssetVocabulary(
595                                    portletDataContext, assetVocabulariesElement, assetVocabulary);
596                    }
597    
598                    Element categoriesElement = rootElement.addElement("categories");
599    
600                    List<AssetCategory> assetCategories = AssetCategoryUtil.findByGroupId(
601                            portletDataContext.getGroupId());
602    
603                    for (AssetCategory assetCategory : assetCategories) {
604                            _portletExporter.exportAssetCategory(
605                                    portletDataContext, assetVocabulariesElement, categoriesElement,
606                                    assetCategory);
607                    }
608    
609                    _portletExporter.exportAssetCategories(portletDataContext, rootElement);
610    
611                    portletDataContext.addZipEntry(
612                            portletDataContext.getRootPath() + "/categories-hierarchy.xml",
613                            document.formattedString());
614            }
615    
616            protected void exportJournalArticle(
617                            PortletDataContext portletDataContext, Layout layout,
618                            Element layoutElement)
619                    throws Exception {
620    
621                    UnicodeProperties typeSettingsProperties =
622                            layout.getTypeSettingsProperties();
623    
624                    String articleId = typeSettingsProperties.getProperty(
625                            "article-id", StringPool.BLANK);
626    
627                    long articleGroupId = layout.getGroupId();
628    
629                    if (Validator.isNull(articleId)) {
630                            if (_log.isWarnEnabled()) {
631                                    _log.warn(
632                                            "No article id found in typeSettings of layout " +
633                                                    layout.getPlid());
634                            }
635                    }
636    
637                    JournalArticle article = null;
638    
639                    try {
640                            article = JournalArticleLocalServiceUtil.getLatestArticle(
641                                    articleGroupId, articleId, WorkflowConstants.STATUS_APPROVED);
642                    }
643                    catch (NoSuchArticleException nsae) {
644                            if (_log.isWarnEnabled()) {
645                                    _log.warn(
646                                            "No approved article found with group id " +
647                                                    articleGroupId + " and article id " + articleId);
648                            }
649                    }
650    
651                    if (article == null) {
652                            return;
653                    }
654    
655                    String path = JournalPortletDataHandlerImpl.getArticlePath(
656                            portletDataContext, article);
657    
658                    Element articleElement = layoutElement.addElement("article");
659    
660                    articleElement.addAttribute("path", path);
661    
662                    Element dlFileEntryTypesElement = layoutElement.addElement(
663                            "dl-file-entry-types");
664                    Element dlFoldersElement = layoutElement.addElement("dl-folders");
665                    Element dlFilesElement = layoutElement.addElement("dl-file-entries");
666                    Element dlFileRanksElement = layoutElement.addElement("dl-file-ranks");
667                    Element dlRepositoriesElement = layoutElement.addElement(
668                            "dl-repositories");
669                    Element dlRepositoryEntriesElement = layoutElement.addElement(
670                            "dl-repository-entries");
671    
672                    JournalPortletDataHandlerImpl.exportArticle(
673                            portletDataContext, layoutElement, layoutElement, layoutElement,
674                            dlFileEntryTypesElement, dlFoldersElement, dlFilesElement,
675                            dlFileRanksElement, dlRepositoriesElement,
676                            dlRepositoryEntriesElement, article, false);
677            }
678    
679            protected void exportLayout(
680                            PortletDataContext portletDataContext,
681                            Portlet layoutConfigurationPortlet, LayoutCache layoutCache,
682                            List<Portlet> portlets, Map<String, Object[]> portletIds,
683                            boolean exportPermissions, boolean exportUserPermissions,
684                            Layout layout, Element layoutsElement)
685                    throws Exception {
686    
687                    String path = portletDataContext.getLayoutPath(
688                            layout.getLayoutId()) + "/layout.xml";
689    
690                    if (!portletDataContext.isPathNotProcessed(path)) {
691                            return;
692                    }
693    
694                    LayoutRevision layoutRevision = null;
695    
696                    ServiceContext serviceContext =
697                            ServiceContextThreadLocal.getServiceContext();
698    
699                    boolean exportLAR = ParamUtil.getBoolean(serviceContext, "exportLAR");
700    
701                    if (!exportLAR && LayoutStagingUtil.isBranchingLayout(layout) &&
702                            !layout.isTypeURL()) {
703    
704                            long layoutSetBranchId = ParamUtil.getLong(
705                                    serviceContext, "layoutSetBranchId");
706    
707                            if (layoutSetBranchId <= 0) {
708                                    return;
709                            }
710    
711                            layoutRevision = LayoutRevisionUtil.fetchByL_H_P(
712                                    layoutSetBranchId, true, layout.getPlid());
713    
714                            if (layoutRevision == null) {
715                                    return;
716                            }
717    
718                            LayoutStagingHandler layoutStagingHandler =
719                                    LayoutStagingUtil.getLayoutStagingHandler(layout);
720    
721                            layoutStagingHandler.setLayoutRevision(layoutRevision);
722                    }
723    
724                    Element layoutElement = layoutsElement.addElement("layout");
725    
726                    if (layoutRevision != null) {
727                            layoutElement.addAttribute(
728                                    "layout-revision-id",
729                                    String.valueOf(layoutRevision.getLayoutRevisionId()));
730                            layoutElement.addAttribute(
731                                    "layout-branch-id",
732                                    String.valueOf(layoutRevision.getLayoutBranchId()));
733                            layoutElement.addAttribute(
734                                    "layout-branch-name",
735                                    String.valueOf(layoutRevision.getLayoutBranch().getName()));
736                    }
737    
738                    layoutElement.addAttribute("layout-uuid", layout.getUuid());
739                    layoutElement.addAttribute(
740                            "layout-id", String.valueOf(layout.getLayoutId()));
741    
742                    long parentLayoutId = layout.getParentLayoutId();
743    
744                    if (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
745                            Layout parentLayout = LayoutLocalServiceUtil.getLayout(
746                                    layout.getGroupId(), layout.isPrivateLayout(), parentLayoutId);
747    
748                            if (parentLayout != null) {
749                                    layoutElement.addAttribute(
750                                            "parent-layout-uuid", parentLayout.getUuid());
751                            }
752                    }
753    
754                    String layoutPrototypeUuid = layout.getLayoutPrototypeUuid();
755    
756                    if (Validator.isNotNull(layoutPrototypeUuid)) {
757                            LayoutPrototype layoutPrototype =
758                                    LayoutPrototypeLocalServiceUtil.getLayoutPrototypeByUuid(
759                                            layoutPrototypeUuid);
760    
761                            layoutElement.addAttribute(
762                                    "layout-prototype-uuid", layoutPrototypeUuid);
763                            layoutElement.addAttribute(
764                                    "layout-prototype-name",
765                                    layoutPrototype.getName(LocaleUtil.getDefault()));
766                    }
767    
768                    boolean deleteLayout = MapUtil.getBoolean(
769                            portletDataContext.getParameterMap(), "delete_" + layout.getPlid());
770    
771                    if (deleteLayout) {
772                            layoutElement.addAttribute("delete", String.valueOf(true));
773    
774                            return;
775                    }
776    
777                    portletDataContext.setPlid(layout.getPlid());
778    
779                    if (layout.isIconImage()) {
780                            Image image = ImageLocalServiceUtil.getImage(
781                                    layout.getIconImageId());
782    
783                            if (image != null) {
784                                    String iconPath = getLayoutIconPath(
785                                            portletDataContext, layout, image);
786    
787                                    layoutElement.addElement("icon-image-path").addText(iconPath);
788    
789                                    portletDataContext.addZipEntry(iconPath, image.getTextObj());
790                            }
791                    }
792    
793                    _portletExporter.exportPortletData(
794                            portletDataContext, layoutConfigurationPortlet, layout, null,
795                            layoutElement);
796    
797                    // Layout permissions
798    
799                    if (exportPermissions) {
800                            _permissionExporter.exportLayoutPermissions(
801                                    portletDataContext, layoutCache,
802                                    portletDataContext.getCompanyId(),
803                                    portletDataContext.getScopeGroupId(), layout, layoutElement,
804                                    exportUserPermissions);
805                    }
806    
807                    if (layout.isTypeArticle()) {
808                            exportJournalArticle(portletDataContext, layout, layoutElement);
809                    }
810                    else if (layout.isTypeLinkToLayout()) {
811                            UnicodeProperties typeSettingsProperties =
812                                    layout.getTypeSettingsProperties();
813    
814                            long linkToLayoutId = GetterUtil.getLong(
815                                    typeSettingsProperties.getProperty(
816                                            "linkToLayoutId", StringPool.BLANK));
817    
818                            if (linkToLayoutId > 0) {
819                                    try {
820                                            Layout linkedToLayout = LayoutLocalServiceUtil.getLayout(
821                                                    portletDataContext.getScopeGroupId(),
822                                                    layout.isPrivateLayout(), linkToLayoutId);
823    
824                                            exportLayout(
825                                                    portletDataContext, layoutConfigurationPortlet,
826                                                    layoutCache, portlets, portletIds, exportPermissions,
827                                                    exportUserPermissions, linkedToLayout, layoutsElement);
828                                    }
829                                    catch (NoSuchLayoutException nsle) {
830                                    }
831                            }
832                    }
833                    else if (layout.isTypePortlet()) {
834                            for (Portlet portlet : portlets) {
835                                    if (portlet.isScopeable() && layout.hasScopeGroup()) {
836                                            String key = PortletPermissionUtil.getPrimaryKey(
837                                                    layout.getPlid(), portlet.getPortletId());
838    
839                                            portletIds.put(
840                                                    key,
841                                                    new Object[] {
842                                                            portlet.getPortletId(), layout.getPlid(),
843                                                            layout.getScopeGroup().getGroupId(),
844                                                            StringPool.BLANK, layout.getUuid()
845                                                    });
846                                    }
847                            }
848    
849                            LayoutTypePortlet layoutTypePortlet =
850                                    (LayoutTypePortlet)layout.getLayoutType();
851    
852                            for (String portletId : layoutTypePortlet.getPortletIds()) {
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 =
875                                                            LayoutLocalServiceUtil.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(portletDataContext.getLayoutPath(layout.getLayoutId()));
1153                    sb.append("/icons/");
1154                    sb.append(image.getImageId());
1155                    sb.append(StringPool.PERIOD);
1156                    sb.append(image.getType());
1157    
1158                    return sb.toString();
1159            }
1160    
1161            protected String getLayoutSetLogoPath(
1162                    PortletDataContext portletDataContext) {
1163    
1164                    return portletDataContext.getRootPath().concat("/logo/");
1165            }
1166    
1167            protected String getLayoutSetPrototype(
1168                    PortletDataContext portletDataContext, String layoutSetPrototypeUuid) {
1169    
1170                    StringBundler sb = new StringBundler(3);
1171    
1172                    sb.append(portletDataContext.getRootPath());
1173                    sb.append("/layout-set-prototype/");
1174                    sb.append(layoutSetPrototypeUuid);
1175    
1176                    return sb.toString();
1177            }
1178    
1179            private static Log _log = LogFactoryUtil.getLog(LayoutExporter.class);
1180    
1181            private PermissionExporter _permissionExporter = new PermissionExporter();
1182            private PortletExporter _portletExporter = new PortletExporter();
1183    
1184    }