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