1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.lar;
16  
17  import com.liferay.counter.service.CounterLocalServiceUtil;
18  import com.liferay.portal.LARFileException;
19  import com.liferay.portal.LARTypeException;
20  import com.liferay.portal.LayoutImportException;
21  import com.liferay.portal.NoSuchLayoutException;
22  import com.liferay.portal.kernel.cluster.ClusterLinkUtil;
23  import com.liferay.portal.kernel.cluster.Priority;
24  import com.liferay.portal.kernel.exception.PortalException;
25  import com.liferay.portal.kernel.exception.SystemException;
26  import com.liferay.portal.kernel.lar.PortletDataContext;
27  import com.liferay.portal.kernel.lar.PortletDataHandlerKeys;
28  import com.liferay.portal.kernel.lar.UserIdStrategy;
29  import com.liferay.portal.kernel.log.Log;
30  import com.liferay.portal.kernel.log.LogFactoryUtil;
31  import com.liferay.portal.kernel.messaging.Message;
32  import com.liferay.portal.kernel.util.ArrayUtil;
33  import com.liferay.portal.kernel.util.FileUtil;
34  import com.liferay.portal.kernel.util.GetterUtil;
35  import com.liferay.portal.kernel.util.LocaleUtil;
36  import com.liferay.portal.kernel.util.LocalizationUtil;
37  import com.liferay.portal.kernel.util.MapUtil;
38  import com.liferay.portal.kernel.util.MethodWrapper;
39  import com.liferay.portal.kernel.util.ReleaseInfo;
40  import com.liferay.portal.kernel.util.StringPool;
41  import com.liferay.portal.kernel.util.StringUtil;
42  import com.liferay.portal.kernel.util.Time;
43  import com.liferay.portal.kernel.util.UnicodeProperties;
44  import com.liferay.portal.kernel.util.Validator;
45  import com.liferay.portal.kernel.xml.Document;
46  import com.liferay.portal.kernel.xml.DocumentException;
47  import com.liferay.portal.kernel.xml.Element;
48  import com.liferay.portal.kernel.xml.SAXReaderUtil;
49  import com.liferay.portal.kernel.zip.ZipReader;
50  import com.liferay.portal.kernel.zip.ZipReaderFactoryUtil;
51  import com.liferay.portal.model.Layout;
52  import com.liferay.portal.model.LayoutSet;
53  import com.liferay.portal.model.LayoutTemplate;
54  import com.liferay.portal.model.LayoutTypePortlet;
55  import com.liferay.portal.model.LayoutTypePortletConstants;
56  import com.liferay.portal.model.PortletConstants;
57  import com.liferay.portal.model.User;
58  import com.liferay.portal.model.impl.ColorSchemeImpl;
59  import com.liferay.portal.service.ImageLocalServiceUtil;
60  import com.liferay.portal.service.LayoutLocalServiceUtil;
61  import com.liferay.portal.service.LayoutSetLocalServiceUtil;
62  import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
63  import com.liferay.portal.service.ServiceContext;
64  import com.liferay.portal.service.persistence.LayoutUtil;
65  import com.liferay.portal.service.persistence.UserUtil;
66  import com.liferay.portal.theme.ThemeLoader;
67  import com.liferay.portal.theme.ThemeLoaderFactory;
68  import com.liferay.portal.util.PortalUtil;
69  import com.liferay.portal.util.PortletKeys;
70  import com.liferay.portal.util.PropsValues;
71  import com.liferay.portlet.asset.DuplicateVocabularyException;
72  import com.liferay.portlet.asset.model.AssetVocabulary;
73  import com.liferay.portlet.asset.service.AssetVocabularyLocalServiceUtil;
74  import com.liferay.portlet.asset.service.persistence.AssetVocabularyUtil;
75  import com.liferay.portlet.journal.model.JournalArticle;
76  
77  import java.io.File;
78  import java.io.IOException;
79  import java.io.InputStream;
80  
81  import java.util.ArrayList;
82  import java.util.Date;
83  import java.util.HashMap;
84  import java.util.HashSet;
85  import java.util.List;
86  import java.util.Locale;
87  import java.util.Map;
88  import java.util.Set;
89  
90  import org.apache.commons.lang.time.StopWatch;
91  
92  /**
93   * <a href="LayoutImporter.java.html"><b><i>View Source</i></b></a>
94   *
95   * @author Brian Wing Shun Chan
96   * @author Joel Kozikowski
97   * @author Charles May
98   * @author Raymond Augé
99   * @author Jorge Ferrer
100  * @author Bruno Farache
101  * @author Wesley Gong
102  * @author Zsigmond Rab
103  * @author Douglas Wong
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 PortalException, SystemException {
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 root = 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 doc = SAXReaderUtil.read(xml);
204 
205             root = doc.getRootElement();
206         }
207         catch (Exception e) {
208             throw new LARFileException(e);
209         }
210 
211         // Build compatibility
212 
213         Element header = root.element("header");
214 
215         int buildNumber = ReleaseInfo.getBuildNumber();
216 
217         int importBuildNumber = GetterUtil.getInteger(
218             header.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 = header.attributeValue("type");
229 
230         if (!larType.equals("layout-set")) {
231             throw new LARTypeException(
232                 "Invalid type of LAR file (" + larType + ")");
233         }
234 
235         // Import GroupId
236 
237         long sourceGroupId = GetterUtil.getLong(
238             header.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 = header.attributeValue("theme-id");
251         String colorSchemeId = header.attributeValue("color-scheme-id");
252 
253         boolean useThemeZip = false;
254 
255         if (themeZip != null) {
256             try {
257                 String importThemeId = importTheme(layoutSet, themeZip);
258 
259                 if (importThemeId != null) {
260                     themeId = importThemeId;
261                     colorSchemeId =
262                         ColorSchemeImpl.getDefaultRegularColorSchemeId();
263 
264                     useThemeZip = true;
265                 }
266 
267                 if (_log.isDebugEnabled()) {
268                     _log.debug(
269                         "Importing theme takes " + stopWatch.getTime() + " ms");
270                 }
271             }
272             catch (Exception e) {
273                 throw new SystemException(e);
274             }
275         }
276 
277         boolean wapTheme = false;
278 
279         LayoutSetLocalServiceUtil.updateLookAndFeel(
280             groupId, privateLayout, themeId, colorSchemeId, StringPool.BLANK,
281             wapTheme);
282 
283         // Read categories, comments, locks, permissions, ratings, and tags to
284         // make them available to the data handlers through the context
285 
286         if (importPermissions) {
287             _permissionImporter.readPortletDataPermissions(context);
288         }
289 
290         if (importCategories) {
291             _portletImporter.readCategories(context);
292         }
293 
294         _portletImporter.readComments(context, root);
295         _portletImporter.readLocks(context, root);
296         _portletImporter.readRatings(context, root);
297         _portletImporter.readTags(context, root);
298 
299         // Layouts
300 
301         List<Layout> previousLayouts = LayoutUtil.findByG_P(
302             groupId, privateLayout);
303 
304         List<Layout> newLayouts = new ArrayList<Layout>();
305 
306         Set<Long> newLayoutIds = new HashSet<Long>();
307 
308         Map<Long, Long> newLayoutIdPlidMap =
309             (Map<Long, Long>)context.getNewPrimaryKeysMap(Layout.class);
310 
311         List<Element> layoutEls = root.element("layouts").elements("layout");
312 
313         if (_log.isDebugEnabled()) {
314             if (layoutEls.size() > 0) {
315                 _log.debug("Importing layouts");
316             }
317         }
318 
319         for (Element layoutRefEl : layoutEls) {
320             long layoutId = GetterUtil.getInteger(
321                 layoutRefEl.attributeValue("layout-id"));
322 
323             long oldLayoutId = layoutId;
324 
325             boolean deleteLayout = GetterUtil.getBoolean(
326                 layoutRefEl.attributeValue("delete"));
327 
328             if (deleteLayout) {
329                 try {
330                     LayoutLocalServiceUtil.deleteLayout(
331                         context.getGroupId(), privateLayout, oldLayoutId);
332                 }
333                 catch (NoSuchLayoutException nsle) {
334                     _log.warn(
335                         "Error deleting layout for {" + sourceGroupId + ", " +
336                             privateLayout + ", " + oldLayoutId + "}");
337                 }
338 
339                 continue;
340             }
341 
342             String layoutPath = layoutRefEl.attributeValue("path");
343 
344             Element layoutEl = null;
345 
346             try {
347                 Document layoutDoc = SAXReaderUtil.read(
348                     context.getZipEntryAsString(layoutPath));
349 
350                 layoutEl = layoutDoc.getRootElement();
351             }
352             catch (DocumentException de) {
353                 throw new SystemException(de);
354             }
355 
356             long parentLayoutId = GetterUtil.getInteger(
357                 layoutEl.elementText("parent-layout-id"));
358 
359             if (_log.isDebugEnabled()) {
360                 _log.debug(
361                     "Importing layout with layout id " + layoutId +
362                         " and parent layout id " + parentLayoutId);
363             }
364 
365             long oldPlid = GetterUtil.getInteger(
366                 layoutEl.attributeValue("old-plid"));
367 
368             String name = layoutEl.elementText("name");
369             String title = layoutEl.elementText("title");
370             String description = layoutEl.elementText("description");
371             String type = layoutEl.elementText("type");
372             String typeSettings = layoutEl.elementText("type-settings");
373             boolean hidden = GetterUtil.getBoolean(
374                 layoutEl.elementText("hidden"));
375             String friendlyURL = layoutEl.elementText("friendly-url");
376             boolean iconImage = GetterUtil.getBoolean(
377                 layoutEl.elementText("icon-image"));
378 
379             byte[] iconBytes = null;
380 
381             if (iconImage) {
382                 String path = layoutEl.elementText("icon-image-path");
383 
384                 iconBytes = context.getZipEntryAsByteArray(path);
385             }
386 
387             if (useThemeZip) {
388                 themeId = StringPool.BLANK;
389                 colorSchemeId = StringPool.BLANK;
390             }
391             else {
392                 themeId = layoutEl.elementText("theme-id");
393                 colorSchemeId = layoutEl.elementText("color-scheme-id");
394             }
395 
396             String wapThemeId = layoutEl.elementText("wap-theme-id");
397             String wapColorSchemeId = layoutEl.elementText(
398                 "wap-color-scheme-id");
399             String css = layoutEl.elementText("css");
400             int priority = GetterUtil.getInteger(
401                 layoutEl.elementText("priority"));
402 
403             Layout layout = null;
404 
405             if (layoutsImportMode.equals(
406                     PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_ADD_AS_NEW)) {
407 
408                 layoutId = LayoutLocalServiceUtil.getNextLayoutId(
409                     groupId, privateLayout);
410                 friendlyURL = StringPool.SLASH + layoutId;
411             }
412             else if (layoutsImportMode.equals(
413                     PortletDataHandlerKeys.
414                         LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_NAME)) {
415 
416                 Locale locale = LocaleUtil.getDefault();
417 
418                 String localizedName = LocalizationUtil.getLocalization(
419                     name, LocaleUtil.toLanguageId(locale));
420 
421                 for (Layout curLayout : previousLayouts) {
422                     if (curLayout.getName(locale).equals(localizedName)) {
423                         layout = curLayout;
424 
425                         break;
426                     }
427                 }
428 
429                 if (layout == null) {
430                     layoutId = LayoutLocalServiceUtil.getNextLayoutId(
431                         groupId, privateLayout);
432                 }
433             }
434             else {
435                 layout = LayoutUtil.fetchByG_P_L(
436                     groupId, privateLayout, layoutId);
437 
438                 if (layout == null) {
439                     layoutId = LayoutLocalServiceUtil.getNextLayoutId(
440                         groupId, privateLayout);
441                 }
442             }
443 
444             if (_log.isDebugEnabled()) {
445                 if (layout == null) {
446                     _log.debug(
447                         "Layout with {groupId=" + groupId + ",privateLayout=" +
448                             privateLayout + ",layoutId=" + layoutId +
449                                 "} does not exist");
450                 }
451                 else {
452                     _log.debug(
453                         "Layout with {groupId=" + groupId + ",privateLayout=" +
454                             privateLayout + ",layoutId=" + layoutId +
455                                 "} exists");
456                 }
457             }
458 
459             if (layout == null) {
460                 long plid = CounterLocalServiceUtil.increment();
461 
462                 layout = LayoutUtil.create(plid);
463 
464                 layout.setGroupId(groupId);
465                 layout.setPrivateLayout(privateLayout);
466                 layout.setLayoutId(layoutId);
467             }
468 
469             layout.setCompanyId(user.getCompanyId());
470 
471             if (parentLayoutId > 0) {
472                 long parentLayoutPlid = newLayoutIdPlidMap.get(parentLayoutId);
473 
474                 Layout parentLayout = LayoutLocalServiceUtil.getLayout(
475                     parentLayoutPlid);
476 
477                 parentLayoutId = parentLayout.getLayoutId();
478             }
479 
480             layout.setParentLayoutId(parentLayoutId);
481             layout.setName(name);
482             layout.setTitle(title);
483             layout.setDescription(description);
484             layout.setType(type);
485 
486             if (layout.isTypePortlet() &&
487                 Validator.isNotNull(layout.getTypeSettings()) &&
488 
489                 !portletsMergeMode.equals(
490                     PortletDataHandlerKeys.PORTLETS_MERGE_MODE_REPLACE)) {
491 
492                 mergePortlets(layout, typeSettings, portletsMergeMode);
493             }
494             else if (layout.isTypeLinkToLayout() &&
495                      Validator.isNotNull(layout.getTypeSettings())) {
496 
497                 UnicodeProperties typeSettingsProperties =
498                     layout.getTypeSettingsProperties();
499 
500                 long oldLinkToLayoutId = GetterUtil.getLong(
501                     typeSettingsProperties.getProperty("linkToLayoutId"));
502 
503                 if (oldLinkToLayoutId > 0) {
504                     long linkToLayoutPlid = newLayoutIdPlidMap.get(
505                         oldLinkToLayoutId);
506 
507                     Layout linkToLayout = LayoutLocalServiceUtil.getLayout(
508                         linkToLayoutPlid);
509 
510                     typeSettingsProperties.setProperty(
511                         "linkToLayoutId",
512                         String.valueOf(linkToLayout.getLayoutId()));
513 
514                     layout.setTypeSettingsProperties(typeSettingsProperties);
515                 }
516                 else {
517                     layout.setTypeSettings(typeSettings);
518                 }
519             }
520             else {
521                 layout.setTypeSettings(typeSettings);
522             }
523 
524             layout.setHidden(hidden);
525             layout.setFriendlyURL(friendlyURL);
526 
527             if (iconImage) {
528                 layout.setIconImage(iconImage);
529 
530                 if (layout.isNew()) {
531                     long iconImageId = CounterLocalServiceUtil.increment();
532 
533                     layout.setIconImageId(iconImageId);
534                 }
535             }
536 
537             layout.setThemeId(themeId);
538             layout.setColorSchemeId(colorSchemeId);
539             layout.setWapThemeId(wapThemeId);
540             layout.setWapColorSchemeId(wapColorSchemeId);
541             layout.setCss(css);
542             layout.setPriority(priority);
543 
544             fixTypeSettings(layout);
545 
546             LayoutUtil.update(layout, false);
547 
548             if ((iconBytes != null) && (iconBytes.length > 0)) {
549                 ImageLocalServiceUtil.updateImage(
550                     layout.getIconImageId(), iconBytes);
551             }
552 
553             context.setPlid(layout.getPlid());
554             context.setOldPlid(oldPlid);
555 
556             newLayoutIdPlidMap.put(oldLayoutId, layout.getPlid());
557 
558             newLayoutIds.add(layoutId);
559 
560             newLayouts.add(layout);
561 
562             // Layout permissions
563 
564             if (importPermissions) {
565                 _permissionImporter.importLayoutPermissions(
566                     layoutCache, companyId, groupId, userId, layout, layoutEl,
567                     root, importUserPermissions);
568             }
569 
570             _portletImporter.importPortletData(
571                 context, PortletKeys.LAYOUT_CONFIGURATION, null, layoutEl);
572         }
573 
574         List<Element> portletEls = root.element("portlets").elements("portlet");
575 
576         // Delete portlet data
577 
578         if (deletePortletData) {
579             if (_log.isDebugEnabled()) {
580                 if (portletEls.size() > 0) {
581                     _log.debug("Deleting portlet data");
582                 }
583             }
584 
585             for (Element portletRefEl : portletEls) {
586                 String portletId = portletRefEl.attributeValue("portlet-id");
587                 long layoutId = GetterUtil.getLong(
588                     portletRefEl.attributeValue("layout-id"));
589                 long plid = newLayoutIdPlidMap.get(layoutId);
590 
591                 context.setPlid(plid);
592 
593                 _portletImporter.deletePortletData(context, portletId, plid);
594             }
595         }
596 
597         // Import portlets
598 
599         if (_log.isDebugEnabled()) {
600             if (portletEls.size() > 0) {
601                 _log.debug("Importing portlets");
602             }
603         }
604 
605         for (Element portletRefEl : portletEls) {
606             String portletPath = portletRefEl.attributeValue("path");
607             String portletId = portletRefEl.attributeValue("portlet-id");
608             long layoutId = GetterUtil.getLong(
609                 portletRefEl.attributeValue("layout-id"));
610             long plid = newLayoutIdPlidMap.get(layoutId);
611             long oldPlid = GetterUtil.getLong(
612                 portletRefEl.attributeValue("old-plid"));
613 
614             Layout layout = LayoutUtil.findByPrimaryKey(plid);
615 
616             context.setPlid(plid);
617             context.setOldPlid(oldPlid);
618 
619             Element portletEl = null;
620 
621             try {
622                 Document portletDoc = SAXReaderUtil.read(
623                     context.getZipEntryAsString(portletPath));
624 
625                 portletEl = portletDoc.getRootElement();
626             }
627             catch (DocumentException de) {
628                 throw new SystemException(de);
629             }
630 
631             // The order of the import is important. You must always import
632             // the portlet preferences first, then the portlet data, then
633             // the portlet permissions. The import of the portlet data
634             // assumes that portlet preferences already exist.
635 
636             // Portlet preferences
637 
638             _portletImporter.importPortletPreferences(
639                 context, layoutSet.getCompanyId(), layout.getGroupId(),
640                 layout, null, portletEl, importPortletSetup,
641                 importPortletArchivedSetups, importPortletUserPreferences,
642                 false);
643 
644             // Portlet data scope
645 
646             long scopeLayoutId = GetterUtil.getLong(
647                 portletEl.attributeValue("scope-layout-id"));
648 
649             context.setScopeLayoutId(scopeLayoutId);
650 
651             // Portlet data
652 
653             Element portletDataEl = portletEl.element("portlet-data");
654 
655             if (importPortletData && (portletDataEl != null)) {
656                 _portletImporter.importPortletData(
657                     context, portletId, plid, portletDataEl);
658             }
659 
660             // Portlet permissions
661 
662             if (importPermissions) {
663                 _permissionImporter.importPortletPermissions(
664                     layoutCache, companyId, groupId, userId, layout, portletEl,
665                     portletId, importUserPermissions);
666             }
667 
668             // Archived setups
669 
670             _portletImporter.importPortletPreferences(
671                 context, layoutSet.getCompanyId(), groupId, null, null,
672                 portletEl, importPortletSetup, importPortletArchivedSetups,
673                 importPortletUserPreferences, false);
674         }
675 
676         // Delete missing layouts
677 
678         if (deleteMissingLayouts) {
679             deleteMissingLayouts(
680                 groupId, privateLayout, newLayoutIds, previousLayouts);
681         }
682 
683         // Page count
684 
685         LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
686 
687         if (_log.isInfoEnabled()) {
688             _log.info("Importing layouts takes " + stopWatch.getTime() + " ms");
689         }
690 
691         // Web content layout type
692 
693         for (Layout layout : newLayouts) {
694             UnicodeProperties typeSettingsProperties =
695                 layout.getTypeSettingsProperties();
696 
697             String articleId = typeSettingsProperties.getProperty("article-id");
698 
699             if (Validator.isNotNull(articleId)) {
700                 Map<String, String> articleIds =
701                     (Map<String, String>)context.getNewPrimaryKeysMap(
702                         JournalArticle.class);
703 
704                 typeSettingsProperties.setProperty(
705                     "article-id",
706                     MapUtil.getString(articleIds, articleId, articleId));
707 
708                 LayoutUtil.update(layout, false);
709             }
710         }
711 
712         zipReader.close();
713     }
714 
715     protected String[] appendPortletIds(
716         String[] portletIds, String[] newPortletIds, String portletsMergeMode) {
717 
718         for (String portletId : newPortletIds) {
719             if (ArrayUtil.contains(portletIds, portletId)) {
720                 continue;
721             }
722 
723             if (portletsMergeMode.equals(
724                     PortletDataHandlerKeys.PORTLETS_MERGE_MODE_ADD_TO_BOTTOM)) {
725 
726                 portletIds = ArrayUtil.append(portletIds, portletId);
727             }
728             else {
729                 portletIds = ArrayUtil.append(
730                     new String[] {portletId}, portletIds);
731             }
732         }
733 
734         return portletIds;
735     }
736 
737     protected void deleteMissingLayouts(
738             long groupId, boolean privateLayout, Set<Long> newLayoutIds,
739             List<Layout> previousLayouts)
740         throws PortalException, SystemException {
741 
742         // Layouts
743 
744         if (_log.isDebugEnabled()) {
745             if (newLayoutIds.size() > 0) {
746                 _log.debug("Delete missing layouts");
747             }
748         }
749 
750         for (Layout layout : previousLayouts) {
751             if (!newLayoutIds.contains(layout.getLayoutId())) {
752                 try {
753                     LayoutLocalServiceUtil.deleteLayout(layout, false);
754                 }
755                 catch (NoSuchLayoutException nsle) {
756                 }
757             }
758         }
759 
760         // Layout set
761 
762         LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
763     }
764 
765     protected void fixTypeSettings(Layout layout)
766         throws PortalException, SystemException {
767 
768         if (!layout.isTypeURL()) {
769             return;
770         }
771 
772         UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
773 
774         String url = GetterUtil.getString(typeSettings.getProperty("url"));
775 
776         String friendlyURLPrivateGroupPath =
777             PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
778         String friendlyURLPrivateUserPath =
779             PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
780         String friendlyURLPublicPath =
781             PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
782 
783         if (!url.startsWith(friendlyURLPrivateGroupPath) &&
784             !url.startsWith(friendlyURLPrivateUserPath) &&
785             !url.startsWith(friendlyURLPublicPath)) {
786 
787             return;
788         }
789 
790         int x = url.indexOf(StringPool.SLASH, 1);
791 
792         if (x == -1) {
793             return;
794         }
795 
796         int y = url.indexOf(StringPool.SLASH, x + 1);
797 
798         if (y == -1) {
799             return;
800         }
801 
802         String friendlyURL = url.substring(x, y);
803 
804         if (!friendlyURL.equals(LayoutExporter.SAME_GROUP_FRIENDLY_URL)) {
805             return;
806         }
807 
808         typeSettings.setProperty(
809             "url",
810             url.substring(0, x) + layout.getGroup().getFriendlyURL() +
811                 url.substring(y));
812     }
813 
814     protected AssetVocabulary getAssetVocabulary(
815             PortletDataContext context, String vocabularyUuid,
816             String vocabularyName, String userUuid,
817             ServiceContext serviceContext)
818         throws Exception {
819 
820         AssetVocabulary assetVocabulary = null;
821 
822         try {
823             if (context.getDataStrategy().equals(
824                     PortletDataHandlerKeys.DATA_STRATEGY_MIRROR)) {
825 
826                 AssetVocabulary existingAssetVocabulary =
827                     AssetVocabularyUtil.fetchByUUID_G(
828                         vocabularyUuid, context.getGroupId());
829 
830                 if (existingAssetVocabulary == null) {
831                     Map<Locale, String> titleMap =
832                         new HashMap<Locale, String>();
833 
834                     titleMap.put(LocaleUtil.getDefault(), vocabularyName);
835 
836                     assetVocabulary =
837                         AssetVocabularyLocalServiceUtil.addVocabulary(
838                             vocabularyUuid, context.getUserId(userUuid),
839                             titleMap, null, StringPool.BLANK, serviceContext);
840                 }
841                 else {
842                     assetVocabulary =
843                         AssetVocabularyLocalServiceUtil.updateVocabulary(
844                             existingAssetVocabulary.getVocabularyId(),
845                             existingAssetVocabulary.getTitleMap(),
846                             existingAssetVocabulary.getDescriptionMap(),
847                             existingAssetVocabulary.getSettings(),
848                             serviceContext);
849                 }
850             }
851             else {
852                 Map<Locale, String> titleMap =  new HashMap<Locale, String>();
853 
854                 titleMap.put(LocaleUtil.getDefault(), vocabularyName);
855 
856                 assetVocabulary = AssetVocabularyLocalServiceUtil.addVocabulary(
857                     null, context.getUserId(userUuid), titleMap, null,
858                     StringPool.BLANK, serviceContext);
859             }
860         }
861         catch (DuplicateVocabularyException dve) {
862             assetVocabulary =
863                 AssetVocabularyLocalServiceUtil.getGroupVocabulary(
864                     context.getGroupId(), vocabularyName);
865         }
866 
867         return assetVocabulary;
868     }
869 
870     protected String importTheme(LayoutSet layoutSet, InputStream themeZip)
871         throws IOException {
872 
873         ThemeLoader themeLoader = ThemeLoaderFactory.getDefaultThemeLoader();
874 
875         if (themeLoader == null) {
876             _log.error("No theme loaders are deployed");
877 
878             return null;
879         }
880 
881         ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(themeZip);
882 
883         String lookAndFeelXML = zipReader.getEntryAsString(
884             "liferay-look-and-feel.xml");
885 
886         String themeId = String.valueOf(layoutSet.getGroupId());
887 
888         if (layoutSet.isPrivateLayout()) {
889             themeId += "-private";
890         }
891         else {
892             themeId += "-public";
893         }
894 
895         if (PropsValues.THEME_LOADER_NEW_THEME_ID_ON_IMPORT) {
896             Date now = new Date();
897 
898             themeId += "-" + Time.getShortTimestamp(now);
899         }
900 
901         String themeName = themeId;
902 
903         lookAndFeelXML = StringUtil.replace(
904             lookAndFeelXML,
905             new String[] {
906                 "[$GROUP_ID$]", "[$THEME_ID$]", "[$THEME_NAME$]"
907             },
908             new String[] {
909                 String.valueOf(layoutSet.getGroupId()), themeId, themeName
910             }
911         );
912 
913         FileUtil.deltree(
914             themeLoader.getFileStorage() + StringPool.SLASH + themeId);
915 
916         List<String> zipEntries = zipReader.getEntries();
917 
918         for (String zipEntry : zipEntries) {
919             String key = zipEntry;
920 
921             if (key.contains(StringPool.SLASH)) {
922                 key = key.substring(key.lastIndexOf(StringPool.SLASH));
923             }
924 
925             if (key.equals("liferay-look-and-feel.xml")) {
926                 FileUtil.write(
927                     themeLoader.getFileStorage() + StringPool.SLASH + themeId +
928                         StringPool.SLASH + key,
929                     lookAndFeelXML.getBytes());
930             }
931             else {
932                 InputStream is = zipReader.getEntryAsInputStream(zipEntry);
933 
934                 FileUtil.write(
935                     themeLoader.getFileStorage() + StringPool.SLASH + themeId +
936                         StringPool.SLASH + key,
937                     is);
938             }
939         }
940 
941         themeLoader.loadThemes();
942 
943         MethodWrapper methodWrapper = new MethodWrapper(
944             ThemeLoaderFactory.class.getName(), "loadThemes");
945 
946         Message message = new Message();
947         message.setPayload(methodWrapper);
948 
949         ClusterLinkUtil.sendMulticastMessage(message, Priority.LEVEL5);
950 
951         themeId +=
952             PortletConstants.WAR_SEPARATOR +
953                 themeLoader.getServletContextName();
954 
955         return PortalUtil.getJsSafePortletId(themeId);
956     }
957 
958     protected void mergePortlets(
959         Layout layout, String newTypeSettings, String portletsMergeMode) {
960 
961         try {
962             UnicodeProperties previousProps =
963                 layout.getTypeSettingsProperties();
964             LayoutTypePortlet previousLayoutType =
965                 (LayoutTypePortlet)layout.getLayoutType();
966             List<String> previousColumns =
967                 previousLayoutType.getLayoutTemplate().getColumns();
968 
969             UnicodeProperties newProps = new UnicodeProperties(true);
970 
971             newProps.load(newTypeSettings);
972 
973             String layoutTemplateId = newProps.getProperty(
974                     LayoutTypePortletConstants.LAYOUT_TEMPLATE_ID);
975 
976             LayoutTemplate newLayoutTemplate =
977                 LayoutTemplateLocalServiceUtil.getLayoutTemplate(
978                     layoutTemplateId, false, null);
979 
980             String[] lostPortletIds = new String[0];
981 
982             for (String columnId : newLayoutTemplate.getColumns()) {
983                 String columnValue =
984                     newProps.getProperty(columnId);
985 
986                 String[] portletIds = StringUtil.split(columnValue);
987 
988                 if (!previousColumns.contains(columnId)) {
989                     lostPortletIds = ArrayUtil.append(
990                         lostPortletIds, portletIds);
991                 }
992                 else {
993 
994                     String[] previousPortletIds = StringUtil.split(
995                         previousProps.getProperty(columnId));
996 
997                     portletIds = appendPortletIds(
998                         previousPortletIds, portletIds, portletsMergeMode);
999 
1000                    previousProps.setProperty(
1001                        columnId, StringUtil.merge(portletIds));
1002                }
1003            }
1004
1005            // Add portlets in non-existent column to the first column
1006
1007            String columnId = previousColumns.get(0);
1008
1009            String[] portletIds = StringUtil.split(
1010                previousProps.getProperty(columnId));
1011
1012            appendPortletIds(portletIds, lostPortletIds, portletsMergeMode);
1013
1014            previousProps.setProperty(
1015                columnId, StringUtil.merge(portletIds));
1016
1017            layout.setTypeSettings(previousProps.toString());
1018
1019        }
1020        catch (IOException e) {
1021            layout.setTypeSettings(newTypeSettings);
1022        }
1023    }
1024
1025    private static Log _log = LogFactoryUtil.getLog(LayoutImporter.class);
1026
1027    private PermissionImporter _permissionImporter = new PermissionImporter();
1028    private PortletImporter _portletImporter = new PortletImporter();
1029
1030}