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