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