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.ExportImportThreadLocal;
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.JournalPortletDataHandler;
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                            ExportImportThreadLocal.setLayoutExportInProcess(true);
190    
191                            return doExportLayoutsAsFile(
192                                    groupId, privateLayout, layoutIds, parameterMap, startDate,
193                                    endDate);
194                    }
195                    finally {
196                            ExportImportThreadLocal.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 = JournalPortletDataHandler.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                    JournalPortletDataHandler.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                                                                    portletDataContext.isPrivateLayout());
873    
874                                                    if (scopeLayout == null) {
875                                                            continue;
876                                                    }
877    
878                                                    scopeGroup = scopeLayout.getScopeGroup();
879                                            }
880                                            else {
881                                                    throw new IllegalArgumentException(
882                                                            "Scope type " + scopeType + " is invalid");
883                                            }
884    
885                                            if (scopeGroup != null) {
886                                                    scopeGroupId = scopeGroup.getGroupId();
887                                            }
888                                    }
889    
890                                    String key = PortletPermissionUtil.getPrimaryKey(
891                                            layout.getPlid(), portletId);
892    
893                                    portletIds.put(
894                                            key,
895                                            new Object[] {
896                                                    portletId, layout.getPlid(), scopeGroupId, scopeType,
897                                                    scopeLayoutUuid
898                                            }
899                                    );
900                            }
901                    }
902    
903                    fixTypeSettings(layout);
904    
905                    layoutElement.addAttribute("path", path);
906    
907                    portletDataContext.addExpando(layoutElement, path, layout);
908    
909                    portletDataContext.addZipEntry(path, layout);
910            }
911    
912            protected void exportTheme(LayoutSet layoutSet, ZipWriter zipWriter)
913                    throws Exception {
914    
915                    Theme theme = layoutSet.getTheme();
916    
917                    String lookAndFeelXML = ContentUtil.get(
918                            "com/liferay/portal/dependencies/liferay-look-and-feel.xml.tmpl");
919    
920                    lookAndFeelXML = StringUtil.replace(
921                            lookAndFeelXML,
922                            new String[] {
923                                    "[$TEMPLATE_EXTENSION$]", "[$VIRTUAL_PATH$]"
924                            },
925                            new String[] {
926                                    theme.getTemplateExtension(), theme.getVirtualPath()
927                            }
928                    );
929    
930                    String servletContextName = theme.getServletContextName();
931    
932                    ServletContext servletContext = ServletContextPool.get(
933                            servletContextName);
934    
935                    if (servletContext == null) {
936                            if (_log.isWarnEnabled()) {
937                                    _log.warn(
938                                            "Servlet context not found for theme " +
939                                                    theme.getThemeId());
940                            }
941    
942                            return;
943                    }
944    
945                    File themeZip = new File(zipWriter.getPath() + "/theme.zip");
946    
947                    ZipWriter themeZipWriter = ZipWriterFactoryUtil.getZipWriter(themeZip);
948    
949                    themeZipWriter.addEntry("liferay-look-and-feel.xml", lookAndFeelXML);
950    
951                    File cssPath = null;
952                    File imagesPath = null;
953                    File javaScriptPath = null;
954                    File templatesPath = null;
955    
956                    if (!theme.isLoadFromServletContext()) {
957                            ThemeLoader themeLoader = ThemeLoaderFactory.getThemeLoader(
958                                    servletContextName);
959    
960                            if (themeLoader == null) {
961                                    _log.error(
962                                            servletContextName + " does not map to a theme loader");
963                            }
964                            else {
965                                    String realPath =
966                                            themeLoader.getFileStorage().getPath() + StringPool.SLASH +
967                                                    theme.getName();
968    
969                                    cssPath = new File(realPath + "/css");
970                                    imagesPath = new File(realPath + "/images");
971                                    javaScriptPath = new File(realPath + "/javascript");
972                                    templatesPath = new File(realPath + "/templates");
973                            }
974                    }
975                    else {
976                            cssPath = new File(servletContext.getRealPath(theme.getCssPath()));
977                            imagesPath = new File(
978                                    servletContext.getRealPath(theme.getImagesPath()));
979                            javaScriptPath = new File(
980                                    servletContext.getRealPath(theme.getJavaScriptPath()));
981                            templatesPath = new File(
982                                    servletContext.getRealPath(theme.getTemplatesPath()));
983                    }
984    
985                    exportThemeFiles("css", cssPath, themeZipWriter);
986                    exportThemeFiles("images", imagesPath, themeZipWriter);
987                    exportThemeFiles("javascript", javaScriptPath, themeZipWriter);
988                    exportThemeFiles("templates", templatesPath, themeZipWriter);
989            }
990    
991            protected void exportThemeFiles(String path, File dir, ZipWriter zipWriter)
992                    throws Exception {
993    
994                    if ((dir == null) || !dir.exists()) {
995                            return;
996                    }
997    
998                    File[] files = dir.listFiles();
999    
1000                    for (File file : files) {
1001                            if (file.isDirectory()) {
1002                                    exportThemeFiles(
1003                                            path + StringPool.SLASH + file.getName(), file, zipWriter);
1004                            }
1005                            else {
1006                                    zipWriter.addEntry(
1007                                            path + StringPool.SLASH + file.getName(),
1008                                            FileUtil.getBytes(file));
1009                            }
1010                    }
1011            }
1012    
1013            protected void fixTypeSettings(Layout layout) throws Exception {
1014                    if (!layout.isTypeURL()) {
1015                            return;
1016                    }
1017    
1018                    UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
1019    
1020                    String url = GetterUtil.getString(typeSettings.getProperty("url"));
1021    
1022                    String friendlyURLPrivateGroupPath =
1023                            PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
1024                    String friendlyURLPrivateUserPath =
1025                            PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
1026                    String friendlyURLPublicPath =
1027                            PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
1028    
1029                    if (!url.startsWith(friendlyURLPrivateGroupPath) &&
1030                            !url.startsWith(friendlyURLPrivateUserPath) &&
1031                            !url.startsWith(friendlyURLPublicPath)) {
1032    
1033                            return;
1034                    }
1035    
1036                    int x = url.indexOf(CharPool.SLASH, 1);
1037    
1038                    if (x == -1) {
1039                            return;
1040                    }
1041    
1042                    int y = url.indexOf(CharPool.SLASH, x + 1);
1043    
1044                    if (y == -1) {
1045                            return;
1046                    }
1047    
1048                    String friendlyURL = url.substring(x, y);
1049                    String groupFriendlyURL = layout.getGroup().getFriendlyURL();
1050    
1051                    if (!friendlyURL.equals(groupFriendlyURL)) {
1052                            return;
1053                    }
1054    
1055                    typeSettings.setProperty(
1056                            "url",
1057                            url.substring(0, x) + SAME_GROUP_FRIENDLY_URL + url.substring(y));
1058            }
1059    
1060            protected boolean[] getExportPortletControls(
1061                            long companyId, String portletId,
1062                            PortletDataContext portletDataContext,
1063                            Map<String, String[]> parameterMap, String type)
1064                    throws Exception {
1065    
1066                    boolean exportPortletData = MapUtil.getBoolean(
1067                            parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
1068                    boolean exportPortletDataAll = MapUtil.getBoolean(
1069                            parameterMap, PortletDataHandlerKeys.PORTLET_DATA_ALL);
1070                    boolean exportPortletSetup = MapUtil.getBoolean(
1071                            parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
1072                    boolean exportPortletSetupAll = MapUtil.getBoolean(
1073                            parameterMap, PortletDataHandlerKeys.PORTLET_SETUP_ALL);
1074    
1075                    if (_log.isDebugEnabled()) {
1076                            _log.debug("Export portlet data " + exportPortletData);
1077                            _log.debug("Export all portlet data " + exportPortletDataAll);
1078                            _log.debug("Export portlet setup " + exportPortletSetup);
1079                    }
1080    
1081                    boolean exportCurPortletData = exportPortletData;
1082                    boolean exportCurPortletSetup = exportPortletSetup;
1083    
1084                    // If PORTLET_DATA_ALL is true, this means that staging has just been
1085                    // activated and all data and setup must be exported. There is no
1086                    // portlet export control to check in this case.
1087    
1088                    if (exportPortletDataAll) {
1089                            exportCurPortletData = true;
1090                            exportCurPortletSetup = true;
1091                    }
1092                    else {
1093                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
1094                                    companyId, portletId);
1095    
1096                            if (portlet != null) {
1097                                    String portletDataHandlerClass =
1098                                            portlet.getPortletDataHandlerClass();
1099    
1100                                    // Checking if the portlet has a data handler, if it doesn't,
1101                                    // the default values are the ones set in PORTLET_DATA and
1102                                    // PORTLET_SETUP. If it has a data handler, iterate over each
1103                                    // portlet export control.
1104    
1105                                    if (portletDataHandlerClass != null) {
1106                                            String rootPortletId = PortletConstants.getRootPortletId(
1107                                                    portletId);
1108    
1109                                            // PORTLET_DATA and the PORTLET_DATA for this specific
1110                                            // data handler must be true
1111    
1112                                            exportCurPortletData =
1113                                                    exportPortletData &&
1114                                                    MapUtil.getBoolean(
1115                                                            parameterMap,
1116                                                            PortletDataHandlerKeys.PORTLET_DATA +
1117                                                                    StringPool.UNDERLINE + rootPortletId);
1118    
1119                                            // PORTLET_SETUP and the PORTLET_SETUP for this specific
1120                                            // data handler must be true
1121    
1122                                            exportCurPortletSetup =
1123                                                    exportPortletSetup &&
1124                                                    MapUtil.getBoolean(
1125                                                            parameterMap,
1126                                                            PortletDataHandlerKeys.PORTLET_SETUP +
1127                                                                    StringPool.UNDERLINE + rootPortletId);
1128                                    }
1129                            }
1130                    }
1131    
1132                    if (exportPortletSetupAll ||
1133                            (exportPortletSetup && type.equals("layout-prototype"))) {
1134    
1135                            exportCurPortletSetup = true;
1136                    }
1137    
1138                    return new boolean[] {exportCurPortletData, exportCurPortletSetup};
1139            }
1140    
1141            protected String getLayoutIconPath(
1142                    PortletDataContext portletDataContext, Layout layout, Image image) {
1143    
1144                    StringBundler sb = new StringBundler(5);
1145    
1146                    sb.append(portletDataContext.getLayoutPath(layout.getLayoutId()));
1147                    sb.append("/icons/");
1148                    sb.append(image.getImageId());
1149                    sb.append(StringPool.PERIOD);
1150                    sb.append(image.getType());
1151    
1152                    return sb.toString();
1153            }
1154    
1155            protected String getLayoutSetLogoPath(
1156                    PortletDataContext portletDataContext) {
1157    
1158                    return portletDataContext.getRootPath().concat("/logo/");
1159            }
1160    
1161            protected String getLayoutSetPrototype(
1162                    PortletDataContext portletDataContext, String layoutSetPrototypeUuid) {
1163    
1164                    StringBundler sb = new StringBundler(3);
1165    
1166                    sb.append(portletDataContext.getRootPath());
1167                    sb.append("/layout-set-prototype/");
1168                    sb.append(layoutSetPrototypeUuid);
1169    
1170                    return sb.toString();
1171            }
1172    
1173            private static Log _log = LogFactoryUtil.getLog(LayoutExporter.class);
1174    
1175            private PermissionExporter _permissionExporter = new PermissionExporter();
1176            private PortletExporter _portletExporter = new PortletExporter();
1177    
1178    }