1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.lar;
16  
17  import com.liferay.portal.NoSuchLayoutException;
18  import com.liferay.portal.kernel.log.Log;
19  import com.liferay.portal.kernel.log.LogFactoryUtil;
20  import com.liferay.portal.kernel.servlet.ServletContextPool;
21  import com.liferay.portal.kernel.util.CharPool;
22  import com.liferay.portal.kernel.util.FileUtil;
23  import com.liferay.portal.kernel.util.GetterUtil;
24  import com.liferay.portal.kernel.util.ListUtil;
25  import com.liferay.portal.kernel.util.MapUtil;
26  import com.liferay.portal.kernel.util.ReleaseInfo;
27  import com.liferay.portal.kernel.util.StringBundler;
28  import com.liferay.portal.kernel.util.StringPool;
29  import com.liferay.portal.kernel.util.StringUtil;
30  import com.liferay.portal.kernel.util.Time;
31  import com.liferay.portal.kernel.util.UnicodeProperties;
32  import com.liferay.portal.kernel.xml.Document;
33  import com.liferay.portal.kernel.xml.Element;
34  import com.liferay.portal.kernel.xml.SAXReaderUtil;
35  import com.liferay.portal.kernel.zip.ZipWriter;
36  import com.liferay.portal.kernel.zip.ZipWriterFactoryUtil;
37  import com.liferay.portal.model.Group;
38  import com.liferay.portal.model.Image;
39  import com.liferay.portal.model.Layout;
40  import com.liferay.portal.model.LayoutConstants;
41  import com.liferay.portal.model.LayoutSet;
42  import com.liferay.portal.model.LayoutTypePortlet;
43  import com.liferay.portal.model.Portlet;
44  import com.liferay.portal.model.PortletConstants;
45  import com.liferay.portal.model.Theme;
46  import com.liferay.portal.service.ImageLocalServiceUtil;
47  import com.liferay.portal.service.LayoutLocalServiceUtil;
48  import com.liferay.portal.service.LayoutSetLocalServiceUtil;
49  import com.liferay.portal.service.PortletLocalServiceUtil;
50  import com.liferay.portal.service.UserLocalServiceUtil;
51  import com.liferay.portal.service.permission.PortletPermissionUtil;
52  import com.liferay.portal.service.persistence.LayoutUtil;
53  import com.liferay.portal.theme.ThemeLoader;
54  import com.liferay.portal.theme.ThemeLoaderFactory;
55  import com.liferay.portal.util.ContentUtil;
56  import com.liferay.portal.util.PortletKeys;
57  import com.liferay.portal.util.PropsValues;
58  import com.liferay.portlet.PortletPreferencesFactoryUtil;
59  import com.liferay.portlet.tags.model.TagsEntry;
60  import com.liferay.portlet.tags.model.TagsEntryConstants;
61  import com.liferay.portlet.tags.model.TagsVocabulary;
62  import com.liferay.portlet.tags.service.TagsEntryLocalServiceUtil;
63  import com.liferay.portlet.tags.service.TagsVocabularyLocalServiceUtil;
64  
65  import java.io.File;
66  
67  import java.util.ArrayList;
68  import java.util.Date;
69  import java.util.HashSet;
70  import java.util.Iterator;
71  import java.util.LinkedHashMap;
72  import java.util.List;
73  import java.util.Map;
74  
75  import javax.servlet.ServletContext;
76  
77  import org.apache.commons.lang.time.StopWatch;
78  
79  /**
80   * <a href="LayoutExporter.java.html"><b><i>View Source</i></b></a>
81   *
82   * @author Brian Wing Shun Chan
83   * @author Joel Kozikowski
84   * @author Charles May
85   * @author Raymond Augé
86   * @author Jorge Ferrer
87   * @author Bruno Farache
88   * @author Karthik Sudarshan
89   * @author Zsigmond Rab
90   * @author Douglas Wong
91   */
92  public class LayoutExporter {
93  
94      public static final String SAME_GROUP_FRIENDLY_URL =
95          "/[$SAME_GROUP_FRIENDLY_URL$]";
96  
97      public static List<Portlet> getAlwaysExportablePortlets(long companyId)
98          throws Exception {
99  
100         List<Portlet> portlets = PortletLocalServiceUtil.getPortlets(companyId);
101 
102         Iterator<Portlet> itr = portlets.iterator();
103 
104         while (itr.hasNext()) {
105             Portlet portlet = itr.next();
106 
107             if (!portlet.isActive()) {
108                 itr.remove();
109 
110                 continue;
111             }
112 
113             PortletDataHandler portletDataHandler =
114                 portlet.getPortletDataHandlerInstance();
115 
116             if ((portletDataHandler == null) ||
117                 (!portletDataHandler.isAlwaysExportable())) {
118 
119                 itr.remove();
120             }
121         }
122 
123         return portlets;
124     }
125 
126     public byte[] exportLayouts(
127             long groupId, boolean privateLayout, long[] layoutIds,
128             Map<String, String[]> parameterMap, Date startDate, Date endDate)
129         throws Exception {
130 
131         File file = exportLayoutsAsFile(
132             groupId, privateLayout, layoutIds, parameterMap, startDate,
133             endDate);
134 
135         try {
136             return FileUtil.getBytes(file);
137         }
138         finally {
139             file.delete();
140         }
141     }
142 
143     public File exportLayoutsAsFile(
144             long groupId, boolean privateLayout, long[] layoutIds,
145             Map<String, String[]> parameterMap, Date startDate, Date endDate)
146         throws Exception {
147 
148         boolean exportCategories = MapUtil.getBoolean(
149             parameterMap, PortletDataHandlerKeys.CATEGORIES);
150         boolean exportPermissions = MapUtil.getBoolean(
151             parameterMap, PortletDataHandlerKeys.PERMISSIONS);
152         boolean exportUserPermissions = MapUtil.getBoolean(
153             parameterMap, PortletDataHandlerKeys.USER_PERMISSIONS);
154         boolean exportPortletArchivedSetups = MapUtil.getBoolean(
155             parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
156         boolean exportPortletUserPreferences = MapUtil.getBoolean(
157             parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
158         boolean exportTheme = MapUtil.getBoolean(
159             parameterMap, PortletDataHandlerKeys.THEME);
160 
161         if (_log.isDebugEnabled()) {
162             _log.debug("Export categories " + exportCategories);
163             _log.debug("Export permissions " + exportPermissions);
164             _log.debug("Export user permissions " + exportUserPermissions);
165             _log.debug(
166                 "Export portlet archived setups " +
167                     exportPortletArchivedSetups);
168             _log.debug(
169                 "Export portlet user preferences " +
170                     exportPortletUserPreferences);
171             _log.debug("Export theme " + exportTheme);
172         }
173 
174         StopWatch stopWatch = null;
175 
176         if (_log.isInfoEnabled()) {
177             stopWatch = new StopWatch();
178 
179             stopWatch.start();
180         }
181 
182         LayoutCache layoutCache = new LayoutCache();
183 
184         LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
185             groupId, privateLayout);
186 
187         long companyId = layoutSet.getCompanyId();
188         long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
189 
190         ZipWriter zipWriter = ZipWriterFactoryUtil.getZipWriter();
191 
192         PortletDataContext context = new PortletDataContextImpl(
193             companyId, groupId, parameterMap, new HashSet<String>(), startDate,
194             endDate, zipWriter);
195 
196         // Build compatibility
197 
198         Document document = SAXReaderUtil.createDocument();
199 
200         Element rootElement = document.addElement("root");
201 
202         Element headerElement = rootElement.addElement("header");
203 
204         headerElement.addAttribute(
205             "build-number", String.valueOf(ReleaseInfo.getBuildNumber()));
206         headerElement.addAttribute("export-date", Time.getRFC822());
207 
208         if (context.hasDateRange()) {
209             headerElement.addAttribute(
210                 "start-date", String.valueOf(context.getStartDate()));
211             headerElement.addAttribute(
212                 "end-date", String.valueOf(context.getEndDate()));
213         }
214 
215         headerElement.addAttribute("type", "layout-set");
216         headerElement.addAttribute("group-id", String.valueOf(groupId));
217         headerElement.addAttribute(
218             "private-layout", String.valueOf(privateLayout));
219         headerElement.addAttribute("theme-id", layoutSet.getThemeId());
220         headerElement.addAttribute(
221             "color-scheme-id", layoutSet.getColorSchemeId());
222 
223         Element cssElement = headerElement.addElement("css");
224 
225         cssElement.addCDATA(layoutSet.getCss());
226 
227         // Layout configuration portlet
228 
229         Portlet layoutConfigurationPortlet =
230             PortletLocalServiceUtil.getPortletById(
231                 companyId, PortletKeys.LAYOUT_CONFIGURATION);
232 
233         // Layouts
234 
235         Map<String, Object[]> portletIds =
236             new LinkedHashMap<String, Object[]>();
237 
238         List<Layout> layouts = null;
239 
240         if ((layoutIds == null) || (layoutIds.length == 0)) {
241             layouts = LayoutLocalServiceUtil.getLayouts(groupId, privateLayout);
242         }
243         else {
244             layouts = LayoutLocalServiceUtil.getLayouts(
245                 groupId, privateLayout, layoutIds);
246         }
247 
248         Layout firstLayout = layouts.get(0);
249 
250         List<Portlet> portlets = getAlwaysExportablePortlets(companyId);
251 
252         for (Portlet portlet : portlets) {
253             String portletId = portlet.getRootPortletId();
254 
255             if (portlet.isScopeable() && firstLayout.hasScopeGroup()) {
256                 String key = PortletPermissionUtil.getPrimaryKey(
257                     firstLayout.getPlid(), portletId);
258 
259                 portletIds.put(
260                     key,
261                     new Object[] {
262                         portletId, firstLayout.getPlid(),
263                         firstLayout.getScopeGroup().getGroupId(),
264                         firstLayout.getLayoutId()
265                     }
266                 );
267             }
268             else {
269                 String key = PortletPermissionUtil.getPrimaryKey(
270                     0, portletId);
271 
272                 if (portletIds.get(key) == null) {
273                     portletIds.put(
274                         key,
275                         new Object[] {
276                             portletId, firstLayout.getPlid(), groupId, 0L
277                         }
278                     );
279                 }
280             }
281         }
282 
283         Element layoutsElement = rootElement.addElement("layouts");
284 
285         for (Layout layout : layouts) {
286             exportLayout(
287                 context, layoutConfigurationPortlet, layoutCache, portletIds,
288                 exportPermissions, exportUserPermissions, layout,
289                 layoutsElement);
290         }
291 
292         if (PropsValues.PERMISSIONS_USER_CHECK_ALGORITHM < 5) {
293             Element rolesElement = rootElement.addElement("roles");
294 
295             // Layout roles
296 
297             if (exportPermissions) {
298                 _permissionExporter.exportLayoutRoles(
299                     layoutCache, companyId, groupId, rolesElement);
300             }
301         }
302 
303         // Export portlets
304 
305         long previousScopeGroupId = context.getScopeGroupId();
306 
307         Element portletsElement = rootElement.addElement("portlets");
308 
309         for (Map.Entry<String, Object[]> portletIdsEntry :
310                 portletIds.entrySet()) {
311 
312             String portletId = (String)portletIdsEntry.getValue()[0];
313             long plid = (Long)portletIdsEntry.getValue()[1];
314             long scopeGroupId = (Long)portletIdsEntry.getValue()[2];
315             long scopeLayoutId = (Long)portletIdsEntry.getValue()[3];
316 
317             Layout layout = LayoutUtil.findByPrimaryKey(plid);
318 
319             context.setPlid(layout.getPlid());
320             context.setOldPlid(layout.getPlid());
321             context.setScopeGroupId(scopeGroupId);
322             context.setScopeLayoutId(scopeLayoutId);
323 
324             boolean[] exportPortletControls = getExportPortletControls(
325                 companyId, portletId, context, parameterMap);
326 
327             _portletExporter.exportPortlet(
328                 context, layoutCache, portletId, layout, portletsElement,
329                 defaultUserId, exportPermissions, exportPortletArchivedSetups,
330                 exportPortletControls[0], exportPortletControls[1],
331                 exportPortletUserPreferences, exportUserPermissions);
332         }
333 
334         context.setScopeGroupId(previousScopeGroupId);
335 
336         // Categories
337 
338         if (exportCategories) {
339             exportCategories(context);
340         }
341 
342         _portletExporter.exportCategories(context, rootElement);
343 
344         // Comments
345 
346         _portletExporter.exportComments(context, rootElement);
347 
348         // Locks
349 
350         _portletExporter.exportLocks(context, rootElement);
351 
352         // Portlet data permissions
353 
354         _permissionExporter.exportPortletDataPermissions(context);
355 
356         // Ratings
357 
358         _portletExporter.exportRatings(context, rootElement);
359 
360         // Tags
361 
362         _portletExporter.exportTags(context, rootElement);
363 
364         // Look and feel
365 
366         if (exportTheme) {
367             exportTheme(layoutSet, zipWriter);
368         }
369 
370         // Log
371 
372         if (_log.isInfoEnabled()) {
373             if (stopWatch != null) {
374                 _log.info(
375                     "Exporting layouts takes " + stopWatch.getTime() + " ms");
376             }
377             else {
378                 _log.info("Exporting layouts is finished");
379             }
380         }
381 
382         // Zip
383 
384         context.addZipEntry("/manifest.xml", document.formattedString());
385 
386         return zipWriter.getFile();
387     }
388 
389     protected void exportCategories(PortletDataContext context)
390         throws Exception {
391 
392         Document document = SAXReaderUtil.createDocument();
393 
394         Element rootElement = document.addElement("categories-hierarchy");
395 
396         List<TagsVocabulary> tagsVocabularies =
397             TagsVocabularyLocalServiceUtil.getGroupVocabularies(
398                 context.getGroupId(),
399                 TagsEntryConstants.FOLKSONOMY_CATEGORY);
400 
401         for (TagsVocabulary tagsVocabulary : tagsVocabularies) {
402             Element vocabularyElement = rootElement.addElement("vocabulary");
403 
404             String name = tagsVocabulary.getName();
405 
406             vocabularyElement.addAttribute("name", name);
407             vocabularyElement.addAttribute(
408                 "userUuid", tagsVocabulary.getUserUuid());
409 
410             List<TagsEntry> tagsCategories =
411                 TagsEntryLocalServiceUtil.getGroupVocabularyEntries(
412                     context.getGroupId(), name);
413 
414             tagsCategories = ListUtil.copy(tagsCategories);
415 
416             orderCategories(
417                 tagsCategories, vocabularyElement,
418                 TagsEntryConstants.DEFAULT_PARENT_ENTRY_ID);
419         }
420 
421         context.addZipEntry(
422             context.getRootPath() + "/categories-hierarchy.xml",
423             document.formattedString());
424     }
425 
426     protected void exportLayout(
427             PortletDataContext context, Portlet layoutConfigurationPortlet,
428             LayoutCache layoutCache, Map<String, Object[]> portletIds,
429             boolean exportPermissions, boolean exportUserPermissions,
430             Layout layout, Element layoutsElement)
431         throws Exception {
432 
433         String path = context.getLayoutPath(
434             layout.getLayoutId()) + "/layout.xml";
435 
436         if (!context.isPathNotProcessed(path)) {
437             return;
438         }
439 
440         Element layoutElement = layoutsElement.addElement("layout");
441 
442         layoutElement.addAttribute(
443             "layout-id", String.valueOf(layout.getLayoutId()));
444 
445         long parentLayoutId = layout.getParentLayoutId();
446 
447         if (parentLayoutId != LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
448             Layout parentLayout = LayoutLocalServiceUtil.getLayout(
449                 layout.getGroupId(), layout.isPrivateLayout(), parentLayoutId);
450 
451             if (parentLayout != null) {
452                 layoutElement.addAttribute(
453                     "parent-layout-friendly-url",
454                     parentLayout.getFriendlyURL());
455             }
456         }
457 
458         boolean deleteLayout = MapUtil.getBoolean(
459             context.getParameterMap(), "delete_" + layout.getPlid());
460 
461         if (deleteLayout) {
462             layoutElement.addAttribute("delete", String.valueOf(true));
463 
464             layoutElement.addAttribute(
465                 "layout-friendly-url", layout.getFriendlyURL());
466 
467             return;
468         }
469 
470         context.setPlid(layout.getPlid());
471 
472         long scopeGroupId = context.getScopeGroupId();
473 
474         if (layout.isIconImage()) {
475             Image image = ImageLocalServiceUtil.getImage(
476                 layout.getIconImageId());
477 
478             if (image != null) {
479                 String iconPath = getLayoutIconPath(context, layout, image);
480 
481                 layoutElement.addElement("icon-image-path").addText(iconPath);
482 
483                 context.addZipEntry(iconPath, image.getTextObj());
484             }
485         }
486 
487         _portletExporter.exportPortletData(
488             context, layoutConfigurationPortlet, layout, null, layoutElement);
489 
490         // Layout permissions
491 
492         if (exportPermissions) {
493             _permissionExporter.exportLayoutPermissions(
494                 context, layoutCache, context.getCompanyId(),
495                 context.getScopeGroupId(), layout, layoutElement,
496                 exportUserPermissions);
497         }
498 
499         if (layout.isTypePortlet()) {
500             LayoutTypePortlet layoutTypePortlet =
501                 (LayoutTypePortlet)layout.getLayoutType();
502 
503             for (String portletId : layoutTypePortlet.getPortletIds()) {
504                 javax.portlet.PortletPreferences jxPreferences =
505                     PortletPreferencesFactoryUtil.getLayoutPortletSetup(
506                         layout, portletId);
507 
508                 long scopeLayoutId = GetterUtil.getLong(
509                     jxPreferences.getValue("lfr-scope-layout-id", null));
510 
511                 if (scopeLayoutId != 0) {
512                     Layout scopeLayout = LayoutLocalServiceUtil.getLayout(
513                         context.getGroupId(), layout.isPrivateLayout(),
514                         scopeLayoutId);
515 
516                     Group scopeGroup = scopeLayout.getScopeGroup();
517 
518                     if (scopeGroup != null) {
519                         scopeGroupId = scopeGroup.getGroupId();
520                     }
521                 }
522 
523                 String key = PortletPermissionUtil.getPrimaryKey(
524                     layout.getPlid(), portletId);
525 
526                 portletIds.put(
527                     key,
528                     new Object[] {
529                         portletId, layout.getPlid(), scopeGroupId,
530                         scopeLayoutId
531                     }
532                 );
533             }
534         }
535         else if (layout.isTypeLinkToLayout()) {
536             UnicodeProperties typeSettingsProperties =
537                 layout.getTypeSettingsProperties();
538 
539             long linkToLayoutId = GetterUtil.getLong(
540                 typeSettingsProperties.getProperty(
541                     "linkToLayoutId", StringPool.BLANK));
542 
543             if (linkToLayoutId > 0) {
544                 try {
545                     Layout linkedToLayout = LayoutUtil.findByG_P_L(
546                         scopeGroupId, layout.isPrivateLayout(), linkToLayoutId);
547 
548                     exportLayout(
549                         context, layoutConfigurationPortlet, layoutCache,
550                         portletIds, exportPermissions, exportUserPermissions,
551                         linkedToLayout, layoutsElement);
552                 }
553                 catch (NoSuchLayoutException nsle) {
554                 }
555             }
556         }
557 
558         fixTypeSettings(layout);
559 
560         layoutElement.addAttribute("path", path);
561 
562         context.addZipEntry(path, layout);
563     }
564 
565     protected void exportTheme(LayoutSet layoutSet, ZipWriter zipWriter)
566         throws Exception {
567 
568         Theme theme = layoutSet.getTheme();
569 
570         String lookAndFeelXML = ContentUtil.get(
571             "com/liferay/portal/dependencies/liferay-look-and-feel.xml.tmpl");
572 
573         lookAndFeelXML = StringUtil.replace(
574             lookAndFeelXML,
575             new String[] {
576                 "[$TEMPLATE_EXTENSION$]", "[$VIRTUAL_PATH$]"
577             },
578             new String[] {
579                 theme.getTemplateExtension(), theme.getVirtualPath()
580             }
581         );
582 
583         String servletContextName = theme.getServletContextName();
584 
585         ServletContext servletContext = ServletContextPool.get(
586             servletContextName);
587 
588         if (servletContext == null) {
589             if (_log.isWarnEnabled()) {
590                 _log.warn(
591                     "Servlet context not found for theme " +
592                         theme.getThemeId());
593             }
594 
595             return;
596         }
597 
598         File themeZip = new File(zipWriter.getPath() + "/theme.zip");
599 
600         ZipWriter themeZipWriter = ZipWriterFactoryUtil.getZipWriter(themeZip);
601 
602         themeZipWriter.addEntry("liferay-look-and-feel.xml", lookAndFeelXML);
603 
604         File cssPath = null;
605         File imagesPath = null;
606         File javaScriptPath = null;
607         File templatesPath = null;
608 
609         if (!theme.isLoadFromServletContext()) {
610             ThemeLoader themeLoader = ThemeLoaderFactory.getThemeLoader(
611                 servletContextName);
612 
613             if (themeLoader == null) {
614                 _log.error(
615                     servletContextName + " does not map to a theme loader");
616             }
617             else {
618                 String realPath =
619                     themeLoader.getFileStorage().getPath() + StringPool.SLASH +
620                         theme.getName();
621 
622                 cssPath = new File(realPath + "/css");
623                 imagesPath = new File(realPath + "/images");
624                 javaScriptPath = new File(realPath + "/javascript");
625                 templatesPath = new File(realPath + "/templates");
626             }
627         }
628         else {
629             cssPath = new File(servletContext.getRealPath(theme.getCssPath()));
630             imagesPath = new File(
631                 servletContext.getRealPath(theme.getImagesPath()));
632             javaScriptPath = new File(
633                 servletContext.getRealPath(theme.getJavaScriptPath()));
634             templatesPath = new File(
635                 servletContext.getRealPath(theme.getTemplatesPath()));
636         }
637 
638         exportThemeFiles("css", cssPath, themeZipWriter);
639         exportThemeFiles("images", imagesPath, themeZipWriter);
640         exportThemeFiles("javascript", javaScriptPath, themeZipWriter);
641         exportThemeFiles("templates", templatesPath, themeZipWriter);
642     }
643 
644     protected void exportThemeFiles(String path, File dir, ZipWriter zipWriter)
645         throws Exception {
646 
647         if ((dir == null) || (!dir.exists())) {
648             return;
649         }
650 
651         File[] files = dir.listFiles();
652 
653         for (File file : files) {
654             if (file.isDirectory()) {
655                 exportThemeFiles(
656                     path + StringPool.SLASH + file.getName(), file, zipWriter);
657             }
658             else {
659                 zipWriter.addEntry(
660                     path + StringPool.SLASH + file.getName(),
661                     FileUtil.getBytes(file));
662             }
663         }
664     }
665 
666     protected void fixTypeSettings(Layout layout) {
667         if (!layout.isTypeURL()) {
668             return;
669         }
670 
671         UnicodeProperties typeSettings = layout.getTypeSettingsProperties();
672 
673         String url = GetterUtil.getString(typeSettings.getProperty("url"));
674 
675         String friendlyURLPrivateGroupPath =
676             PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING;
677         String friendlyURLPrivateUserPath =
678             PropsValues.LAYOUT_FRIENDLY_URL_PRIVATE_USER_SERVLET_MAPPING;
679         String friendlyURLPublicPath =
680             PropsValues.LAYOUT_FRIENDLY_URL_PUBLIC_SERVLET_MAPPING;
681 
682         if (!url.startsWith(friendlyURLPrivateGroupPath) &&
683             !url.startsWith(friendlyURLPrivateUserPath) &&
684             !url.startsWith(friendlyURLPublicPath)) {
685 
686             return;
687         }
688 
689         int x = url.indexOf(CharPool.SLASH, 1);
690 
691         if (x == -1) {
692             return;
693         }
694 
695         int y = url.indexOf(CharPool.SLASH, x + 1);
696 
697         if (y == -1) {
698             return;
699         }
700 
701         String friendlyURL = url.substring(x, y);
702         String groupFriendlyURL = layout.getGroup().getFriendlyURL();
703 
704         if (!friendlyURL.equals(groupFriendlyURL)) {
705             return;
706         }
707 
708         typeSettings.setProperty(
709             "url",
710             url.substring(0, x) + SAME_GROUP_FRIENDLY_URL + url.substring(y));
711     }
712 
713     protected boolean[] getExportPortletControls(
714             long companyId, String portletId, PortletDataContext context,
715             Map<String, String[]> parameterMap)
716         throws Exception {
717 
718         boolean exportPortletData = MapUtil.getBoolean(
719             parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
720         boolean exportPortletDataAll = MapUtil.getBoolean(
721             parameterMap, PortletDataHandlerKeys.PORTLET_DATA_ALL);
722         boolean exportPortletSetup = MapUtil.getBoolean(
723             parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
724 
725         if (_log.isDebugEnabled()) {
726             _log.debug("Export portlet data " + exportPortletData);
727             _log.debug("Export all portlet data " + exportPortletDataAll);
728             _log.debug("Export portlet setup " + exportPortletSetup);
729         }
730 
731         boolean exportCurPortletData = exportPortletData;
732         boolean exportCurPortletSetup = exportPortletSetup;
733 
734         // If PORTLET_DATA_ALL is true, this means that staging has just been
735         // activated and all data and setup must be exported. There is no
736         // portlet export control to check in this case.
737 
738         if (exportPortletDataAll) {
739             exportCurPortletData = true;
740             exportCurPortletSetup = true;
741         }
742         else {
743             Portlet portlet = PortletLocalServiceUtil.getPortletById(
744                 companyId, portletId);
745 
746             if (portlet != null) {
747                 String portletDataHandlerClass =
748                     portlet.getPortletDataHandlerClass();
749 
750                 // Checking if the portlet has a data handler, if it doesn't,
751                 // the default values are the ones set in PORTLET_DATA and
752                 // PORTLET_SETUP. If it has a data handler, iterate over each
753                 // portlet export control.
754 
755                 if (portletDataHandlerClass != null) {
756                     String rootPortletId = PortletConstants.getRootPortletId(
757                         portletId);
758 
759                     // PORTLET_DATA and the PORTLET_DATA for this specific
760                     // data handler must be true
761 
762                     exportCurPortletData =
763                         exportPortletData &&
764                         MapUtil.getBoolean(
765                             parameterMap,
766                             PortletDataHandlerKeys.PORTLET_DATA +
767                                 StringPool.UNDERLINE + rootPortletId);
768 
769                     // PORTLET_DATA and the PORTLET_SETUP for this specific
770                     // data handler must be true
771 
772                     exportCurPortletSetup =
773                         exportPortletData &&
774                         MapUtil.getBoolean(
775                             parameterMap,
776                             PortletDataHandlerKeys.PORTLET_SETUP +
777                                 StringPool.UNDERLINE + rootPortletId);
778                 }
779             }
780         }
781 
782         return new boolean[] {exportCurPortletData, exportCurPortletSetup};
783     }
784 
785     protected String getLayoutIconPath(
786         PortletDataContext context, Layout layout, Image image) {
787 
788         StringBundler sb = new StringBundler(5);
789 
790         sb.append(context.getLayoutPath(layout.getLayoutId()));
791         sb.append("/icons/");
792         sb.append(image.getImageId());
793         sb.append(StringPool.PERIOD);
794         sb.append(image.getType());
795 
796         return sb.toString();
797     }
798 
799     protected void orderCategories(
800             List<TagsEntry> tagsCategories, Element parentElement,
801             long parentEntryId)
802         throws Exception {
803 
804         List<TagsEntry> tagsParentCategories = new ArrayList<TagsEntry>();
805 
806         Iterator<TagsEntry> itr = tagsCategories.iterator();
807 
808         while (itr.hasNext()) {
809             TagsEntry tagsCategory = itr.next();
810 
811             if (tagsCategory.getParentEntryId() == parentEntryId) {
812                 Element categoryElement = parentElement.addElement("category");
813 
814                 categoryElement.addAttribute("name", tagsCategory.getName());
815                 categoryElement.addAttribute(
816                     "parentEntryName", tagsCategory.getParentName());
817                 categoryElement.addAttribute(
818                     "userUuid", tagsCategory.getUserUuid());
819 
820                 tagsParentCategories.add(tagsCategory);
821 
822                 itr.remove();
823             }
824         }
825 
826         for (TagsEntry tagsParentCategory : tagsParentCategories) {
827             orderCategories(
828                 tagsCategories, parentElement, tagsParentCategory.getEntryId());
829         }
830     }
831 
832     private static Log _log = LogFactoryUtil.getLog(LayoutExporter.class);
833 
834     private PermissionExporter _permissionExporter = new PermissionExporter();
835     private PortletExporter _portletExporter = new PortletExporter();
836 
837 }