1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.lar;
24  
25  import com.liferay.counter.service.CounterLocalServiceUtil;
26  import com.liferay.portal.LARFileException;
27  import com.liferay.portal.LARTypeException;
28  import com.liferay.portal.LayoutImportException;
29  import com.liferay.portal.NoSuchLayoutException;
30  import com.liferay.portal.PortalException;
31  import com.liferay.portal.SystemException;
32  import com.liferay.portal.comm.CommLink;
33  import com.liferay.portal.kernel.util.ArrayUtil;
34  import com.liferay.portal.kernel.util.FileUtil;
35  import com.liferay.portal.kernel.util.GetterUtil;
36  import com.liferay.portal.kernel.util.LocaleUtil;
37  import com.liferay.portal.kernel.util.MethodWrapper;
38  import com.liferay.portal.kernel.util.ReleaseInfo;
39  import com.liferay.portal.kernel.util.StringPool;
40  import com.liferay.portal.kernel.util.StringUtil;
41  import com.liferay.portal.kernel.util.Time;
42  import com.liferay.portal.kernel.util.UnicodeProperties;
43  import com.liferay.portal.kernel.util.Validator;
44  import com.liferay.portal.kernel.xml.Document;
45  import com.liferay.portal.kernel.xml.DocumentException;
46  import com.liferay.portal.kernel.xml.Element;
47  import com.liferay.portal.kernel.xml.SAXReaderUtil;
48  import com.liferay.portal.kernel.zip.ZipReader;
49  import com.liferay.portal.model.Group;
50  import com.liferay.portal.model.GroupConstants;
51  import com.liferay.portal.model.Layout;
52  import com.liferay.portal.model.LayoutConstants;
53  import com.liferay.portal.model.LayoutSet;
54  import com.liferay.portal.model.LayoutTemplate;
55  import com.liferay.portal.model.LayoutTypePortlet;
56  import com.liferay.portal.model.Portlet;
57  import com.liferay.portal.model.PortletConstants;
58  import com.liferay.portal.model.Resource;
59  import com.liferay.portal.model.ResourceConstants;
60  import com.liferay.portal.model.Role;
61  import com.liferay.portal.model.User;
62  import com.liferay.portal.model.impl.ColorSchemeImpl;
63  import com.liferay.portal.model.impl.LayoutTypePortletImpl;
64  import com.liferay.portal.service.GroupLocalServiceUtil;
65  import com.liferay.portal.service.ImageLocalServiceUtil;
66  import com.liferay.portal.service.LayoutLocalServiceUtil;
67  import com.liferay.portal.service.LayoutSetLocalServiceUtil;
68  import com.liferay.portal.service.LayoutTemplateLocalServiceUtil;
69  import com.liferay.portal.service.PermissionLocalServiceUtil;
70  import com.liferay.portal.service.PortletLocalServiceUtil;
71  import com.liferay.portal.service.permission.PortletPermissionUtil;
72  import com.liferay.portal.service.persistence.LayoutUtil;
73  import com.liferay.portal.service.persistence.UserUtil;
74  import com.liferay.portal.theme.ThemeLoader;
75  import com.liferay.portal.theme.ThemeLoaderFactory;
76  import com.liferay.portal.util.PortalUtil;
77  import com.liferay.portal.util.PortletKeys;
78  import com.liferay.portal.util.PropsValues;
79  import com.liferay.util.LocalizationUtil;
80  import com.liferay.util.MapUtil;
81  
82  import java.io.ByteArrayInputStream;
83  import java.io.IOException;
84  import java.io.InputStream;
85  
86  import java.util.ArrayList;
87  import java.util.Date;
88  import java.util.HashSet;
89  import java.util.Iterator;
90  import java.util.List;
91  import java.util.Locale;
92  import java.util.Map;
93  import java.util.Set;
94  
95  import org.apache.commons.lang.time.StopWatch;
96  import org.apache.commons.logging.Log;
97  import org.apache.commons.logging.LogFactory;
98  
99  /**
100  * <a href="LayoutImporter.java.html"><b><i>View Source</i></b></a>
101  *
102  * @author Brian Wing Shun Chan
103  * @author Joel Kozikowski
104  * @author Charles May
105  * @author Raymond Augé
106  * @author Jorge Ferrer
107  * @author Bruno Farache
108  *
109  */
110 public class LayoutImporter {
111 
112     public void importLayouts(
113             long userId, long groupId, boolean privateLayout,
114             Map<String, String[]> parameterMap, InputStream is)
115         throws PortalException, SystemException {
116 
117         boolean deleteMissingLayouts = MapUtil.getBoolean(
118             parameterMap, PortletDataHandlerKeys.DELETE_MISSING_LAYOUTS,
119             Boolean.TRUE.booleanValue());
120         boolean deletePortletData = MapUtil.getBoolean(
121             parameterMap, PortletDataHandlerKeys.DELETE_PORTLET_DATA);
122         boolean importPermissions = MapUtil.getBoolean(
123             parameterMap, PortletDataHandlerKeys.PERMISSIONS);
124         boolean importUserPermissions = MapUtil.getBoolean(
125             parameterMap, PortletDataHandlerKeys.PERMISSIONS);
126         boolean importPortletData = MapUtil.getBoolean(
127             parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
128         boolean importPortletSetup = MapUtil.getBoolean(
129             parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
130         boolean importPortletArchivedSetups = MapUtil.getBoolean(
131             parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
132         boolean importPortletUserPreferences = MapUtil.getBoolean(
133             parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
134         boolean importTheme = MapUtil.getBoolean(
135             parameterMap, PortletDataHandlerKeys.THEME);
136         String layoutsImportMode = MapUtil.getString(
137             parameterMap, PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE,
138             PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_ID);
139         String portletsMergeMode = MapUtil.getString(
140             parameterMap, PortletDataHandlerKeys.PORTLETS_MERGE_MODE,
141             PortletDataHandlerKeys.PORTLETS_MERGE_MODE_REPLACE);
142         String userIdStrategy = MapUtil.getString(
143             parameterMap, PortletDataHandlerKeys.USER_ID_STRATEGY);
144 
145         if (_log.isDebugEnabled()) {
146             _log.debug("Delete portlet data " + deletePortletData);
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 = new ZipReader(is);
181 
182         PortletDataContext context = new PortletDataContextImpl(
183             companyId, groupId, parameterMap, new HashSet<String>(), strategy,
184             zipReader);
185 
186         Group guestGroup = GroupLocalServiceUtil.getGroup(
187             companyId, GroupConstants.GUEST);
188 
189         // Zip
190 
191         Element root = null;
192         byte[] 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 importGroupId = GetterUtil.getLong(
238             header.attributeValue("group-id"));
239 
240         context.setImportGroupId(importGroupId);
241 
242         // Look and feel
243 
244         if (importTheme) {
245             themeZip = context.getZipEntryAsByteArray("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 comments, ratings, and tags to make them available to the data
284         // handlers through the context
285 
286         _portletImporter.readComments(context, root);
287         _portletImporter.readRatings(context, root);
288         _portletImporter.readTags(context, root);
289 
290         // Layouts
291 
292         List<Layout> previousLayouts = LayoutUtil.findByG_P(
293             groupId, privateLayout);
294 
295         Set<Long> newLayoutIds = new HashSet<Long>();
296 
297         Map<Long, Long> newLayoutIdPlidMap =
298             (Map<Long, Long>)context.getNewPrimaryKeysMap(Layout.class);
299 
300         List<Element> layoutEls = root.element("layouts").elements("layout");
301 
302         if (_log.isDebugEnabled()) {
303             if (layoutEls.size() > 0) {
304                 _log.debug("Importing layouts");
305             }
306         }
307 
308         for (Element layoutRefEl : layoutEls) {
309             long layoutId = GetterUtil.getInteger(
310                 layoutRefEl.attributeValue("layout-id"));
311 
312             long oldLayoutId = layoutId;
313 
314             String layoutPath = layoutRefEl.attributeValue("path");
315 
316             Element layoutEl = null;
317 
318             try {
319                 Document layoutDoc = SAXReaderUtil.read(
320                     context.getZipEntryAsString(layoutPath));
321 
322                 layoutEl = layoutDoc.getRootElement();
323             }
324             catch (DocumentException de) {
325                 throw new SystemException(de);
326             }
327 
328             long parentLayoutId = GetterUtil.getInteger(
329                 layoutEl.elementText("parent-layout-id"));
330 
331             if (_log.isDebugEnabled()) {
332                 _log.debug(
333                     "Importing layout with layout id " + layoutId +
334                         " and parent layout id " + parentLayoutId);
335             }
336 
337             long oldPlid = GetterUtil.getInteger(
338                 layoutEl.attributeValue("old-plid"));
339 
340             String name = layoutEl.elementText("name");
341             String title = layoutEl.elementText("title");
342             String description = layoutEl.elementText("description");
343             String type = layoutEl.elementText("type");
344             String typeSettings = layoutEl.elementText("type-settings");
345             boolean hidden = GetterUtil.getBoolean(
346                 layoutEl.elementText("hidden"));
347             String friendlyURL = layoutEl.elementText("friendly-url");
348             boolean iconImage = GetterUtil.getBoolean(
349                 layoutEl.elementText("icon-image"));
350 
351             byte[] iconBytes = null;
352 
353             if (iconImage) {
354                 String path = layoutEl.elementText("icon-image-path");
355 
356                 iconBytes = context.getZipEntryAsByteArray(path);
357             }
358 
359             if (useThemeZip) {
360                 themeId = StringPool.BLANK;
361                 colorSchemeId = StringPool.BLANK;
362             }
363             else {
364                 themeId = layoutEl.elementText("theme-id");
365                 colorSchemeId = layoutEl.elementText("color-scheme-id");
366             }
367 
368             String wapThemeId = layoutEl.elementText("wap-theme-id");
369             String wapColorSchemeId = layoutEl.elementText(
370                 "wap-color-scheme-id");
371             String css = layoutEl.elementText("css");
372             int priority = GetterUtil.getInteger(
373                 layoutEl.elementText("priority"));
374 
375             Layout layout = null;
376 
377             if (layoutsImportMode.equals(
378                     PortletDataHandlerKeys.LAYOUTS_IMPORT_MODE_ADD_AS_NEW)) {
379 
380                 layoutId = LayoutLocalServiceUtil.getNextLayoutId(
381                     groupId, privateLayout);
382                 friendlyURL = StringPool.SLASH + layoutId;
383             }
384             else if (layoutsImportMode.equals(
385                     PortletDataHandlerKeys.
386                         LAYOUTS_IMPORT_MODE_MERGE_BY_LAYOUT_NAME)) {
387 
388                 Locale locale = LocaleUtil.getDefault();
389 
390                 String localizedName = LocalizationUtil.getLocalization(
391                     name, LocaleUtil.toLanguageId(locale));
392 
393                 for (Layout curLayout : previousLayouts) {
394                     if (curLayout.getName(locale).equals(localizedName)) {
395                         layout = curLayout;
396                         break;
397                     }
398                 }
399             }
400             else {
401                 layout = LayoutUtil.fetchByG_P_L(
402                     groupId, privateLayout, layoutId);
403             }
404 
405             if (_log.isDebugEnabled()) {
406                 if (layout == null) {
407                     _log.debug(
408                         "Layout with {groupId=" + groupId + ",privateLayout=" +
409                             privateLayout + ",layoutId=" + layoutId +
410                                 "} does not exist");
411                 }
412                 else {
413                     _log.debug(
414                         "Layout with {groupId=" + groupId + ",privateLayout=" +
415                             privateLayout + ",layoutId=" + layoutId +
416                                 "} exists");
417                 }
418             }
419 
420             if (layout == null) {
421                 long plid = CounterLocalServiceUtil.increment();
422 
423                 layout = LayoutUtil.create(plid);
424 
425                 layout.setGroupId(groupId);
426                 layout.setPrivateLayout(privateLayout);
427                 layout.setLayoutId(layoutId);
428             }
429 
430             layout.setCompanyId(user.getCompanyId());
431             layout.setParentLayoutId(parentLayoutId);
432             layout.setName(name);
433             layout.setTitle(title);
434             layout.setDescription(description);
435             layout.setType(type);
436 
437             if (layout.getType().equals(LayoutConstants.TYPE_PORTLET) &&
438                     Validator.isNotNull(layout.getTypeSettings()) &&
439                         !portletsMergeMode.equals(
440                             PortletDataHandlerKeys.
441                                 PORTLETS_MERGE_MODE_REPLACE)) {
442                 mergePortlets(layout, typeSettings, portletsMergeMode);
443             }
444             else {
445                 layout.setTypeSettings(typeSettings);
446             }
447 
448             layout.setHidden(hidden);
449             layout.setFriendlyURL(friendlyURL);
450 
451             if (iconImage) {
452                 layout.setIconImage(iconImage);
453 
454                 if (layout.isNew()) {
455                     long iconImageId = CounterLocalServiceUtil.increment();
456 
457                     layout.setIconImageId(iconImageId);
458                 }
459             }
460 
461             layout.setThemeId(themeId);
462             layout.setColorSchemeId(colorSchemeId);
463             layout.setWapThemeId(wapThemeId);
464             layout.setWapColorSchemeId(wapColorSchemeId);
465             layout.setCss(css);
466             layout.setPriority(priority);
467 
468             fixTypeSettings(layout);
469 
470             LayoutUtil.update(layout, false);
471 
472             if ((iconBytes != null) && (iconBytes.length > 0)) {
473                 ImageLocalServiceUtil.updateImage(
474                     layout.getIconImageId(), iconBytes);
475             }
476 
477             context.setPlid(layout.getPlid());
478             context.setOldPlid(oldPlid);
479 
480             newLayoutIdPlidMap.put(oldLayoutId, layout.getPlid());
481 
482             newLayoutIds.add(layoutId);
483 
484             Element permissionsEl = layoutEl.element("permissions");
485 
486             // Layout permissions
487 
488             if (importPermissions) {
489                 importLayoutPermissions(
490                     layoutCache, companyId, groupId, guestGroup, layout,
491                     permissionsEl, importUserPermissions);
492             }
493 
494             _portletImporter.importPortletData(
495                 context, PortletKeys.LAYOUT_CONFIGURATION, null, layoutEl);
496         }
497 
498         List<Element> portletEls = root.element("portlets").elements("portlet");
499 
500         // Delete portlet data
501 
502         if (deletePortletData) {
503             if (_log.isDebugEnabled()) {
504                 if (portletEls.size() > 0) {
505                     _log.debug("Deleting portlet data");
506                 }
507             }
508 
509             for (Element portletRefEl : portletEls) {
510                 String portletId = portletRefEl.attributeValue("portlet-id");
511                 long layoutId = GetterUtil.getLong(
512                     portletRefEl.attributeValue("layout-id"));
513                 long plid = newLayoutIdPlidMap.get(layoutId);
514 
515                 context.setPlid(plid);
516 
517                 _portletImporter.deletePortletData(context, portletId, plid);
518             }
519         }
520 
521         // Import portlets
522 
523         if (_log.isDebugEnabled()) {
524             if (portletEls.size() > 0) {
525                 _log.debug("Importing portlets");
526             }
527         }
528 
529         for (Element portletRefEl : portletEls) {
530             String portletPath = portletRefEl.attributeValue("path");
531             String portletId = portletRefEl.attributeValue("portlet-id");
532             long layoutId = GetterUtil.getLong(
533                 portletRefEl.attributeValue("layout-id"));
534             long plid = newLayoutIdPlidMap.get(layoutId);
535             long oldPlid = GetterUtil.getLong(
536                 portletRefEl.attributeValue("old-plid"));
537 
538             Layout layout = LayoutUtil.findByPrimaryKey(plid);
539 
540             context.setPlid(plid);
541             context.setOldPlid(oldPlid);
542 
543             Element portletEl = null;
544 
545             try {
546                 Document portletDoc = SAXReaderUtil.read(
547                     context.getZipEntryAsString(portletPath));
548 
549                 portletEl = portletDoc.getRootElement();
550             }
551             catch (DocumentException de) {
552                 throw new SystemException(de);
553             }
554 
555             // The order of the import is important. You must always import
556             // the portlet preferences first, then the portlet data, then
557             // the portlet permissions. The import of the portlet data
558             // assumes that portlet preferences already exist.
559 
560             // Portlet preferences
561 
562             _portletImporter.importPortletPreferences(
563                 context, layoutSet.getCompanyId(), layout.getGroupId(),
564                 layout.getPlid(), null, portletEl, importPortletSetup,
565                 importPortletArchivedSetups, importPortletUserPreferences);
566 
567             // Portlet data
568 
569             Element portletDataEl = portletEl.element("portlet-data");
570 
571             if (importPortletData && portletDataEl != null) {
572                 _portletImporter.importPortletData(
573                     context, portletId, plid, portletDataEl);
574             }
575 
576             // Portlet permissions
577 
578             Element permissionsEl = portletEl.element("permissions");
579 
580             if (importPermissions && (permissionsEl != null)) {
581                 importPortletPermissions(
582                     layoutCache, companyId, groupId, guestGroup, layout,
583                     permissionsEl, importUserPermissions);
584             }
585 
586             // Archived setups
587 
588             _portletImporter.importPortletPreferences(
589                 context, layoutSet.getCompanyId(), groupId, 0, null, portletEl,
590                 importPortletSetup, importPortletArchivedSetups,
591                 importPortletUserPreferences);
592 
593             // Portlet roles
594 
595             Element rolesEl = portletEl.element("roles");
596 
597             if (importPermissions && (rolesEl != null)) {
598                 importPortletRoles(layoutCache, companyId, groupId, portletEl);
599                 importPortletRoles(
600                     layoutCache, companyId, groupId, portletId, rolesEl);
601             }
602         }
603 
604         Element rolesEl = root.element("roles");
605 
606         // Layout roles
607 
608         if (importPermissions) {
609             importLayoutRoles(layoutCache, companyId, groupId, rolesEl);
610         }
611 
612         // Delete missing layouts
613 
614         if (deleteMissingLayouts) {
615             deleteMissingLayouts(
616                 groupId, privateLayout, newLayoutIds, previousLayouts);
617         }
618 
619         // Page count
620 
621         LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
622 
623         if (_log.isInfoEnabled()) {
624             _log.info("Importing layouts takes " + stopWatch.getTime() + " ms");
625         }
626     }
627 
628     protected void deleteMissingLayouts(
629             long groupId, boolean privateLayout, Set<Long> newLayoutIds,
630             List<Layout> previousLayouts)
631         throws PortalException, SystemException {
632 
633         // Layouts
634 
635         if (_log.isDebugEnabled()) {
636             if (newLayoutIds.size() > 0) {
637                 _log.debug("Delete missing layouts");
638             }
639         }
640 
641         for (Layout layout : previousLayouts) {
642             if (!newLayoutIds.contains(layout.getLayoutId())) {
643                 try {
644                     LayoutLocalServiceUtil.deleteLayout(layout, false);
645                 }
646                 catch (NoSuchLayoutException nsle) {
647                 }
648             }
649         }
650 
651         // Layout set
652 
653         LayoutSetLocalServiceUtil.updatePageCount(groupId, privateLayout);
654     }
655 
656     protected void fixTypeSettings(Layout layout) {
657         if (layout.getType().equals(LayoutConstants.TYPE_URL)) {
658             UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
659 
660             String url = GetterUtil.getString(typeSettings.getProperty("url"));
661 
662             String friendlyURLPrivateGroupPath =
663                 PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
664             String friendlyURLPrivateUserPath =
665                 PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
666             String friendlyURLPublicPath =
667                 PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
668 
669             if (url.startsWith(friendlyURLPrivateGroupPath) ||
670                 url.startsWith(friendlyURLPrivateUserPath) ||
671                 url.startsWith(friendlyURLPublicPath)) {
672 
673                 int x = url.indexOf(StringPool.SLASH, 1);
674 
675                 if (x > 0) {
676                     int y = url.indexOf(StringPool.SLASH, x + 1);
677 
678                     if (y > x) {
679                         String fixedUrl = url.substring(0, x) +
680 
681                         layout.getGroup().getFriendlyURL() +
682 
683                         url.substring(y);
684 
685                         typeSettings.setProperty("url", fixedUrl);
686                     }
687                 }
688             }
689         }
690     }
691 
692     protected List<String> getActions(Element el) {
693         List<String> actions = new ArrayList<String>();
694 
695         Iterator<Element> itr = el.elements("action-key").iterator();
696 
697         while (itr.hasNext()) {
698             Element actionEl = itr.next();
699 
700             actions.add(actionEl.getText());
701         }
702 
703         return actions;
704     }
705 
706     protected void importGroupPermissions(
707             LayoutCache layoutCache, long companyId, long groupId,
708             String resourceName, String resourcePrimKey, Element parentEl,
709             String elName, boolean portletActions)
710         throws PortalException, SystemException {
711 
712         Element actionEl = parentEl.element(elName);
713 
714         if (actionEl == null) {
715             return;
716         }
717 
718         List<String> actions = getActions(actionEl);
719 
720         Resource resource = layoutCache.getResource(
721             companyId, groupId, resourceName,
722             ResourceConstants.SCOPE_INDIVIDUAL, resourcePrimKey,
723             portletActions);
724 
725         PermissionLocalServiceUtil.setGroupPermissions(
726             groupId, actions.toArray(new String[actions.size()]),
727             resource.getResourceId());
728     }
729 
730     protected void importGroupRoles(
731             LayoutCache layoutCache, long companyId, long groupId,
732             String resourceName, String entityName,
733             Element parentEl)
734         throws PortalException, SystemException {
735 
736         Element entityRolesEl = parentEl.element(entityName + "-roles");
737 
738         if (entityRolesEl == null) {
739             return;
740         }
741 
742         importRolePermissions(
743             layoutCache, companyId, resourceName, ResourceConstants.SCOPE_GROUP,
744             String.valueOf(groupId), entityRolesEl, true);
745     }
746 
747     protected void importInheritedPermissions(
748             LayoutCache layoutCache, long companyId, String resourceName,
749             String resourcePrimKey, Element permissionsEl, String entityName,
750             boolean portletActions)
751         throws PortalException, SystemException {
752 
753         Element entityPermissionsEl = permissionsEl.element(
754             entityName + "-permissions");
755 
756         if (entityPermissionsEl == null) {
757             return;
758         }
759 
760         List<Element> actionsEls = entityPermissionsEl.elements(
761             entityName + "-actions");
762 
763         for (int i = 0; i < actionsEls.size(); i++) {
764             Element actionEl = actionsEls.get(i);
765 
766             String name = actionEl.attributeValue("name");
767 
768             long entityGroupId = layoutCache.getEntityGroupId(
769                 companyId, entityName, name);
770 
771             if (entityGroupId == 0) {
772                 _log.warn(
773                     "Ignore inherited permissions for entity " + entityName +
774                         " with name " + name);
775             }
776             else {
777                 Element parentEl = SAXReaderUtil.createElement("parent");
778 
779                 parentEl.add(actionEl.createCopy());
780 
781                 importGroupPermissions(
782                     layoutCache, companyId, entityGroupId, resourceName,
783                     resourcePrimKey, parentEl, entityName + "-actions",
784                     portletActions);
785             }
786         }
787     }
788 
789     protected void importInheritedRoles(
790             LayoutCache layoutCache, long companyId, long groupId,
791             String resourceName, String entityName, Element parentEl)
792         throws PortalException, SystemException {
793 
794         Element entityRolesEl = parentEl.element(entityName + "-roles");
795 
796         if (entityRolesEl == null) {
797             return;
798         }
799 
800         List<Element> entityEls = entityRolesEl.elements(entityName);
801 
802         for (int i = 0; i < entityEls.size(); i++) {
803             Element entityEl = entityEls.get(i);
804 
805             String name = entityEl.attributeValue("name");
806 
807             long entityGroupId = layoutCache.getEntityGroupId(
808                 companyId, entityName, name);
809 
810             if (entityGroupId == 0) {
811                 _log.warn(
812                     "Ignore inherited roles for entity " + entityName +
813                         " with name " + name);
814             }
815             else {
816                 importRolePermissions(
817                     layoutCache, companyId, resourceName,
818                     ResourceConstants.SCOPE_GROUP, String.valueOf(groupId),
819                     entityEl, false);
820             }
821         }
822     }
823 
824     protected void importLayoutPermissions(
825             LayoutCache layoutCache, long companyId, long groupId,
826             Group guestGroup, Layout layout, Element permissionsEl,
827             boolean importUserPermissions)
828         throws PortalException, SystemException {
829 
830         String resourceName = Layout.class.getName();
831         String resourcePrimKey = String.valueOf(layout.getPlid());
832 
833         importGroupPermissions(
834             layoutCache, companyId, groupId, resourceName, resourcePrimKey,
835             permissionsEl, "community-actions", false);
836 
837         if (groupId != guestGroup.getGroupId()) {
838             importGroupPermissions(
839                 layoutCache, companyId, guestGroup.getGroupId(), resourceName,
840                 resourcePrimKey, permissionsEl, "guest-actions", false);
841         }
842 
843         if (importUserPermissions) {
844             importUserPermissions(
845                 layoutCache, companyId, groupId, resourceName, resourcePrimKey,
846                 permissionsEl, false);
847         }
848 
849         importInheritedPermissions(
850             layoutCache, companyId, resourceName, resourcePrimKey,
851             permissionsEl, "organization", false);
852 
853         importInheritedPermissions(
854             layoutCache, companyId, resourceName, resourcePrimKey,
855             permissionsEl, "location", false);
856 
857         importInheritedPermissions(
858             layoutCache, companyId, resourceName, resourcePrimKey,
859             permissionsEl, "user-group", false);
860     }
861 
862     protected void importLayoutRoles(
863             LayoutCache layoutCache, long companyId, long groupId,
864             Element rolesEl)
865         throws PortalException, SystemException {
866 
867         String resourceName = Layout.class.getName();
868 
869         importGroupRoles(
870             layoutCache, companyId, groupId, resourceName, "community",
871             rolesEl);
872 
873         importUserRoles(layoutCache, companyId, groupId, resourceName, rolesEl);
874 
875         importInheritedRoles(
876             layoutCache, companyId, groupId, resourceName, "organization",
877             rolesEl);
878 
879         importInheritedRoles(
880             layoutCache, companyId, groupId, resourceName, "location", rolesEl);
881 
882         importInheritedRoles(
883             layoutCache, companyId, groupId, resourceName, "user-group",
884             rolesEl);
885     }
886 
887     protected void importPortletPermissions(
888             LayoutCache layoutCache, long companyId, long groupId,
889             Group guestGroup, Layout layout, Element permissionsEl,
890             boolean importUserPermissions)
891         throws PortalException, SystemException {
892 
893         Iterator<Element> itr = permissionsEl.elements("portlet").iterator();
894 
895         while (itr.hasNext()) {
896             Element portletEl = itr.next();
897 
898             String portletId = portletEl.attributeValue("portlet-id");
899 
900             String resourceName = PortletConstants.getRootPortletId(portletId);
901             String resourcePrimKey = PortletPermissionUtil.getPrimaryKey(
902                 layout.getPlid(), portletId);
903 
904             Portlet portlet = PortletLocalServiceUtil.getPortletById(
905                 companyId, resourceName);
906 
907             if (portlet == null) {
908                 if (_log.isDebugEnabled()) {
909                     _log.debug(
910                         "Do not import portlet permissions for " + portletId +
911                             " because the portlet does not exist");
912                 }
913             }
914             else {
915                 importGroupPermissions(
916                     layoutCache, companyId, groupId, resourceName,
917                     resourcePrimKey, portletEl, "community-actions", true);
918 
919                 if (groupId != guestGroup.getGroupId()) {
920                     importGroupPermissions(
921                         layoutCache, companyId, guestGroup.getGroupId(),
922                         resourceName, resourcePrimKey, portletEl,
923                         "guest-actions", true);
924                 }
925 
926                 if (importUserPermissions) {
927                     importUserPermissions(
928                         layoutCache, companyId, groupId, resourceName,
929                         resourcePrimKey, portletEl, true);
930                 }
931 
932                 importInheritedPermissions(
933                     layoutCache, companyId, resourceName, resourcePrimKey,
934                     portletEl, "organization", true);
935 
936                 importInheritedPermissions(
937                     layoutCache, companyId, resourceName, resourcePrimKey,
938                     portletEl, "location", true);
939 
940                 importInheritedPermissions(
941                     layoutCache, companyId, resourceName, resourcePrimKey,
942                     portletEl, "user-group", true);
943             }
944         }
945     }
946 
947     protected void importPortletRoles(
948             LayoutCache layoutCache, long companyId, long groupId,
949             String portletId, Element rolesEl)
950         throws PortalException, SystemException {
951 
952         String resourceName = PortletConstants.getRootPortletId(portletId);
953 
954         Portlet portlet = PortletLocalServiceUtil.getPortletById(
955             companyId, resourceName);
956 
957         if (portlet == null) {
958             if (_log.isDebugEnabled()) {
959                 _log.debug(
960                     "Do not import portlet roles for " + portletId +
961                         " because the portlet does not exist");
962             }
963         }
964         else {
965             importGroupRoles(
966                 layoutCache, companyId, groupId, resourceName, "community",
967                 rolesEl);
968 
969             importUserRoles(
970                 layoutCache, companyId, groupId, resourceName, rolesEl);
971 
972             importInheritedRoles(
973                 layoutCache, companyId, groupId, resourceName,
974                 "organization", rolesEl);
975 
976             importInheritedRoles(
977                 layoutCache, companyId, groupId, resourceName, "location",
978                 rolesEl);
979 
980             importInheritedRoles(
981                 layoutCache, companyId, groupId, resourceName, "user-group",
982                 rolesEl);
983         }
984     }
985 
986     protected void importPortletRoles(
987             LayoutCache layoutCache, long companyId, long groupId,
988             Element rolesEl)
989         throws PortalException, SystemException {
990 
991         Iterator<Element> itr = rolesEl.elements("portlet").iterator();
992 
993         while (itr.hasNext()) {
994             Element portletEl = itr.next();
995 
996             String portletId = portletEl.attributeValue("portlet-id");
997 
998             String resourceName = PortletConstants.getRootPortletId(portletId);
999 
1000            Portlet portlet = PortletLocalServiceUtil.getPortletById(
1001                companyId, resourceName);
1002
1003            if (portlet == null) {
1004                if (_log.isDebugEnabled()) {
1005                    _log.debug(
1006                        "Do not import portlet roles for " + portletId +
1007                            " because the portlet does not exist");
1008                }
1009            }
1010            else {
1011                importGroupRoles(
1012                    layoutCache, companyId, groupId, resourceName, "community",
1013                    portletEl);
1014
1015                importUserRoles(
1016                    layoutCache, companyId, groupId, resourceName, portletEl);
1017
1018                importInheritedRoles(
1019                    layoutCache, companyId, groupId, resourceName,
1020                    "organization", portletEl);
1021
1022                importInheritedRoles(
1023                    layoutCache, companyId, groupId, resourceName, "location",
1024                    portletEl);
1025
1026                importInheritedRoles(
1027                    layoutCache, companyId, groupId, resourceName, "user-group",
1028                    portletEl);
1029            }
1030        }
1031    }
1032
1033    protected void importRolePermissions(
1034            LayoutCache layoutCache, long companyId, String resourceName,
1035            int scope, String resourcePrimKey, Element parentEl,
1036            boolean communityRole)
1037        throws PortalException, SystemException {
1038
1039        List<Element> roleEls = parentEl.elements("role");
1040
1041        for (int i = 0; i < roleEls.size(); i++) {
1042            Element roleEl = roleEls.get(i);
1043
1044            String roleName = roleEl.attributeValue("name");
1045
1046            Role role = layoutCache.getRole(companyId, roleName);
1047
1048            if (role == null) {
1049                _log.warn(
1050                    "Ignoring permissions for role with name " + roleName);
1051            }
1052            else {
1053                List<String> actions = getActions(roleEl);
1054
1055                PermissionLocalServiceUtil.setRolePermissions(
1056                    role.getRoleId(), companyId, resourceName, scope,
1057                    resourcePrimKey,
1058                    actions.toArray(new String[actions.size()]));
1059
1060                if (communityRole) {
1061                    long[] groupIds = {GetterUtil.getLong(resourcePrimKey)};
1062
1063                    GroupLocalServiceUtil.addRoleGroups(
1064                        role.getRoleId(), groupIds);
1065                }
1066            }
1067        }
1068    }
1069
1070    protected String importTheme(LayoutSet layoutSet, byte[] themeZip)
1071        throws IOException {
1072
1073        ThemeLoader themeLoader = ThemeLoaderFactory.getDefaultThemeLoader();
1074
1075        if (themeLoader == null) {
1076            _log.error("No theme loaders are deployed");
1077
1078            return null;
1079        }
1080
1081        ZipReader zipReader = new ZipReader(new ByteArrayInputStream(themeZip));
1082
1083        Map<String, byte[]> entries = zipReader.getEntries();
1084
1085        String lookAndFeelXML = new String(
1086            entries.get("liferay-look-and-feel.xml"));
1087
1088        String themeId = String.valueOf(layoutSet.getGroupId());
1089
1090        if (layoutSet.isPrivateLayout()) {
1091            themeId += "-private";
1092        }
1093        else {
1094            themeId += "-public";
1095        }
1096
1097        if (PropsValues.THEME_LOADER_NEW_THEME_ID_ON_IMPORT) {
1098            Date now = new Date();
1099
1100            themeId += "-" + Time.getShortTimestamp(now);
1101        }
1102
1103        String themeName = themeId;
1104
1105        lookAndFeelXML = StringUtil.replace(
1106            lookAndFeelXML,
1107            new String[] {
1108                "[$GROUP_ID$]", "[$THEME_ID$]", "[$THEME_NAME$]"
1109            },
1110            new String[] {
1111                String.valueOf(layoutSet.getGroupId()), themeId, themeName
1112            }
1113        );
1114
1115        FileUtil.deltree(themeLoader.getFileStorage() + "/" + themeId);
1116
1117        Iterator<Map.Entry<String, byte[]>> itr = entries.entrySet().iterator();
1118
1119        while (itr.hasNext()) {
1120            Map.Entry<String, byte[]> entry = itr.next();
1121
1122            String key = entry.getKey();
1123            byte[] value = entry.getValue();
1124
1125            if (key.equals("liferay-look-and-feel.xml")) {
1126                value = lookAndFeelXML.getBytes();
1127            }
1128
1129            FileUtil.write(
1130                themeLoader.getFileStorage() + "/" + themeId + "/" + key,
1131                value);
1132        }
1133
1134        themeLoader.loadThemes();
1135
1136        CommLink commLink = CommLink.getInstance();
1137
1138        MethodWrapper methodWrapper = new MethodWrapper(
1139            ThemeLoaderFactory.class.getName(), "loadThemes");
1140
1141        commLink.send(methodWrapper);
1142
1143        themeId +=
1144            PortletConstants.WAR_SEPARATOR +
1145                themeLoader.getServletContextName();
1146
1147        return PortalUtil.getJsSafePortletId(themeId);
1148    }
1149
1150    protected void importUserPermissions(
1151            LayoutCache layoutCache, long companyId, long groupId,
1152            String resourceName, String resourcePrimKey, Element parentEl,
1153            boolean portletActions)
1154        throws PortalException, SystemException {
1155
1156        Element userPermissionsEl = parentEl.element("user-permissions");
1157
1158        if (userPermissionsEl == null) {
1159            return;
1160        }
1161
1162        List<Element> userActionsEls = userPermissionsEl.elements(
1163            "user-actions");
1164
1165        for (int i = 0; i < userActionsEls.size(); i++) {
1166            Element userActionsEl = userActionsEls.get(i);
1167
1168            String emailAddress = userActionsEl.attributeValue("email-address");
1169
1170            User user = layoutCache.getUser(companyId, groupId, emailAddress);
1171
1172            if (user == null) {
1173                if (_log.isWarnEnabled()) {
1174                    _log.warn(
1175                        "Ignoring permissions for user with email address " +
1176                            emailAddress);
1177                }
1178            }
1179            else {
1180                List<String> actions = getActions(userActionsEl);
1181
1182                Resource resource = layoutCache.getResource(
1183                    companyId, groupId, resourceName,
1184                    ResourceConstants.SCOPE_INDIVIDUAL, resourcePrimKey,
1185                    portletActions);
1186
1187                PermissionLocalServiceUtil.setUserPermissions(
1188                    user.getUserId(),
1189                    actions.toArray(new String[actions.size()]),
1190                    resource.getResourceId());
1191            }
1192        }
1193    }
1194
1195    protected void importUserRoles(
1196            LayoutCache layoutCache, long companyId, long groupId,
1197            String resourceName, Element parentEl)
1198        throws PortalException, SystemException {
1199
1200        Element userRolesEl = parentEl.element("user-roles");
1201
1202        if (userRolesEl == null) {
1203            return;
1204        }
1205
1206        List<Element> userEls = userRolesEl.elements("user");
1207
1208        for (int i = 0; i < userEls.size(); i++) {
1209            Element userEl = userEls.get(i);
1210
1211            String emailAddress = userEl.attributeValue("email-address");
1212
1213            User user = layoutCache.getUser(companyId, groupId, emailAddress);
1214
1215            if (user == null) {
1216                if (_log.isWarnEnabled()) {
1217                    _log.warn(
1218                        "Ignoring roles for user with email address " +
1219                            emailAddress);
1220                }
1221            }
1222            else {
1223                importRolePermissions(
1224                    layoutCache, companyId, resourceName,
1225                    ResourceConstants.SCOPE_GROUP, String.valueOf(groupId),
1226                    userEl, false);
1227            }
1228        }
1229    }
1230
1231    protected void mergePortlets(
1232        Layout layout, String newTypeSettings, String portletsMergeMode) {
1233
1234        try {
1235            UnicodeProperties previousProps =
1236                layout.getTypeSettingsProperties();
1237            LayoutTypePortlet previousLayoutType =
1238                (LayoutTypePortlet)layout.getLayoutType();
1239            List<String> previousColumns =
1240                previousLayoutType.getLayoutTemplate().getColumns();
1241
1242            UnicodeProperties newProps = new UnicodeProperties(true);
1243
1244            newProps.load(newTypeSettings);
1245
1246            String layoutTemplateId = newProps.getProperty(
1247                    LayoutTypePortletImpl.LAYOUT_TEMPLATE_ID);
1248
1249            LayoutTemplate newLayoutTemplate =
1250                LayoutTemplateLocalServiceUtil.getLayoutTemplate(
1251                    layoutTemplateId, false, null);
1252
1253            String[] lostPortletIds = new String[0];
1254
1255            for (String columnId : newLayoutTemplate.getColumns()) {
1256                String columnValue =
1257                    newProps.getProperty(columnId);
1258
1259                String[] portletIds = StringUtil.split(columnValue);
1260
1261                if (!previousColumns.contains(columnId)) {
1262                    lostPortletIds = ArrayUtil.append(
1263                        lostPortletIds, portletIds);
1264                }
1265                else {
1266
1267                    String[] previousPortletIds = StringUtil.split(
1268                        previousProps.getProperty(columnId));
1269
1270                    portletIds = appendPortletIds(
1271                        previousPortletIds, portletIds, portletsMergeMode);
1272
1273                    previousProps.setProperty(
1274                        columnId, StringUtil.merge(portletIds));
1275                }
1276            }
1277
1278            // Add portlets in non-existent column to the first column
1279
1280            String columnId = previousColumns.get(0);
1281
1282            String[] portletIds = StringUtil.split(
1283                previousProps.getProperty(columnId));
1284
1285            appendPortletIds(portletIds, lostPortletIds, portletsMergeMode);
1286
1287            previousProps.setProperty(
1288                columnId, StringUtil.merge(portletIds));
1289
1290            layout.setTypeSettings(previousProps.toString());
1291
1292        }
1293        catch (IOException e) {
1294            layout.setTypeSettings(newTypeSettings);
1295        }
1296    }
1297
1298    protected String[] appendPortletIds(
1299        String[] portletIds, String[] newPortletIds,
1300        String portletsMergeMode) {
1301
1302        for (String portletId : newPortletIds) {
1303            if (ArrayUtil.contains(portletIds, portletId)) {
1304                continue;
1305            }
1306
1307            if (portletsMergeMode.equals(
1308                    PortletDataHandlerKeys.PORTLETS_MERGE_MODE_ADD_TO_BOTTOM)) {
1309                portletIds = ArrayUtil.append(
1310                    portletIds, portletId);
1311            }
1312            else {
1313                portletIds = ArrayUtil.append(
1314                    new String[]{portletId}, portletIds);
1315            }
1316        }
1317
1318        return portletIds;
1319    }
1320
1321    private static Log _log = LogFactory.getLog(LayoutImporter.class);
1322
1323    private PortletImporter _portletImporter = new PortletImporter();
1324
1325}