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