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.counter.service.CounterLocalServiceUtil;
018    import com.liferay.portal.LARFileException;
019    import com.liferay.portal.LARTypeException;
020    import com.liferay.portal.LayoutImportException;
021    import com.liferay.portal.NoSuchLayoutException;
022    import com.liferay.portal.kernel.cluster.ClusterExecutorUtil;
023    import com.liferay.portal.kernel.cluster.ClusterRequest;
024    import com.liferay.portal.kernel.lar.PortletDataContext;
025    import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
026    import com.liferay.portal.kernel.lar.UserIdStrategy;
027    import com.liferay.portal.kernel.log.Log;
028    import com.liferay.portal.kernel.log.LogFactoryUtil;
029    import com.liferay.portal.kernel.util.ArrayUtil;
030    import com.liferay.portal.kernel.util.CharPool;
031    import com.liferay.portal.kernel.util.FileUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.LocaleUtil;
034    import com.liferay.portal.kernel.util.MapUtil;
035    import com.liferay.portal.kernel.util.MethodHandler;
036    import com.liferay.portal.kernel.util.MethodKey;
037    import com.liferay.portal.kernel.util.ReleaseInfo;
038    import com.liferay.portal.kernel.util.StringPool;
039    import com.liferay.portal.kernel.util.StringUtil;
040    import com.liferay.portal.kernel.util.Time;
041    import com.liferay.portal.kernel.util.UnicodeProperties;
042    import com.liferay.portal.kernel.util.Validator;
043    import com.liferay.portal.kernel.xml.Document;
044    import com.liferay.portal.kernel.xml.Element;
045    import com.liferay.portal.kernel.xml.Node;
046    import com.liferay.portal.kernel.xml.SAXReaderUtil;
047    import com.liferay.portal.kernel.zip.ZipReader;
048    import com.liferay.portal.kernel.zip.ZipReaderFactoryUtil;
049    import com.liferay.portal.model.Layout;
050    import com.liferay.portal.model.LayoutConstants;
051    import com.liferay.portal.model.LayoutSet;
052    import com.liferay.portal.model.LayoutTemplate;
053    import com.liferay.portal.model.LayoutTypePortlet;
054    import com.liferay.portal.model.LayoutTypePortletConstants;
055    import com.liferay.portal.model.PortletConstants;
056    import com.liferay.portal.model.User;
057    import com.liferay.portal.model.impl.ColorSchemeImpl;
058    import com.liferay.portal.service.ImageLocalServiceUtil;
059    import com.liferay.portal.service.LayoutLocalServiceUtil;
060    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
061    import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
062    import com.liferay.portal.service.ServiceContext;
063    import com.liferay.portal.service.persistence.LayoutUtil;
064    import com.liferay.portal.service.persistence.UserUtil;
065    import com.liferay.portal.theme.ThemeLoader;
066    import com.liferay.portal.theme.ThemeLoaderFactory;
067    import com.liferay.portal.util.PortalUtil;
068    import com.liferay.portal.util.PortletKeys;
069    import com.liferay.portal.util.PropsValues;
070    import com.liferay.portlet.asset.DuplicateVocabularyException;
071    import com.liferay.portlet.asset.model.AssetVocabulary;
072    import com.liferay.portlet.asset.service.AssetVocabularyLocalServiceUtil;
073    import com.liferay.portlet.asset.service.persistence.AssetVocabularyUtil;
074    import com.liferay.portlet.journal.model.JournalArticle;
075    
076    import java.io.File;
077    import java.io.IOException;
078    import java.io.InputStream;
079    
080    import java.util.ArrayList;
081    import java.util.Date;
082    import java.util.HashMap;
083    import java.util.HashSet;
084    import java.util.List;
085    import java.util.Locale;
086    import java.util.Map;
087    import java.util.Set;
088    
089    import org.apache.commons.lang.time.StopWatch;
090    
091    /**
092     * @author Brian Wing Shun Chan
093     * @author Joel Kozikowski
094     * @author Charles May
095     * @author Raymond Augé
096     * @author Jorge Ferrer
097     * @author Bruno Farache
098     * @author Wesley Gong
099     * @author Zsigmond Rab
100     * @author Douglas Wong
101     * @author Julio Camarero
102     */
103    public class LayoutImporter {
104    
105            public void importLayouts(
106                            long userId, long groupId, boolean privateLayout,
107                            Map<String, String[]> parameterMap, File file)
108                    throws Exception {
109    
110                    boolean deleteMissingLayouts = MapUtil.getBoolean(
111                            parameterMap, PortletDataHandlerKeys.DELETE_MISSING_LAYOUTS,
112                            Boolean.TRUE.booleanValue());
113                    boolean deletePortletData = MapUtil.getBoolean(
114                            parameterMap, PortletDataHandlerKeys.DELETE_PORTLET_DATA);
115                    boolean importCategories = MapUtil.getBoolean(
116                            parameterMap, PortletDataHandlerKeys.CATEGORIES);
117                    boolean importPermissions = MapUtil.getBoolean(
118                            parameterMap, PortletDataHandlerKeys.PERMISSIONS);
119                    boolean importUserPermissions = MapUtil.getBoolean(
120                            parameterMap, PortletDataHandlerKeys.PERMISSIONS);
121                    boolean importPortletData = MapUtil.getBoolean(
122                            parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
123                    boolean importPortletSetup = MapUtil.getBoolean(
124                            parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
125                    boolean importPortletArchivedSetups = MapUtil.getBoolean(
126                            parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
127                    boolean importPortletUserPreferences = MapUtil.getBoolean(
128                            parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
129                    boolean importTheme = MapUtil.getBoolean(
130                            parameterMap, PortletDataHandlerKeys.THEME);
131                    String layoutsImportMode = MapUtil.getString(
132                            parameterMap, PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE,
133                            PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_ID);
134                    String portletsMergeMode = MapUtil.getString(
135                            parameterMap, PortletDataHandlerKeys.PORTLETS_MERGE_MODE,
136                            PortletDataHandlerKeys.PORTLETS_MERGE_MODE_REPLACE);
137                    String userIdStrategy = MapUtil.getString(
138                            parameterMap, PortletDataHandlerKeys.USER_ID_STRATEGY);
139    
140                    if (_log.isDebugEnabled()) {
141                            _log.debug("Delete portlet data " + deletePortletData);
142                            _log.debug("Import categories " + importCategories);
143                            _log.debug("Import permissions " + importPermissions);
144                            _log.debug("Import user permissions " + importUserPermissions);
145                            _log.debug("Import portlet data " + importPortletData);
146                            _log.debug("Import portlet setup " + importPortletSetup);
147                            _log.debug(
148                                    "Import portlet archived setups " +
149                                            importPortletArchivedSetups);
150                            _log.debug(
151                                    "Import portlet user preferences " +
152                                            importPortletUserPreferences);
153                            _log.debug("Import theme " + importTheme);
154                    }
155    
156                    StopWatch stopWatch = null;
157    
158                    if (_log.isInfoEnabled()) {
159                            stopWatch = new StopWatch();
160    
161                            stopWatch.start();
162                    }
163    
164                    LayoutCache layoutCache = new LayoutCache();
165    
166                    LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
167                            groupId, privateLayout);
168    
169                    long companyId = layoutSet.getCompanyId();
170    
171                    User user = UserUtil.findByPrimaryKey(userId);
172    
173                    UserIdStrategy strategy = _portletImporter.getUserIdStrategy(
174                            user, userIdStrategy);
175    
176                    ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(file);
177    
178                    PortletDataContext context = new PortletDataContextImpl(
179                            companyId, groupId, parameterMap, new HashSet<String>(), strategy,
180                            zipReader);
181    
182                    context.setPortetDataContextListener(
183                            new PortletDataContextListenerImpl(context));
184    
185                    context.setPrivateLayout(privateLayout);
186    
187                    // Zip
188    
189                    Element rootElement = null;
190                    InputStream themeZip = null;
191    
192                    // Manifest
193    
194                    String xml = context.getZipEntryAsString("/manifest.xml");
195    
196                    if (xml == null) {
197                            throw new LARFileException("manifest.xml not found in the LAR");
198                    }
199    
200                    try {
201                            Document document = SAXReaderUtil.read(xml);
202    
203                            rootElement = document.getRootElement();
204                    }
205                    catch (Exception e) {
206                            throw new LARFileException(e);
207                    }
208    
209                    // Build compatibility
210    
211                    Element headerElement = rootElement.element("header");
212    
213                    int buildNumber = ReleaseInfo.getBuildNumber();
214    
215                    int importBuildNumber = GetterUtil.getInteger(
216                            headerElement.attributeValue("build-number"));
217    
218                    if (buildNumber != importBuildNumber) {
219                            throw new LayoutImportException(
220                                    "LAR build number " + importBuildNumber + " does not match " +
221                                            "portal build number " + buildNumber);
222                    }
223    
224                    // Type compatibility
225    
226                    String larType = headerElement.attributeValue("type");
227    
228                    if (!larType.equals("layout-set")) {
229                            throw new LARTypeException(
230                                    "Invalid type of LAR file (" + larType + ")");
231                    }
232    
233                    // Group id
234    
235                    long sourceGroupId = GetterUtil.getLong(
236                            headerElement.attributeValue("group-id"));
237    
238                    context.setSourceGroupId(sourceGroupId);
239    
240                    // Look and feel
241    
242                    if (importTheme) {
243                            themeZip = context.getZipEntryAsInputStream("theme.zip");
244                    }
245    
246                    // Look and feel
247    
248                    String themeId = headerElement.attributeValue("theme-id");
249                    String colorSchemeId = headerElement.attributeValue("color-scheme-id");
250    
251                    boolean useThemeZip = false;
252    
253                    if (themeZip != null) {
254                            String importThemeId = importTheme(layoutSet, themeZip);
255    
256                            if (importThemeId != null) {
257                                    themeId = importThemeId;
258                                    colorSchemeId =
259                                            ColorSchemeImpl.getDefaultRegularColorSchemeId();
260    
261                                    useThemeZip = true;
262                            }
263    
264                            if (_log.isDebugEnabled()) {
265                                    _log.debug(
266                                            "Importing theme takes " + stopWatch.getTime() + " ms");
267                            }
268                    }
269    
270                    boolean wapTheme = false;
271    
272                    LayoutSetLocalServiceUtil.updateLookAndFeel(
273                            groupId, privateLayout, themeId, colorSchemeId, StringPool.BLANK,
274                            wapTheme);
275    
276                    // Read categories, comments, locks, permissions, ratings, and tags to
277                    // make them available to the data handlers through the context
278    
279                    if (importPermissions) {
280                            _permissionImporter.readPortletDataPermissions(context);
281                    }
282    
283                    if (importCategories) {
284                            _portletImporter.readCategories(context);
285                    }
286    
287                    _portletImporter.readComments(context, rootElement);
288                    _portletImporter.readLocks(context, rootElement);
289                    _portletImporter.readRatings(context, rootElement);
290                    _portletImporter.readTags(context, rootElement);
291    
292                    // Layouts
293    
294                    List<Layout> previousLayouts = LayoutUtil.findByG_P(
295                            groupId, privateLayout);
296    
297                    List<Layout> newLayouts = new ArrayList<Layout>();
298    
299                    Set<Long> newLayoutIds = new HashSet<Long>();
300    
301                    Map<Long, Layout> newLayoutsMap =
302                            (Map<Long, Layout>)context.getNewPrimaryKeysMap(Layout.class);
303    
304                    Element layoutsElement = rootElement.element("layouts");
305    
306                    List<Element> layoutElements = layoutsElement.elements("layout");
307    
308                    if (_log.isDebugEnabled()) {
309                            if (layoutElements.size() > 0) {
310                                    _log.debug("Importing layouts");
311                            }
312                    }
313    
314                    for (Element layoutElement : layoutElements) {
315                            importLayout(
316                                    context, user, layoutCache, previousLayouts, newLayouts,
317                                    newLayoutsMap, newLayoutIds, portletsMergeMode, themeId,
318                                    colorSchemeId, layoutsImportMode, privateLayout,
319                                    importPermissions, importUserPermissions, useThemeZip,
320                                    rootElement, layoutElement);
321                    }
322    
323                    Element portletsElement = rootElement.element("portlets");
324    
325                    List<Element> portletElements = portletsElement.elements("portlet");
326    
327                    // Delete portlet data
328    
329                    if (deletePortletData) {
330                            if (_log.isDebugEnabled()) {
331                                    if (portletElements.size() > 0) {
332                                            _log.debug("Deleting portlet data");
333                                    }
334                            }
335    
336                            for (Element portletElement : portletElements) {
337                                    String portletId = portletElement.attributeValue("portlet-id");
338                                    long layoutId = GetterUtil.getLong(
339                                            portletElement.attributeValue("layout-id"));
340                                    long plid = newLayoutsMap.get(layoutId).getPlid();
341    
342                                    context.setPlid(plid);
343    
344                                    _portletImporter.deletePortletData(context, portletId, plid);
345                            }
346                    }
347    
348                    // Import portlets
349    
350                    if (_log.isDebugEnabled()) {
351                            if (portletElements.size() > 0) {
352                                    _log.debug("Importing portlets");
353                            }
354                    }
355    
356                    for (Element portletElement : portletElements) {
357                            String portletPath = portletElement.attributeValue("path");
358                            String portletId = portletElement.attributeValue("portlet-id");
359                            long layoutId = GetterUtil.getLong(
360                                    portletElement.attributeValue("layout-id"));
361                            long plid = newLayoutsMap.get(layoutId).getPlid();
362                            long oldPlid = GetterUtil.getLong(
363                                    portletElement.attributeValue("old-plid"));
364    
365                            Layout layout = null;
366    
367                            try {
368                                    layout = LayoutUtil.findByPrimaryKey(plid);
369                            }
370                            catch (NoSuchLayoutException nsle) {
371                                    continue;
372                            }
373    
374                            context.setPlid(plid);
375                            context.setOldPlid(oldPlid);
376    
377                            Document portletDocument = SAXReaderUtil.read(
378                                    context.getZipEntryAsString(portletPath));
379    
380                            portletElement = portletDocument.getRootElement();
381    
382                            // The order of the import is important. You must always import
383                            // the portlet preferences first, then the portlet data, then
384                            // the portlet permissions. The import of the portlet data
385                            // assumes that portlet preferences already exist.
386    
387                            // Portlet preferences
388    
389                            _portletImporter.importPortletPreferences(
390                                    context, layoutSet.getCompanyId(), layout.getGroupId(),
391                                    layout, null, portletElement, importPortletSetup,
392                                    importPortletArchivedSetups, importPortletUserPreferences,
393                                    false);
394    
395                            // Portlet data scope
396    
397                            String scopeLayoutUuid = GetterUtil.getString(
398                                    portletElement.attributeValue("scope-layout-uuid"));
399    
400                            context.setScopeLayoutUuid(scopeLayoutUuid);
401    
402                            // Portlet data
403    
404                            Element portletDataElement = portletElement.element("portlet-data");
405    
406                            if (importPortletData && (portletDataElement != null)) {
407                                    _portletImporter.importPortletData(
408                                            context, portletId, plid, portletDataElement);
409                            }
410    
411                            // Portlet permissions
412    
413                            if (importPermissions) {
414                                    _permissionImporter.importPortletPermissions(
415                                            layoutCache, companyId, groupId, userId, layout,
416                                            portletElement, portletId, importUserPermissions);
417                            }
418    
419                            // Archived setups
420    
421                            _portletImporter.importPortletPreferences(
422                                    context, layoutSet.getCompanyId(), groupId, null, null,
423                                    portletElement, importPortletSetup, importPortletArchivedSetups,
424                                    importPortletUserPreferences, false);
425                    }
426    
427                    // Delete missing layouts
428    
429                    if (deleteMissingLayouts) {
430                            deleteMissingLayouts(
431                                    groupId, privateLayout, newLayoutIds, previousLayouts);
432                    }
433    
434                    // Page count
435    
436                    LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
437    
438                    if (_log.isInfoEnabled()) {
439                            _log.info("Importing layouts takes " + stopWatch.getTime() + " ms");
440                    }
441    
442                    // Web content layout type
443    
444                    for (Layout layout : newLayouts) {
445                            UnicodeProperties typeSettingsProperties =
446                                    layout.getTypeSettingsProperties();
447    
448                            String articleId = typeSettingsProperties.getProperty("article-id");
449    
450                            if (Validator.isNotNull(articleId)) {
451                                    Map<String, String> articleIds =
452                                            (Map<String, String>)context.getNewPrimaryKeysMap(
453                                                    JournalArticle.class);
454    
455                                    typeSettingsProperties.setProperty(
456                                            "article-id",
457                                            MapUtil.getString(articleIds, articleId, articleId));
458    
459                                    LayoutUtil.update(layout, false);
460                            }
461                    }
462    
463                    zipReader.close();
464            }
465    
466            protected String[] appendPortletIds(
467                    String[] portletIds, String[] newPortletIds, String portletsMergeMode) {
468    
469                    for (String portletId : newPortletIds) {
470                            if (ArrayUtil.contains(portletIds, portletId)) {
471                                    continue;
472                            }
473    
474                            if (portletsMergeMode.equals(
475                                            PortletDataHandlerKeys.PORTLETS_MERGE_MODE_ADD_TO_BOTTOM)) {
476    
477                                    portletIds = ArrayUtil.append(portletIds, portletId);
478                            }
479                            else {
480                                    portletIds = ArrayUtil.append(
481                                            new String[] {portletId}, portletIds);
482                            }
483                    }
484    
485                    return portletIds;
486            }
487    
488            protected void deleteMissingLayouts(
489                            long groupId, boolean privateLayout, Set<Long> newLayoutIds,
490                            List<Layout> previousLayouts)
491                    throws Exception {
492    
493                    // Layouts
494    
495                    if (_log.isDebugEnabled()) {
496                            if (newLayoutIds.size() > 0) {
497                                    _log.debug("Delete missing layouts");
498                            }
499                    }
500    
501                    for (Layout layout : previousLayouts) {
502                            if (!newLayoutIds.contains(layout.getLayoutId())) {
503                                    try {
504                                            LayoutLocalServiceUtil.deleteLayout(layout, false);
505                                    }
506                                    catch (NoSuchLayoutException nsle) {
507                                    }
508                            }
509                    }
510    
511                    // Layout set
512    
513                    LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
514            }
515    
516            protected void fixTypeSettings(Layout layout) throws Exception {
517                    if (!layout.isTypeURL()) {
518                            return;
519                    }
520    
521                    UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
522    
523                    String url = GetterUtil.getString(typeSettings.getProperty("url"));
524    
525                    String friendlyURLPrivateGroupPath =
526                            PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
527                    String friendlyURLPrivateUserPath =
528                            PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
529                    String friendlyURLPublicPath =
530                            PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
531    
532                    if (!url.startsWith(friendlyURLPrivateGroupPath) &&
533                            !url.startsWith(friendlyURLPrivateUserPath) &&
534                            !url.startsWith(friendlyURLPublicPath)) {
535    
536                            return;
537                    }
538    
539                    int x = url.indexOf(CharPool.SLASH, 1);
540    
541                    if (x == -1) {
542                            return;
543                    }
544    
545                    int y = url.indexOf(CharPool.SLASH, x + 1);
546    
547                    if (y == -1) {
548                            return;
549                    }
550    
551                    String friendlyURL = url.substring(x, y);
552    
553                    if (!friendlyURL.equals(LayoutExporter.SAME_GROUP_FRIENDLY_URL)) {
554                            return;
555                    }
556    
557                    typeSettings.setProperty(
558                            "url",
559                            url.substring(0, x) + layout.getGroup().getFriendlyURL() +
560                                    url.substring(y));
561            }
562    
563            protected AssetVocabulary getAssetVocabulary(
564                            PortletDataContext context, String vocabularyUuid,
565                            String vocabularyName, String userUuid,
566                            ServiceContext serviceContext)
567                    throws Exception {
568    
569                    AssetVocabulary assetVocabulary = null;
570    
571                    try {
572                            if (context.getDataStrategy().equals(
573                                            PortletDataHandlerKeys.DATA_STRATEGY_MIRROR)) {
574    
575                                    AssetVocabulary existingAssetVocabulary =
576                                            AssetVocabularyUtil.fetchByUUID_G(
577                                                    vocabularyUuid, context.getGroupId());
578    
579                                    if (existingAssetVocabulary == null) {
580                                            Map<Locale, String> titleMap =
581                                                    new HashMap<Locale, String>();
582    
583                                            titleMap.put(LocaleUtil.getDefault(), vocabularyName);
584    
585                                            serviceContext.setUuid(vocabularyUuid);
586    
587                                            assetVocabulary =
588                                                    AssetVocabularyLocalServiceUtil.addVocabulary(
589                                                            context.getUserId(userUuid), titleMap, null,
590                                                            StringPool.BLANK, serviceContext);
591                                    }
592                                    else {
593                                            assetVocabulary =
594                                                    AssetVocabularyLocalServiceUtil.updateVocabulary(
595                                                            existingAssetVocabulary.getVocabularyId(),
596                                                            existingAssetVocabulary.getTitleMap(),
597                                                            existingAssetVocabulary.getDescriptionMap(),
598                                                            existingAssetVocabulary.getSettings(),
599                                                            serviceContext);
600                                    }
601                            }
602                            else {
603                                    Map<Locale, String> titleMap =    new HashMap<Locale, String>();
604    
605                                    titleMap.put(LocaleUtil.getDefault(), vocabularyName);
606    
607                                    assetVocabulary = AssetVocabularyLocalServiceUtil.addVocabulary(
608                                            context.getUserId(userUuid), titleMap, null,
609                                            StringPool.BLANK, serviceContext);
610                            }
611                    }
612                    catch (DuplicateVocabularyException dve) {
613                            assetVocabulary =
614                                    AssetVocabularyLocalServiceUtil.getGroupVocabulary(
615                                            context.getGroupId(), vocabularyName);
616                    }
617    
618                    return assetVocabulary;
619            }
620    
621            protected void importLayout(
622                            PortletDataContext context, User user, LayoutCache layoutCache,
623                            List<Layout> previousLayouts, List<Layout> newLayouts,
624                            Map<Long, Layout> newLayoutsMap, Set<Long> newLayoutIds,
625                            String portletsMergeMode, String themeId, String colorSchemeId,
626                            String layoutsImportMode, boolean privateLayout,
627                            boolean importPermissions, boolean importUserPermissions,
628                            boolean useThemeZip, Element rootElement, Element layoutElement)
629                    throws Exception {
630    
631                    long groupId = context.getGroupId();
632    
633                    String layoutUuid = GetterUtil.getString(
634                            layoutElement.attributeValue("layout-uuid"));
635    
636                    long layoutId = GetterUtil.getInteger(
637                            layoutElement.attributeValue("layout-id"));
638    
639                    long oldLayoutId = layoutId;
640    
641                    boolean deleteLayout = GetterUtil.getBoolean(
642                            layoutElement.attributeValue("delete"));
643    
644                    if (deleteLayout) {
645                            try {
646                                    Layout layout =
647                                            LayoutLocalServiceUtil.getLayoutByUuidAndGroupId(
648                                                    layoutUuid, groupId);
649    
650                                    if (layout != null) {
651                                            newLayoutsMap.put(oldLayoutId, layout);
652    
653                                            LayoutLocalServiceUtil.deleteLayout(layout);
654                                    }
655                            }
656                            catch (NoSuchLayoutException nsle) {
657                                    _log.warn(
658                                            "Error deleting layout for {" + layoutUuid + ", " +
659                                                    groupId + "}");
660                            }
661    
662                            return;
663                    }
664    
665                    String path = layoutElement.attributeValue("path");
666    
667                    if (!context.isPathNotProcessed(path)) {
668                            return;
669                    }
670    
671                    Layout layout = (Layout)context.getZipEntryAsObject(path);
672    
673                    Layout existingLayout = null;
674                    Layout importedLayout = null;
675    
676                    String friendlyURL = layout.getFriendlyURL();
677    
678                    if (layoutsImportMode.equals(
679                                    PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_ADD_AS_NEW)) {
680    
681                            layoutId = LayoutLocalServiceUtil.getNextLayoutId(
682                                    groupId, privateLayout);
683                            friendlyURL = StringPool.SLASH + layoutId;
684                    }
685                    else if (layoutsImportMode.equals(
686                                            PortletDataHandlerKeys.
687                                                    LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_NAME)) {
688    
689                            Locale locale = LocaleUtil.getDefault();
690    
691                            String localizedName = layout.getName(locale);
692    
693                            for (Layout curLayout : previousLayouts) {
694                                    if (curLayout.getName(locale).equals(localizedName)) {
695                                            existingLayout = curLayout;
696    
697                                            break;
698                                    }
699                            }
700    
701                            if (existingLayout == null) {
702                                    layoutId = LayoutLocalServiceUtil.getNextLayoutId(
703                                            groupId, privateLayout);
704                            }
705                    }
706                    else {
707    
708                            // The default behaviour of import mode is
709                            // PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_ID
710    
711                            existingLayout = LayoutUtil.fetchByUUID_G(
712                                    layout.getUuid(), groupId);
713    
714                            if (existingLayout == null) {
715                                    existingLayout = LayoutUtil.fetchByG_P_F(
716                                            groupId, privateLayout, friendlyURL);
717                            }
718    
719                            if (existingLayout == null) {
720                                    layoutId = LayoutLocalServiceUtil.getNextLayoutId(
721                                            groupId, privateLayout);
722                            }
723                    }
724    
725                    if (_log.isDebugEnabled()) {
726                            if (existingLayout == null) {
727                                    _log.debug(
728                                            "Layout with {groupId=" + groupId + ",privateLayout=" +
729                                                    privateLayout + ",layoutId=" + layoutId +
730                                                            "} does not exist");
731                            }
732                            else {
733                                    _log.debug(
734                                            "Layout with {groupId=" + groupId + ",privateLayout=" +
735                                                    privateLayout + ",layoutId=" + layoutId +
736                                                            "} exists");
737                            }
738                    }
739    
740                    if (existingLayout == null) {
741                            long plid = CounterLocalServiceUtil.increment();
742    
743                            importedLayout = LayoutUtil.create(plid);
744    
745                            importedLayout.setUuid(layout.getUuid());
746                            importedLayout.setGroupId(groupId);
747                            importedLayout.setPrivateLayout(privateLayout);
748                            importedLayout.setLayoutId(layoutId);
749    
750                            if (layout.isIconImage()) {
751                                    long iconImageId = CounterLocalServiceUtil.increment();
752    
753                                    importedLayout.setIconImageId(iconImageId);
754                            }
755                    }
756                    else {
757                            importedLayout = existingLayout;
758                    }
759    
760                    newLayoutsMap.put(oldLayoutId, importedLayout);
761    
762                    long parentLayoutId = layout.getParentLayoutId();
763    
764                    Node parentLayoutNode = rootElement.selectSingleNode(
765                            "./layouts/layout[@layout-id='" + parentLayoutId + "']");
766    
767                    if ((parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) &&
768                            (parentLayoutNode != null)) {
769    
770                            importLayout(
771                                    context, user, layoutCache, previousLayouts, newLayouts,
772                                    newLayoutsMap, newLayoutIds, portletsMergeMode, themeId,
773                                    colorSchemeId, layoutsImportMode, privateLayout,
774                                    importPermissions, importUserPermissions, useThemeZip,
775                                    rootElement, (Element)parentLayoutNode);
776    
777                            Layout parentLayout = newLayoutsMap.get(parentLayoutId);
778    
779                            parentLayoutId = parentLayout.getLayoutId();
780                    }
781    
782                    if (_log.isDebugEnabled()) {
783                            _log.debug(
784                                    "Importing layout with layout id " + layoutId +
785                                            " and parent layout id " + parentLayoutId);
786                    }
787    
788                    importedLayout.setCompanyId(user.getCompanyId());
789                    importedLayout.setParentLayoutId(parentLayoutId);
790                    importedLayout.setName(layout.getName());
791                    importedLayout.setTitle(layout.getTitle());
792                    importedLayout.setDescription(layout.getDescription());
793                    importedLayout.setType(layout.getType());
794    
795                    if (layout.isTypePortlet() &&
796                            Validator.isNotNull(layout.getTypeSettings()) &&
797                            !portletsMergeMode.equals(
798                                    PortletDataHandlerKeys.PORTLETS_MERGE_MODE_REPLACE)) {
799    
800                            mergePortlets(
801                                    importedLayout, layout.getTypeSettings(), portletsMergeMode);
802                    }
803                    else if (layout.isTypeLinkToLayout()) {
804                            UnicodeProperties typeSettingsProperties =
805                                    layout.getTypeSettingsProperties();
806    
807                            long linkToLayoutId = GetterUtil.getLong(
808                                    typeSettingsProperties.getProperty(
809                                            "linkToLayoutId", StringPool.BLANK));
810    
811                            if (linkToLayoutId > 0) {
812                                    Node linkedLayoutNode = rootElement.selectSingleNode(
813                                            "./layouts/layout[@layout-id='" + linkToLayoutId + "']");
814    
815                                    importLayout(
816                                            context, user, layoutCache, previousLayouts, newLayouts,
817                                            newLayoutsMap, newLayoutIds, portletsMergeMode, themeId,
818                                            colorSchemeId, layoutsImportMode, privateLayout,
819                                            importPermissions, importUserPermissions, useThemeZip,
820                                            rootElement, (Element)linkedLayoutNode);
821    
822                                    Layout linkedLayout = newLayoutsMap.get(linkToLayoutId);
823    
824                                    typeSettingsProperties.setProperty(
825                                            "linkToLayoutId",
826                                            String.valueOf(linkedLayout.getLayoutId()));
827                            }
828    
829                            importedLayout.setTypeSettings(layout.getTypeSettings());
830                    }
831                    else {
832                            importedLayout.setTypeSettings(layout.getTypeSettings());
833                    }
834    
835                    importedLayout.setHidden(layout.isHidden());
836                    importedLayout.setFriendlyURL(friendlyURL);
837    
838                    if (useThemeZip) {
839                            importedLayout.setThemeId(StringPool.BLANK);
840                            importedLayout.setColorSchemeId(StringPool.BLANK);
841                    }
842                    else {
843                            importedLayout.setThemeId(layout.getThemeId());
844                            importedLayout.setColorSchemeId(layout.getColorSchemeId());
845                    }
846    
847                    importedLayout.setWapThemeId(layout.getWapThemeId());
848                    importedLayout.setWapColorSchemeId(layout.getWapColorSchemeId());
849                    importedLayout.setCss(layout.getCss());
850                    importedLayout.setPriority(layout.getPriority());
851    
852                    fixTypeSettings(importedLayout);
853    
854                    if (layout.isIconImage()) {
855                            String iconImagePath = layoutElement.elementText("icon-image-path");
856    
857                            byte[] iconBytes = context.getZipEntryAsByteArray(iconImagePath);
858    
859                            if ((iconBytes != null) && (iconBytes.length > 0)) {
860                                    importedLayout.setIconImage(true);
861    
862                                    ImageLocalServiceUtil.updateImage(
863                                            importedLayout.getIconImageId(), iconBytes);
864                            }
865                    }
866                    else {
867                            ImageLocalServiceUtil.deleteImage(importedLayout.getIconImageId());
868                    }
869    
870                    LayoutUtil.update(importedLayout, false);
871    
872                    context.setPlid(importedLayout.getPlid());
873                    context.setOldPlid(layout.getPlid());
874    
875                    newLayoutIds.add(importedLayout.getLayoutId());
876    
877                    newLayouts.add(importedLayout);
878    
879                    // Layout permissions
880    
881                    if (importPermissions) {
882                            _permissionImporter.importLayoutPermissions(
883                                    layoutCache, context.getCompanyId(), groupId, user.getUserId(),
884                                    importedLayout, layoutElement, rootElement,
885                                    importUserPermissions);
886                    }
887    
888                    _portletImporter.importPortletData(
889                            context, PortletKeys.LAYOUT_CONFIGURATION, null, layoutElement);
890            }
891    
892            protected String importTheme(LayoutSet layoutSet, InputStream themeZip)
893                    throws Exception {
894    
895                    ThemeLoader themeLoader = ThemeLoaderFactory.getDefaultThemeLoader();
896    
897                    if (themeLoader == null) {
898                            _log.error("No theme loaders are deployed");
899    
900                            return null;
901                    }
902    
903                    ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(themeZip);
904    
905                    String lookAndFeelXML = zipReader.getEntryAsString(
906                            "liferay-look-and-feel.xml");
907    
908                    String themeId = String.valueOf(layoutSet.getGroupId());
909    
910                    if (layoutSet.isPrivateLayout()) {
911                            themeId += "-private";
912                    }
913                    else {
914                            themeId += "-public";
915                    }
916    
917                    if (PropsValues.THEME_LOADER_NEW_THEME_ID_ON_IMPORT) {
918                            Date now = new Date();
919    
920                            themeId += "-" + Time.getShortTimestamp(now);
921                    }
922    
923                    String themeName = themeId;
924    
925                    lookAndFeelXML = StringUtil.replace(
926                            lookAndFeelXML,
927                            new String[] {
928                                    "[$GROUP_ID$]", "[$THEME_ID$]", "[$THEME_NAME$]"
929                            },
930                            new String[] {
931                                    String.valueOf(layoutSet.getGroupId()), themeId, themeName
932                            }
933                    );
934    
935                    FileUtil.deltree(
936                            themeLoader.getFileStorage() + StringPool.SLASH + themeId);
937    
938                    List<String> zipEntries = zipReader.getEntries();
939    
940                    for (String zipEntry : zipEntries) {
941                            String key = zipEntry;
942    
943                            if (key.contains(StringPool.SLASH)) {
944                                    key = key.substring(key.lastIndexOf(CharPool.SLASH));
945                            }
946    
947                            if (key.equals("liferay-look-and-feel.xml")) {
948                                    FileUtil.write(
949                                            themeLoader.getFileStorage() + StringPool.SLASH + themeId +
950                                                    StringPool.SLASH + key,
951                                            lookAndFeelXML.getBytes());
952                            }
953                            else {
954                                    InputStream is = zipReader.getEntryAsInputStream(zipEntry);
955    
956                                    FileUtil.write(
957                                            themeLoader.getFileStorage() + StringPool.SLASH + themeId +
958                                                    StringPool.SLASH + key,
959                                            is);
960                            }
961                    }
962    
963                    themeLoader.loadThemes();
964    
965                    ClusterRequest clusterRequest = ClusterRequest.createMulticastRequest(
966                            _loadThemesMethodHandler, true);
967    
968                    clusterRequest.setFireAndForget(true);
969    
970                    ClusterExecutorUtil.execute(clusterRequest);
971    
972                    themeId +=
973                            PortletConstants.WAR_SEPARATOR +
974                                    themeLoader.getServletContextName();
975    
976                    return PortalUtil.getJsSafePortletId(themeId);
977            }
978    
979            protected void mergePortlets(
980                    Layout layout, String newTypeSettings, String portletsMergeMode) {
981    
982                    try {
983                            UnicodeProperties previousProperties =
984                                    layout.getTypeSettingsProperties();
985                            LayoutTypePortlet previousLayoutType =
986                                    (LayoutTypePortlet)layout.getLayoutType();
987                            List<String> previousColumns =
988                                    previousLayoutType.getLayoutTemplate().getColumns();
989    
990                            UnicodeProperties newProperties = new UnicodeProperties(true);
991    
992                            newProperties.load(newTypeSettings);
993    
994                            String layoutTemplateId = newProperties.getProperty(
995                                    LayoutTypePortletConstants.LAYOUT_TEMPLATE_ID);
996    
997                            LayoutTemplate newLayoutTemplate =
998                                    LayoutTemplateLocalServiceUtil.getLayoutTemplate(
999                                            layoutTemplateId, false, null);
1000    
1001                            String[] lostPortletIds = new String[0];
1002    
1003                            for (String columnId : newLayoutTemplate.getColumns()) {
1004                                    String columnValue = newProperties.getProperty(columnId);
1005    
1006                                    String[] portletIds = StringUtil.split(columnValue);
1007    
1008                                    if (!previousColumns.contains(columnId)) {
1009                                            lostPortletIds = ArrayUtil.append(
1010                                                    lostPortletIds, portletIds);
1011                                    }
1012                                    else {
1013                                            String[] previousPortletIds = StringUtil.split(
1014                                                    previousProperties.getProperty(columnId));
1015    
1016                                            portletIds = appendPortletIds(
1017                                                    previousPortletIds, portletIds, portletsMergeMode);
1018    
1019                                            previousProperties.setProperty(
1020                                                    columnId, StringUtil.merge(portletIds));
1021                                    }
1022                            }
1023    
1024                            // Add portlets in non-existent column to the first column
1025    
1026                            String columnId = previousColumns.get(0);
1027    
1028                            String[] portletIds = StringUtil.split(
1029                                    previousProperties.getProperty(columnId));
1030    
1031                            appendPortletIds(portletIds, lostPortletIds, portletsMergeMode);
1032    
1033                            previousProperties.setProperty(
1034                                    columnId, StringUtil.merge(portletIds));
1035    
1036                            layout.setTypeSettings(previousProperties.toString());
1037                    }
1038                    catch (IOException ioe) {
1039                            layout.setTypeSettings(newTypeSettings);
1040                    }
1041            }
1042    
1043            private static Log _log = LogFactoryUtil.getLog(LayoutImporter.class);
1044    
1045            private static MethodHandler _loadThemesMethodHandler = new MethodHandler(
1046                    new MethodKey(ThemeLoaderFactory.class.getName(), "loadThemes"));
1047    
1048            private PermissionImporter _permissionImporter = new PermissionImporter();
1049            private PortletImporter _portletImporter = new PortletImporter();
1050    
1051    }