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