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