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.log.Log;
27  import com.liferay.portal.kernel.log.LogFactoryUtil;
28  import com.liferay.portal.kernel.messaging.Message;
29  import com.liferay.portal.kernel.util.ArrayUtil;
30  import com.liferay.portal.kernel.util.FileUtil;
31  import com.liferay.portal.kernel.util.GetterUtil;
32  import com.liferay.portal.kernel.util.LocaleUtil;
33  import com.liferay.portal.kernel.util.LocalizationUtil;
34  import com.liferay.portal.kernel.util.MapUtil;
35  import com.liferay.portal.kernel.util.MethodWrapper;
36  import com.liferay.portal.kernel.util.ReleaseInfo;
37  import com.liferay.portal.kernel.util.StringPool;
38  import com.liferay.portal.kernel.util.StringUtil;
39  import com.liferay.portal.kernel.util.Time;
40  import com.liferay.portal.kernel.util.UnicodeProperties;
41  import com.liferay.portal.kernel.util.Validator;
42  import com.liferay.portal.kernel.xml.Document;
43  import com.liferay.portal.kernel.xml.DocumentException;
44  import com.liferay.portal.kernel.xml.Element;
45  import com.liferay.portal.kernel.xml.SAXReaderUtil;
46  import com.liferay.portal.kernel.zip.ZipReader;
47  import com.liferay.portal.kernel.zip.ZipReaderFactoryUtil;
48  import com.liferay.portal.model.Layout;
49  import com.liferay.portal.model.LayoutSet;
50  import com.liferay.portal.model.LayoutTemplate;
51  import com.liferay.portal.model.LayoutTypePortlet;
52  import com.liferay.portal.model.LayoutTypePortletConstants;
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.ServiceContext;
61  import com.liferay.portal.service.persistence.LayoutUtil;
62  import com.liferay.portal.service.persistence.UserUtil;
63  import com.liferay.portal.theme.ThemeLoader;
64  import com.liferay.portal.theme.ThemeLoaderFactory;
65  import com.liferay.portal.util.PortalUtil;
66  import com.liferay.portal.util.PortletKeys;
67  import com.liferay.portal.util.PropsValues;
68  import com.liferay.portlet.asset.DuplicateCategoryException;
69  import com.liferay.portlet.asset.DuplicateVocabularyException;
70  import com.liferay.portlet.asset.NoSuchCategoryException;
71  import com.liferay.portlet.asset.model.AssetCategory;
72  import com.liferay.portlet.asset.model.AssetVocabulary;
73  import com.liferay.portlet.asset.service.AssetCategoryLocalServiceUtil;
74  import com.liferay.portlet.asset.service.AssetVocabularyLocalServiceUtil;
75  import com.liferay.portlet.asset.service.persistence.AssetCategoryUtil;
76  import com.liferay.portlet.asset.service.persistence.AssetVocabularyUtil;
77  import com.liferay.portlet.journal.model.JournalArticle;
78  
79  import java.io.File;
80  import java.io.IOException;
81  import java.io.InputStream;
82  
83  import java.util.ArrayList;
84  import java.util.Date;
85  import java.util.HashMap;
86  import java.util.HashSet;
87  import java.util.List;
88  import java.util.Locale;
89  import java.util.Map;
90  import java.util.Set;
91  
92  import org.apache.commons.lang.time.StopWatch;
93  
94  /**
95   * <a href="LayoutImporter.java.html"><b><i>View Source</i></b></a>
96   *
97   * @author Brian Wing Shun Chan
98   * @author Joel Kozikowski
99   * @author Charles May
100  * @author Raymond Augé
101  * @author Jorge Ferrer
102  * @author Bruno Farache
103  * @author Wesley Gong
104  * @author Zsigmond Rab
105  * @author Douglas Wong
106  */
107 public class LayoutImporter {
108 
109     public void importLayouts(
110             long userId, long groupId, boolean privateLayout,
111             Map<String, String[]> parameterMap, File file)
112         throws PortalException, SystemException {
113 
114         boolean deleteMissingLayouts = MapUtil.getBoolean(
115             parameterMap, PortletDataHandlerKeys.DELETE_MISSING_LAYOUTS,
116             Boolean.TRUE.booleanValue());
117         boolean deletePortletData = MapUtil.getBoolean(
118             parameterMap, PortletDataHandlerKeys.DELETE_PORTLET_DATA);
119         boolean importCategories = MapUtil.getBoolean(
120             parameterMap, PortletDataHandlerKeys.CATEGORIES);
121         boolean importPermissions = MapUtil.getBoolean(
122             parameterMap, PortletDataHandlerKeys.PERMISSIONS);
123         boolean importUserPermissions = MapUtil.getBoolean(
124             parameterMap, PortletDataHandlerKeys.PERMISSIONS);
125         boolean importPortletData = MapUtil.getBoolean(
126             parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
127         boolean importPortletSetup = MapUtil.getBoolean(
128             parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
129         boolean importPortletArchivedSetups = MapUtil.getBoolean(
130             parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
131         boolean importPortletUserPreferences = MapUtil.getBoolean(
132             parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
133         boolean importTheme = MapUtil.getBoolean(
134             parameterMap, PortletDataHandlerKeys.THEME);
135         String layoutsImportMode = MapUtil.getString(
136             parameterMap, PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE,
137             PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_ID);
138         String portletsMergeMode = MapUtil.getString(
139             parameterMap, PortletDataHandlerKeys.PORTLETS_MERGE_MODE,
140             PortletDataHandlerKeys.PORTLETS_MERGE_MODE_REPLACE);
141         String userIdStrategy = MapUtil.getString(
142             parameterMap, PortletDataHandlerKeys.USER_ID_STRATEGY);
143 
144         if (_log.isDebugEnabled()) {
145             _log.debug("Delete portlet data " + deletePortletData);
146             _log.debug("Import categories " + importCategories);
147             _log.debug("Import permissions " + importPermissions);
148             _log.debug("Import user permissions " + importUserPermissions);
149             _log.debug("Import portlet data " + importPortletData);
150             _log.debug("Import portlet setup " + importPortletSetup);
151             _log.debug(
152                 "Import portlet archived setups " +
153                     importPortletArchivedSetups);
154             _log.debug(
155                 "Import portlet user preferences " +
156                     importPortletUserPreferences);
157             _log.debug("Import theme " + importTheme);
158         }
159 
160         StopWatch stopWatch = null;
161 
162         if (_log.isInfoEnabled()) {
163             stopWatch = new StopWatch();
164 
165             stopWatch.start();
166         }
167 
168         LayoutCache layoutCache = new LayoutCache();
169 
170         LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
171             groupId, privateLayout);
172 
173         long companyId = layoutSet.getCompanyId();
174 
175         User user = UserUtil.findByPrimaryKey(userId);
176 
177         UserIdStrategy strategy = _portletImporter.getUserIdStrategy(
178             user, userIdStrategy);
179 
180         ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(file);
181 
182         PortletDataContext context = new PortletDataContextImpl(
183             companyId, groupId, parameterMap, new HashSet<String>(), strategy,
184             zipReader);
185 
186         context.setPrivateLayout(privateLayout);
187 
188         // Zip
189 
190         Element root = null;
191         InputStream themeZip = null;
192 
193         // Manifest
194 
195         String xml = context.getZipEntryAsString("/manifest.xml");
196 
197         if (xml == null) {
198             throw new LARFileException("manifest.xml not found in the LAR");
199         }
200 
201         try {
202             Document doc = SAXReaderUtil.read(xml);
203 
204             root = doc.getRootElement();
205         }
206         catch (Exception e) {
207             throw new LARFileException(e);
208         }
209 
210         // Build compatibility
211 
212         Element header = root.element("header");
213 
214         int buildNumber = ReleaseInfo.getBuildNumber();
215 
216         int importBuildNumber = GetterUtil.getInteger(
217             header.attributeValue("build-number"));
218 
219         if (buildNumber != importBuildNumber) {
220             throw new LayoutImportException(
221                 "LAR build number " + importBuildNumber + " does not match " +
222                     "portal build number " + buildNumber);
223         }
224 
225         // Type compatibility
226 
227         String larType = header.attributeValue("type");
228 
229         if (!larType.equals("layout-set")) {
230             throw new LARTypeException(
231                 "Invalid type of LAR file (" + larType + ")");
232         }
233 
234         // Import GroupId
235 
236         long sourceGroupId = GetterUtil.getLong(
237             header.attributeValue("group-id"));
238 
239         context.setSourceGroupId(sourceGroupId);
240 
241         // Look and feel
242 
243         if (importTheme) {
244             themeZip = context.getZipEntryAsInputStream("theme.zip");
245         }
246 
247         // Look and feel
248 
249         String themeId = header.attributeValue("theme-id");
250         String colorSchemeId = header.attributeValue("color-scheme-id");
251 
252         boolean useThemeZip = false;
253 
254         if (themeZip != null) {
255             try {
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             catch (Exception e) {
272                 throw new SystemException(e);
273             }
274         }
275 
276         boolean wapTheme = false;
277 
278         LayoutSetLocalServiceUtil.updateLookAndFeel(
279             groupId, privateLayout, themeId, colorSchemeId, StringPool.BLANK,
280             wapTheme);
281 
282         // Read categories, comments, ratings, and tags to make them available
283         // to the data handlers through the context
284 
285         if (importCategories) {
286             importCategories(context);
287         }
288 
289         _portletImporter.readCategories(context, root);
290         _portletImporter.readComments(context, root);
291 
292         if (importPermissions) {
293             _permissionImporter.readPortletDataPermissions(context);
294         }
295 
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             layout.setParentLayoutId(parentLayoutId);
471             layout.setName(name);
472             layout.setTitle(title);
473             layout.setDescription(description);
474             layout.setType(type);
475 
476             if (layout.isTypePortlet() &&
477                 Validator.isNotNull(layout.getTypeSettings()) &&
478                 !portletsMergeMode.equals(
479                     PortletDataHandlerKeys.PORTLETS_MERGE_MODE_REPLACE)) {
480 
481                 mergePortlets(layout, typeSettings, portletsMergeMode);
482             }
483             else {
484                 layout.setTypeSettings(typeSettings);
485             }
486 
487             layout.setHidden(hidden);
488             layout.setFriendlyURL(friendlyURL);
489 
490             if (iconImage) {
491                 layout.setIconImage(iconImage);
492 
493                 if (layout.isNew()) {
494                     long iconImageId = CounterLocalServiceUtil.increment();
495 
496                     layout.setIconImageId(iconImageId);
497                 }
498             }
499 
500             layout.setThemeId(themeId);
501             layout.setColorSchemeId(colorSchemeId);
502             layout.setWapThemeId(wapThemeId);
503             layout.setWapColorSchemeId(wapColorSchemeId);
504             layout.setCss(css);
505             layout.setPriority(priority);
506 
507             fixTypeSettings(layout);
508 
509             LayoutUtil.update(layout, false);
510 
511             if ((iconBytes != null) && (iconBytes.length > 0)) {
512                 ImageLocalServiceUtil.updateImage(
513                     layout.getIconImageId(), iconBytes);
514             }
515 
516             context.setPlid(layout.getPlid());
517             context.setOldPlid(oldPlid);
518 
519             newLayoutIdPlidMap.put(oldLayoutId, layout.getPlid());
520 
521             newLayoutIds.add(layoutId);
522 
523             newLayouts.add(layout);
524 
525             // Layout permissions
526 
527             if (importPermissions) {
528                 _permissionImporter.importLayoutPermissions(
529                     layoutCache, companyId, groupId, userId, layout, layoutEl,
530                     root, importUserPermissions);
531             }
532 
533             _portletImporter.importPortletData(
534                 context, PortletKeys.LAYOUT_CONFIGURATION, null, layoutEl);
535         }
536 
537         List<Element> portletEls = root.element("portlets").elements("portlet");
538 
539         // Delete portlet data
540 
541         if (deletePortletData) {
542             if (_log.isDebugEnabled()) {
543                 if (portletEls.size() > 0) {
544                     _log.debug("Deleting portlet data");
545                 }
546             }
547 
548             for (Element portletRefEl : portletEls) {
549                 String portletId = portletRefEl.attributeValue("portlet-id");
550                 long layoutId = GetterUtil.getLong(
551                     portletRefEl.attributeValue("layout-id"));
552                 long plid = newLayoutIdPlidMap.get(layoutId);
553 
554                 context.setPlid(plid);
555 
556                 _portletImporter.deletePortletData(context, portletId, plid);
557             }
558         }
559 
560         // Import portlets
561 
562         if (_log.isDebugEnabled()) {
563             if (portletEls.size() > 0) {
564                 _log.debug("Importing portlets");
565             }
566         }
567 
568         for (Element portletRefEl : portletEls) {
569             String portletPath = portletRefEl.attributeValue("path");
570             String portletId = portletRefEl.attributeValue("portlet-id");
571             long layoutId = GetterUtil.getLong(
572                 portletRefEl.attributeValue("layout-id"));
573             long plid = newLayoutIdPlidMap.get(layoutId);
574             long oldPlid = GetterUtil.getLong(
575                 portletRefEl.attributeValue("old-plid"));
576 
577             Layout layout = LayoutUtil.findByPrimaryKey(plid);
578 
579             context.setPlid(plid);
580             context.setOldPlid(oldPlid);
581 
582             Element portletEl = null;
583 
584             try {
585                 Document portletDoc = SAXReaderUtil.read(
586                     context.getZipEntryAsString(portletPath));
587 
588                 portletEl = portletDoc.getRootElement();
589             }
590             catch (DocumentException de) {
591                 throw new SystemException(de);
592             }
593 
594             // The order of the import is important. You must always import
595             // the portlet preferences first, then the portlet data, then
596             // the portlet permissions. The import of the portlet data
597             // assumes that portlet preferences already exist.
598 
599             // Portlet preferences
600 
601             _portletImporter.importPortletPreferences(
602                 context, layoutSet.getCompanyId(), layout.getGroupId(),
603                 layout, null, portletEl, importPortletSetup,
604                 importPortletArchivedSetups, importPortletUserPreferences,
605                 false);
606 
607             // Portlet data scope
608 
609             long scopeLayoutId = GetterUtil.getLong(
610                 portletEl.attributeValue("scope-layout-id"));
611 
612             context.setScopeLayoutId(scopeLayoutId);
613 
614             // Portlet data
615 
616             Element portletDataEl = portletEl.element("portlet-data");
617 
618             if (importPortletData && portletDataEl != null) {
619                 _portletImporter.importPortletData(
620                     context, portletId, plid, portletDataEl);
621             }
622 
623             // Portlet permissions
624 
625             if (importPermissions) {
626                 _permissionImporter.importPortletPermissions(
627                     layoutCache, companyId, groupId, userId, layout, portletEl,
628                     portletId, importUserPermissions);
629             }
630 
631             // Archived setups
632 
633             _portletImporter.importPortletPreferences(
634                 context, layoutSet.getCompanyId(), groupId, null, null,
635                 portletEl, importPortletSetup, importPortletArchivedSetups,
636                 importPortletUserPreferences, false);
637         }
638 
639         // Delete missing layouts
640 
641         if (deleteMissingLayouts) {
642             deleteMissingLayouts(
643                 groupId, privateLayout, newLayoutIds, previousLayouts);
644         }
645 
646         // Page count
647 
648         LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
649 
650         if (_log.isInfoEnabled()) {
651             _log.info("Importing layouts takes " + stopWatch.getTime() + " ms");
652         }
653 
654         // Web content layout type
655 
656         for (Layout layout : newLayouts) {
657             UnicodeProperties typeSettingsProperties =
658                 layout.getTypeSettingsProperties();
659 
660             String articleId = typeSettingsProperties.getProperty("article-id");
661 
662             if (Validator.isNotNull(articleId)) {
663                 Map<String, String> articleIds =
664                     (Map<String, String>)context.getNewPrimaryKeysMap(
665                         JournalArticle.class);
666 
667                 typeSettingsProperties.setProperty(
668                     "article-id",
669                     MapUtil.getString(articleIds, articleId, articleId));
670 
671                 LayoutUtil.update(layout, false);
672             }
673         }
674 
675         zipReader.close();
676     }
677 
678     protected String[] appendPortletIds(
679         String[] portletIds, String[] newPortletIds, String portletsMergeMode) {
680 
681         for (String portletId : newPortletIds) {
682             if (ArrayUtil.contains(portletIds, portletId)) {
683                 continue;
684             }
685 
686             if (portletsMergeMode.equals(
687                     PortletDataHandlerKeys.PORTLETS_MERGE_MODE_ADD_TO_BOTTOM)) {
688 
689                 portletIds = ArrayUtil.append(portletIds, portletId);
690             }
691             else {
692                 portletIds = ArrayUtil.append(
693                     new String[] {portletId}, portletIds);
694             }
695         }
696 
697         return portletIds;
698     }
699 
700     protected void deleteMissingLayouts(
701             long groupId, boolean privateLayout, Set<Long> newLayoutIds,
702             List<Layout> previousLayouts)
703         throws PortalException, SystemException {
704 
705         // Layouts
706 
707         if (_log.isDebugEnabled()) {
708             if (newLayoutIds.size() > 0) {
709                 _log.debug("Delete missing layouts");
710             }
711         }
712 
713         for (Layout layout : previousLayouts) {
714             if (!newLayoutIds.contains(layout.getLayoutId())) {
715                 try {
716                     LayoutLocalServiceUtil.deleteLayout(layout, false);
717                 }
718                 catch (NoSuchLayoutException nsle) {
719                 }
720             }
721         }
722 
723         // Layout set
724 
725         LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
726     }
727 
728     protected void fixTypeSettings(Layout layout)
729         throws PortalException, SystemException {
730 
731         if (!layout.isTypeURL()) {
732             return;
733         }
734 
735         UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
736 
737         String url = GetterUtil.getString(typeSettings.getProperty("url"));
738 
739         String friendlyURLPrivateGroupPath =
740             PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
741         String friendlyURLPrivateUserPath =
742             PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
743         String friendlyURLPublicPath =
744             PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
745 
746         if (!url.startsWith(friendlyURLPrivateGroupPath) &&
747             !url.startsWith(friendlyURLPrivateUserPath) &&
748             !url.startsWith(friendlyURLPublicPath)) {
749 
750             return;
751         }
752 
753         int x = url.indexOf(StringPool.SLASH, 1);
754 
755         if (x == -1) {
756             return;
757         }
758 
759         int y = url.indexOf(StringPool.SLASH, x + 1);
760 
761         if (y == -1) {
762             return;
763         }
764 
765         String friendlyURL = url.substring(x, y);
766 
767         if (!friendlyURL.equals(LayoutExporter.SAME_GROUP_FRIENDLY_URL)) {
768             return;
769         }
770 
771         typeSettings.setProperty(
772             "url",
773             url.substring(0, x) + layout.getGroup().getFriendlyURL() +
774                 url.substring(y));
775     }
776 
777     protected AssetVocabulary getAssetVocabulary(
778             PortletDataContext context, String vocabularyUuid,
779             String vocabularyName, String userUuid,
780             ServiceContext serviceContext)
781         throws Exception {
782 
783         AssetVocabulary assetVocabulary = null;
784 
785         try {
786             if (context.getDataStrategy().equals(
787                     PortletDataHandlerKeys.DATA_STRATEGY_MIRROR)) {
788 
789                 AssetVocabulary existingAssetVocabulary =
790                     AssetVocabularyUtil.fetchByUUID_G(
791                         vocabularyUuid, context.getGroupId());
792 
793                 if (existingAssetVocabulary == null) {
794                     Map<Locale, String> titleMap =
795                         new HashMap<Locale, String>();
796 
797                     titleMap.put(LocaleUtil.getDefault(), vocabularyName);
798 
799                     assetVocabulary =
800                         AssetVocabularyLocalServiceUtil.addVocabulary(
801                             vocabularyUuid, context.getUserId(userUuid),
802                             titleMap, null, StringPool.BLANK, serviceContext);
803                 }
804                 else {
805                     assetVocabulary =
806                         AssetVocabularyLocalServiceUtil.updateVocabulary(
807                             existingAssetVocabulary.getVocabularyId(),
808                             existingAssetVocabulary.getTitleMap(),
809                             existingAssetVocabulary.getDescriptionMap(),
810                             existingAssetVocabulary.getSettings(),
811                             serviceContext);
812                 }
813             }
814             else {
815                 Map<Locale, String> titleMap =  new HashMap<Locale, String>();
816 
817                 titleMap.put(LocaleUtil.getDefault(), vocabularyName);
818 
819                 assetVocabulary = AssetVocabularyLocalServiceUtil.addVocabulary(
820                     null, context.getUserId(userUuid), titleMap, null,
821                     StringPool.BLANK, serviceContext);
822             }
823         }
824         catch (DuplicateVocabularyException dve) {
825             assetVocabulary =
826                 AssetVocabularyLocalServiceUtil.getGroupVocabulary(
827                     context.getGroupId(), vocabularyName);
828         }
829 
830         return assetVocabulary;
831     }
832 
833     protected void importCategories(PortletDataContext context)
834         throws SystemException {
835 
836         try {
837             String xml = context.getZipEntryAsString(
838                 context.getSourceRootPath() + "/categories-hierarchy.xml");
839 
840             if (Validator.isNull(xml)) {
841                 return;
842             }
843 
844             Document doc = SAXReaderUtil.read(xml);
845 
846             Element root = doc.getRootElement();
847 
848             List<Element> vocabularies = root.elements("vocabulary");
849 
850             for (Element vocabularyEl : vocabularies) {
851                 String vocabularyUuid = GetterUtil.getString(
852                     vocabularyEl.attributeValue("uuid"));
853                 String vocabularyName = GetterUtil.getString(
854                     vocabularyEl.attributeValue("name"));
855                 String userUuid = GetterUtil.getString(
856                     vocabularyEl.attributeValue("userUuid"));
857 
858                 ServiceContext serviceContext = new ServiceContext();
859 
860                 serviceContext.setAddCommunityPermissions(true);
861                 serviceContext.setAddGuestPermissions(true);
862                 serviceContext.setScopeGroupId(context.getGroupId());
863 
864                 AssetVocabulary assetVocabulary = getAssetVocabulary(
865                     context, vocabularyUuid, vocabularyName, userUuid,
866                     serviceContext);
867 
868                 List<Element> categories = vocabularyEl.elements("category");
869 
870                 for (Element category : categories) {
871                     String categoryUuid = GetterUtil.getString(
872                         category.attributeValue("uuid"));
873                     String parentCategoryUuid = GetterUtil.getString(
874                         category.attributeValue("parentCategoryUuid"));
875                     String categoryName = GetterUtil.getString(
876                         category.attributeValue("name"));
877                     String[] properties = null;
878 
879                     try {
880                         importCategory(
881                             context, categoryUuid, userUuid, parentCategoryUuid,
882                             categoryName, assetVocabulary.getVocabularyId(),
883                             properties, serviceContext);
884                     }
885                     catch (DuplicateCategoryException dce) {
886                     }
887                     catch (NoSuchCategoryException nsce) {
888                     }
889                 }
890             }
891         }
892         catch (Exception e) {
893             throw new SystemException(e);
894         }
895     }
896 
897     protected void importCategory(
898             PortletDataContext context, String categoryUuid, String userUuid,
899             String parentCategoryUuid, String categoryName, long vocabularyId,
900             String[] properties, ServiceContext serviceContext)
901         throws PortalException, SystemException {
902 
903         long parentCategoryId = 0;
904 
905         if (Validator.isNotNull(parentCategoryUuid)) {
906             AssetCategory parentCategory = AssetCategoryUtil.findByUUID_G(
907                 parentCategoryUuid,context.getScopeGroupId());
908 
909             parentCategoryId = parentCategory.getCategoryId();
910         }
911 
912         if (context.getDataStrategy().equals(
913                 PortletDataHandlerKeys.DATA_STRATEGY_MIRROR)) {
914 
915             AssetCategory existingAssetCategory =
916                 AssetCategoryUtil.fetchByUUID_G(
917                     categoryUuid, context.getGroupId());
918 
919             if (existingAssetCategory == null) {
920                 Map<Locale, String> titleMap =  new HashMap<Locale, String>();
921 
922                 titleMap.put(LocaleUtil.getDefault(), categoryName);
923 
924                 AssetCategoryLocalServiceUtil.addCategory(
925                     categoryUuid, context.getUserId(userUuid), parentCategoryId,
926                     titleMap, vocabularyId, properties, serviceContext);
927             }
928             else {
929                 AssetCategoryLocalServiceUtil.updateCategory(
930                     context.getUserId(userUuid),
931                     existingAssetCategory.getCategoryId(), parentCategoryId,
932                     existingAssetCategory.getTitleMap(), vocabularyId,
933                     properties, serviceContext);
934             }
935         }
936         else {
937             Map<Locale, String> titleMap =  new HashMap<Locale, String>();
938 
939             titleMap.put(LocaleUtil.getDefault(), categoryName);
940 
941             AssetCategoryLocalServiceUtil.addCategory(
942                 null, context.getUserId(userUuid), parentCategoryId, titleMap,
943                 vocabularyId, properties, serviceContext);
944         }
945     }
946 
947     protected String importTheme(LayoutSet layoutSet, InputStream themeZip)
948         throws IOException {
949 
950         ThemeLoader themeLoader = ThemeLoaderFactory.getDefaultThemeLoader();
951 
952         if (themeLoader == null) {
953             _log.error("No theme loaders are deployed");
954 
955             return null;
956         }
957 
958         ZipReader zipReader = ZipReaderFactoryUtil.getZipReader(themeZip);
959 
960         String lookAndFeelXML = zipReader.getEntryAsString(
961             "liferay-look-and-feel.xml");
962 
963         String themeId = String.valueOf(layoutSet.getGroupId());
964 
965         if (layoutSet.isPrivateLayout()) {
966             themeId += "-private";
967         }
968         else {
969             themeId += "-public";
970         }
971 
972         if (PropsValues.THEME_LOADER_NEW_THEME_ID_ON_IMPORT) {
973             Date now = new Date();
974 
975             themeId += "-" + Time.getShortTimestamp(now);
976         }
977 
978         String themeName = themeId;
979 
980         lookAndFeelXML = StringUtil.replace(
981             lookAndFeelXML,
982             new String[] {
983                 "[$GROUP_ID$]", "[$THEME_ID$]", "[$THEME_NAME$]"
984             },
985             new String[] {
986                 String.valueOf(layoutSet.getGroupId()), themeId, themeName
987             }
988         );
989 
990         FileUtil.deltree(
991             themeLoader.getFileStorage() + StringPool.SLASH + themeId);
992 
993         List<String> zipEntries = zipReader.getEntries();
994 
995         for (String zipEntry : zipEntries) {
996             String key = zipEntry;
997 
998             if (key.contains(StringPool.SLASH)) {
999                 key = key.substring(key.lastIndexOf(StringPool.SLASH));
1000            }
1001
1002            if (key.equals("liferay-look-and-feel.xml")) {
1003                FileUtil.write(
1004                    themeLoader.getFileStorage() + StringPool.SLASH + themeId +
1005                        StringPool.SLASH + key,
1006                    lookAndFeelXML.getBytes());
1007            }
1008            else {
1009                InputStream is = zipReader.getEntryAsInputStream(zipEntry);
1010
1011                FileUtil.write(
1012                    themeLoader.getFileStorage() + StringPool.SLASH + themeId +
1013                        StringPool.SLASH + key,
1014                    is);
1015            }
1016        }
1017
1018        themeLoader.loadThemes();
1019
1020        MethodWrapper methodWrapper = new MethodWrapper(
1021            ThemeLoaderFactory.class.getName(), "loadThemes");
1022
1023        Message message = new Message();
1024        message.setPayload(methodWrapper);
1025
1026        ClusterLinkUtil.sendMulticastMessage(message, Priority.LEVEL5);
1027
1028        themeId +=
1029            PortletConstants.WAR_SEPARATOR +
1030                themeLoader.getServletContextName();
1031
1032        return PortalUtil.getJsSafePortletId(themeId);
1033    }
1034
1035    protected void mergePortlets(
1036        Layout layout, String newTypeSettings, String portletsMergeMode) {
1037
1038        try {
1039            UnicodeProperties previousProps =
1040                layout.getTypeSettingsProperties();
1041            LayoutTypePortlet previousLayoutType =
1042                (LayoutTypePortlet)layout.getLayoutType();
1043            List<String> previousColumns =
1044                previousLayoutType.getLayoutTemplate().getColumns();
1045
1046            UnicodeProperties newProps = new UnicodeProperties(true);
1047
1048            newProps.load(newTypeSettings);
1049
1050            String layoutTemplateId = newProps.getProperty(
1051                    LayoutTypePortletConstants.LAYOUT_TEMPLATE_ID);
1052
1053            LayoutTemplate newLayoutTemplate =
1054                LayoutTemplateLocalServiceUtil.getLayoutTemplate(
1055                    layoutTemplateId, false, null);
1056
1057            String[] lostPortletIds = new String[0];
1058
1059            for (String columnId : newLayoutTemplate.getColumns()) {
1060                String columnValue =
1061                    newProps.getProperty(columnId);
1062
1063                String[] portletIds = StringUtil.split(columnValue);
1064
1065                if (!previousColumns.contains(columnId)) {
1066                    lostPortletIds = ArrayUtil.append(
1067                        lostPortletIds, portletIds);
1068                }
1069                else {
1070
1071                    String[] previousPortletIds = StringUtil.split(
1072                        previousProps.getProperty(columnId));
1073
1074                    portletIds = appendPortletIds(
1075                        previousPortletIds, portletIds, portletsMergeMode);
1076
1077                    previousProps.setProperty(
1078                        columnId, StringUtil.merge(portletIds));
1079                }
1080            }
1081
1082            // Add portlets in non-existent column to the first column
1083
1084            String columnId = previousColumns.get(0);
1085
1086            String[] portletIds = StringUtil.split(
1087                previousProps.getProperty(columnId));
1088
1089            appendPortletIds(portletIds, lostPortletIds, portletsMergeMode);
1090
1091            previousProps.setProperty(
1092                columnId, StringUtil.merge(portletIds));
1093
1094            layout.setTypeSettings(previousProps.toString());
1095
1096        }
1097        catch (IOException e) {
1098            layout.setTypeSettings(newTypeSettings);
1099        }
1100    }
1101
1102    private static Log _log = LogFactoryUtil.getLog(LayoutImporter.class);
1103
1104    private PermissionImporter _permissionImporter = new PermissionImporter();
1105    private PortletImporter _portletImporter = new PortletImporter();
1106
1107}