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