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