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