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.portal.PortalException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.io.FileCacheOutputStream;
28  import com.liferay.portal.kernel.util.FileUtil;
29  import com.liferay.portal.kernel.util.ReleaseInfo;
30  import com.liferay.portal.kernel.util.StringPool;
31  import com.liferay.portal.kernel.util.StringUtil;
32  import com.liferay.portal.kernel.util.Time;
33  import com.liferay.portal.kernel.xml.Document;
34  import com.liferay.portal.kernel.xml.Element;
35  import com.liferay.portal.kernel.xml.SAXReaderUtil;
36  import com.liferay.portal.kernel.zip.ZipWriter;
37  import com.liferay.portal.model.Group;
38  import com.liferay.portal.model.GroupConstants;
39  import com.liferay.portal.model.Image;
40  import com.liferay.portal.model.Layout;
41  import com.liferay.portal.model.LayoutConstants;
42  import com.liferay.portal.model.LayoutSet;
43  import com.liferay.portal.model.LayoutTypePortlet;
44  import com.liferay.portal.model.Portlet;
45  import com.liferay.portal.model.PortletConstants;
46  import com.liferay.portal.model.Resource;
47  import com.liferay.portal.model.ResourceConstants;
48  import com.liferay.portal.model.Theme;
49  import com.liferay.portal.service.GroupLocalServiceUtil;
50  import com.liferay.portal.service.ImageLocalServiceUtil;
51  import com.liferay.portal.service.LayoutLocalServiceUtil;
52  import com.liferay.portal.service.LayoutSetLocalServiceUtil;
53  import com.liferay.portal.service.PortletLocalServiceUtil;
54  import com.liferay.portal.service.UserLocalServiceUtil;
55  import com.liferay.portal.service.permission.PortletPermissionUtil;
56  import com.liferay.portal.service.persistence.LayoutUtil;
57  import com.liferay.portal.theme.ThemeLoader;
58  import com.liferay.portal.theme.ThemeLoaderFactory;
59  import com.liferay.portal.util.ContentUtil;
60  import com.liferay.portal.util.PortletKeys;
61  import com.liferay.portal.util.PropsValues;
62  import com.liferay.portal.velocity.VelocityContextPool;
63  import com.liferay.util.MapUtil;
64  
65  import java.io.File;
66  import java.io.IOException;
67  import java.io.InputStream;
68  
69  import java.util.Date;
70  import java.util.HashSet;
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  import org.apache.commons.logging.Log;
79  import org.apache.commons.logging.LogFactory;
80  
81  /**
82   * <a href="LayoutExporter.java.html"><b><i>View Source</i></b></a>
83   *
84   * @author Brian Wing Shun Chan
85   * @author Joel Kozikowski
86   * @author Charles May
87   * @author Raymond Augé
88   * @author Jorge Ferrer
89   * @author Bruno Farache
90   *
91   */
92  public class LayoutExporter {
93  
94      public byte[] exportLayouts(
95              long groupId, boolean privateLayout, long[] layoutIds,
96              Map<String, String[]> parameterMap, Date startDate, Date endDate)
97          throws PortalException, SystemException {
98  
99          FileCacheOutputStream fcos = exportLayoutsAsStream(
100             groupId, privateLayout, layoutIds, parameterMap, startDate,
101             endDate);
102 
103         try {
104             return fcos.getBytes();
105         }
106         catch (IOException ioe) {
107             throw new SystemException(ioe);
108         }
109     }
110 
111     public FileCacheOutputStream exportLayoutsAsStream(
112             long groupId, boolean privateLayout, long[] layoutIds,
113             Map<String, String[]> parameterMap, Date startDate, Date endDate)
114         throws PortalException, SystemException {
115 
116         boolean exportPermissions = MapUtil.getBoolean(
117             parameterMap, PortletDataHandlerKeys.PERMISSIONS);
118         boolean exportUserPermissions = MapUtil.getBoolean(
119             parameterMap, PortletDataHandlerKeys.USER_PERMISSIONS);
120         boolean exportPortletData = MapUtil.getBoolean(
121             parameterMap, PortletDataHandlerKeys.PORTLET_DATA);
122         boolean exportPortletSetup = MapUtil.getBoolean(
123             parameterMap, PortletDataHandlerKeys.PORTLET_SETUP);
124         boolean exportPortletArchivedSetups = MapUtil.getBoolean(
125             parameterMap, PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS);
126         boolean exportPortletUserPreferences = MapUtil.getBoolean(
127             parameterMap, PortletDataHandlerKeys.PORTLET_USER_PREFERENCES);
128         boolean exportTheme = MapUtil.getBoolean(
129             parameterMap, PortletDataHandlerKeys.THEME);
130 
131         if (_log.isDebugEnabled()) {
132             _log.debug("Export permissions " + exportPermissions);
133             _log.debug("Export user permissions " + exportUserPermissions);
134             _log.debug("Export portlet data " + exportPortletData);
135             _log.debug("Export portlet setup " + exportPortletSetup);
136             _log.debug(
137                 "Export portlet archived setups " +
138                     exportPortletArchivedSetups);
139             _log.debug(
140                 "Export portlet user preferences " +
141                     exportPortletUserPreferences);
142             _log.debug("Export theme " + exportTheme);
143         }
144 
145         StopWatch stopWatch = null;
146 
147         if (_log.isInfoEnabled()) {
148             stopWatch = new StopWatch();
149 
150             stopWatch.start();
151         }
152 
153         LayoutCache layoutCache = new LayoutCache();
154 
155         LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
156             groupId, privateLayout);
157 
158         long companyId = layoutSet.getCompanyId();
159         long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
160 
161         ZipWriter zipWriter = null;
162 
163         try {
164             zipWriter = new ZipWriter();
165         }
166         catch (IOException ioe) {
167             throw new SystemException(ioe);
168         }
169 
170         PortletDataContext context = new PortletDataContextImpl(
171             companyId, groupId, parameterMap, new HashSet<String>(), startDate,
172             endDate, zipWriter);
173 
174         Group guestGroup = GroupLocalServiceUtil.getGroup(
175             companyId, GroupConstants.GUEST);
176 
177         // Build compatibility
178 
179         Document doc = SAXReaderUtil.createDocument();
180 
181         Element root = doc.addElement("root");
182 
183         Element header = root.addElement("header");
184 
185         header.addAttribute(
186             "build-number", String.valueOf(ReleaseInfo.getBuildNumber()));
187         header.addAttribute("export-date", Time.getRFC822());
188 
189         if (context.hasDateRange()) {
190             header.addAttribute(
191                 "start-date", String.valueOf(context.getStartDate()));
192             header.addAttribute(
193                 "end-date", String.valueOf(context.getEndDate()));
194         }
195 
196         header.addAttribute("type", "layout-set");
197         header.addAttribute("group-id", String.valueOf(groupId));
198         header.addAttribute("private-layout", String.valueOf(privateLayout));
199         header.addAttribute("theme-id", layoutSet.getThemeId());
200         header.addAttribute("color-scheme-id", layoutSet.getColorSchemeId());
201 
202         // Layout Configuration Portlet
203 
204         Portlet layoutConfigurationPortlet =
205             PortletLocalServiceUtil.getPortletById(
206                 context.getCompanyId(), PortletKeys.LAYOUT_CONFIGURATION);
207 
208         // Layouts
209 
210         Map<String, Object[]> portletIds =
211             new LinkedHashMap<String, Object[]>();
212 
213         List<Layout> layouts = null;
214 
215         if ((layoutIds == null) || (layoutIds.length == 0)) {
216             layouts = LayoutLocalServiceUtil.getLayouts(groupId, privateLayout);
217         }
218         else {
219             layouts = LayoutLocalServiceUtil.getLayouts(
220                 groupId, privateLayout, layoutIds);
221         }
222 
223         Element layoutsEl = root.addElement("layouts");
224 
225         for (Layout layout : layouts) {
226             context.setPlid(layout.getPlid());
227 
228             Document layoutDoc = SAXReaderUtil.createDocument();
229 
230             Element layoutEl = layoutDoc.addElement("layout");
231 
232             layoutEl.addAttribute("old-plid", String.valueOf(layout.getPlid()));
233             layoutEl.addAttribute(
234                 "layout-id", String.valueOf(layout.getLayoutId()));
235             layoutEl.addElement("parent-layout-id").addText(
236                 String.valueOf(layout.getParentLayoutId()));
237             layoutEl.addElement("name").addCDATA(layout.getName());
238             layoutEl.addElement("title").addCDATA(layout.getTitle());
239             layoutEl.addElement("description").addText(layout.getDescription());
240             layoutEl.addElement("type").addText(layout.getType());
241             layoutEl.addElement("type-settings").addCDATA(
242                 layout.getTypeSettings());
243             layoutEl.addElement("hidden").addText(
244                 String.valueOf(layout.getHidden()));
245             layoutEl.addElement("friendly-url").addText(
246                 layout.getFriendlyURL());
247             layoutEl.addElement("icon-image").addText(
248                 String.valueOf(layout.getIconImage()));
249 
250             if (layout.isIconImage()) {
251                 Image image = ImageLocalServiceUtil.getImage(
252                     layout.getIconImageId());
253 
254                 if (image != null) {
255                     String iconPath = getLayoutIconPath(context, layout, image);
256 
257                     layoutEl.addElement("icon-image-path").addText(
258                         iconPath);
259 
260                     context.addZipEntry(iconPath, image.getTextObj());
261                 }
262             }
263 
264             layoutEl.addElement("theme-id").addText(layout.getThemeId());
265             layoutEl.addElement("color-scheme-id").addText(
266                 layout.getColorSchemeId());
267             layoutEl.addElement("wap-theme-id").addText(layout.getWapThemeId());
268             layoutEl.addElement("wap-color-scheme-id").addText(
269                 layout.getWapColorSchemeId());
270             layoutEl.addElement("css").addCDATA(layout.getCss());
271             layoutEl.addElement("priority").addText(
272                 String.valueOf(layout.getPriority()));
273 
274             // Layout permissions
275 
276             if (exportPermissions) {
277                 Element permissionsEl = layoutEl.addElement("permissions");
278 
279                 String resourceName = Layout.class.getName();
280                 String resourcePrimKey = String.valueOf(layout.getPlid());
281 
282                 if (PropsValues.PERMISSIONS_USER_CHECK_ALGORITHM == 5) {
283                     exportLayoutPermissions_5(
284                         layoutCache, companyId, groupId, resourceName,
285                         resourcePrimKey, permissionsEl);
286                 }
287                 else {
288                     exportLayoutPermissions_4(
289                         layoutCache, companyId, groupId, guestGroup,
290                         resourceName, resourcePrimKey, permissionsEl,
291                         exportUserPermissions);
292                 }
293             }
294 
295             if (layout.getType().equals(LayoutConstants.TYPE_PORTLET)) {
296                 LayoutTypePortlet layoutTypePortlet =
297                     (LayoutTypePortlet)layout.getLayoutType();
298 
299                 for (String portletId : layoutTypePortlet.getPortletIds()) {
300                     String key = PortletPermissionUtil.getPrimaryKey(
301                         layout.getPlid(), portletId);
302 
303                     portletIds.put(
304                         key, new Object[] {portletId, layout.getPlid()});
305                 }
306             }
307 
308             String layoutPath = context.getLayoutPath(layout.getLayoutId()) +
309                 "/layout.xml";
310 
311             Element el = layoutsEl.addElement("layout");
312 
313             el.addAttribute("layout-id", String.valueOf(layout.getLayoutId()));
314             el.addAttribute("path", layoutPath);
315 
316             _portletExporter.exportPortletData(
317                 context, layoutConfigurationPortlet, layout, null, layoutEl);
318 
319             try {
320                 context.addZipEntry(layoutPath, layoutDoc.formattedString());
321             }
322             catch (IOException ioe) {
323             }
324         }
325 
326         if (PropsValues.PERMISSIONS_USER_CHECK_ALGORITHM != 5) {
327             Element rolesEl = root.addElement("roles");
328 
329             // Layout roles
330 
331             if (exportPermissions) {
332                 exportLayoutRoles(layoutCache, companyId, groupId, rolesEl);
333             }
334         }
335 
336         // Export Portlets
337 
338         Element portletsEl = root.addElement("portlets");
339 
340         for (Map.Entry<String, Object[]> portletIdsEntry :
341                 portletIds.entrySet()) {
342 
343             String portletId = (String)portletIdsEntry.getValue()[0];
344             String rootPortletId = PortletConstants.getRootPortletId(portletId);
345             long plid = (Long)portletIdsEntry.getValue()[1];
346 
347             Layout layout = LayoutUtil.findByPrimaryKey(plid);
348 
349             context.setPlid(layout.getPlid());
350             context.setOldPlid(layout.getPlid());
351 
352             boolean exportCurPortletData = exportPortletData;
353             boolean exportCurPortletSetup = exportPortletSetup;
354 
355             Portlet portlet = PortletLocalServiceUtil.getPortletById(
356                 context.getCompanyId(), portletId);
357 
358             if (portlet != null) {
359                 String portletDataHandlerClass =
360                     portlet.getPortletDataHandlerClass();
361 
362                 if (portletDataHandlerClass != null) {
363                     exportCurPortletData =
364                         exportPortletData &&
365                         MapUtil.getBoolean(
366                             parameterMap,
367                             PortletDataHandlerKeys.PORTLET_DATA +
368                                 StringPool.UNDERLINE + rootPortletId);
369 
370                     exportCurPortletSetup =
371                         exportPortletSetup &&
372                         MapUtil.getBoolean(
373                             parameterMap,
374                             PortletDataHandlerKeys.PORTLET_SETUP +
375                                 StringPool.UNDERLINE + rootPortletId);
376                 }
377             }
378 
379             _portletExporter.exportPortlet(
380                 context, layoutCache, portletId, layout, portletsEl,
381                 defaultUserId, exportPermissions, exportPortletArchivedSetups,
382                 exportCurPortletData, exportCurPortletSetup,
383                 exportPortletUserPreferences, exportUserPermissions);
384         }
385 
386         // Comments
387 
388         _portletExporter.exportComments(context, root);
389 
390         // Ratings
391 
392         _portletExporter.exportRatings(context, root);
393 
394         // Tags
395 
396         _portletExporter.exportTags(context, root);
397 
398         // Look and feel
399 
400         InputStream themeZip = null;
401 
402         try {
403             if (exportTheme) {
404                 themeZip = exportTheme(layoutSet).getFileInputStream();
405             }
406         }
407         catch (IOException ioe) {
408             throw new SystemException(ioe);
409         }
410 
411         // Log
412 
413         if (_log.isInfoEnabled()) {
414             _log.info("Exporting layouts takes " + stopWatch.getTime() + " ms");
415         }
416 
417         // Zip
418 
419         try {
420             context.addZipEntry("/manifest.xml", doc.formattedString());
421 
422             if (themeZip != null) {
423                 context.addZipEntry("/theme.zip", themeZip);
424             }
425 
426             return zipWriter.finishWithStream();
427         }
428         catch (IOException ioe) {
429             throw new SystemException(ioe);
430         }
431     }
432 
433     protected void exportLayoutPermissions_4(
434             LayoutCache layoutCache, long companyId, long groupId,
435             Group guestGroup, String resourceName, String resourcePrimKey,
436             Element permissionsEl, boolean exportUserPermissions)
437         throws SystemException {
438 
439         _portletExporter.exportGroupPermissions(
440             companyId, groupId, resourceName, resourcePrimKey, permissionsEl,
441             "community-actions");
442 
443         if (groupId != guestGroup.getGroupId()) {
444             _portletExporter.exportGroupPermissions(
445                 companyId, guestGroup.getGroupId(), resourceName,
446                 resourcePrimKey, permissionsEl, "guest-actions");
447         }
448 
449         if (exportUserPermissions) {
450             _portletExporter.exportUserPermissions(
451                 layoutCache, companyId, groupId, resourceName, resourcePrimKey,
452                 permissionsEl);
453         }
454 
455         _portletExporter.exportInheritedPermissions(
456             layoutCache, companyId, resourceName, resourcePrimKey,
457             permissionsEl, "organization");
458 
459         _portletExporter.exportInheritedPermissions(
460             layoutCache, companyId, resourceName, resourcePrimKey,
461             permissionsEl, "location");
462 
463         _portletExporter.exportInheritedPermissions(
464             layoutCache, companyId, resourceName, resourcePrimKey,
465             permissionsEl, "user-group");
466     }
467 
468     protected void exportLayoutPermissions_5(
469             LayoutCache layoutCache, long companyId, long groupId,
470             String resourceName, String resourcePrimKey, Element permissionsEl)
471         throws PortalException, SystemException {
472 
473         boolean portletActions = false;
474 
475         Resource resource = layoutCache.getResource(
476             companyId, groupId, resourceName,
477             ResourceConstants.SCOPE_INDIVIDUAL, resourcePrimKey,
478             portletActions);
479 
480         _portletExporter.exportPermissions_5(
481             layoutCache, groupId, resourceName, resource.getResourceId(),
482             permissionsEl);
483     }
484 
485     protected void exportLayoutRoles(
486             LayoutCache layoutCache, long companyId, long groupId,
487             Element rolesEl)
488         throws SystemException {
489 
490         String resourceName = Layout.class.getName();
491 
492         _portletExporter.exportGroupRoles(
493             layoutCache, companyId, groupId, resourceName, "community",
494             rolesEl);
495 
496         _portletExporter.exportUserRoles(
497         layoutCache, companyId, groupId, resourceName, rolesEl);
498 
499         _portletExporter.exportInheritedRoles(
500             layoutCache, companyId, groupId, resourceName, "organization",
501             rolesEl);
502 
503         _portletExporter.exportInheritedRoles(
504             layoutCache, companyId, groupId, resourceName, "location", rolesEl);
505 
506         _portletExporter.exportInheritedRoles(
507             layoutCache, companyId, groupId, resourceName, "user-group",
508             rolesEl);
509     }
510 
511     protected FileCacheOutputStream exportTheme(LayoutSet layoutSet)
512         throws IOException, SystemException {
513 
514         Theme theme = layoutSet.getTheme();
515 
516         ZipWriter zipWriter = new ZipWriter();
517 
518         String lookAndFeelXML = ContentUtil.get(
519             "com/liferay/portal/dependencies/liferay-look-and-feel.xml.tmpl");
520 
521         lookAndFeelXML = StringUtil.replace(
522             lookAndFeelXML,
523             new String[] {
524                 "[$TEMPLATE_EXTENSION$]", "[$VIRTUAL_PATH$]"
525             },
526             new String[] {
527                 theme.getTemplateExtension(), theme.getVirtualPath()
528             }
529         );
530 
531         zipWriter.addEntry("liferay-look-and-feel.xml", lookAndFeelXML);
532 
533         String servletContextName = theme.getServletContextName();
534 
535         ServletContext servletContext = VelocityContextPool.get(
536             servletContextName);
537 
538         if (servletContext == null) {
539             if (_log.isWarnEnabled()) {
540                 _log.warn(
541                     "Servlet context not found for theme " +
542                         theme.getThemeId());
543             }
544 
545             return null;
546         }
547 
548         File cssPath = null;
549         File imagesPath = null;
550         File javaScriptPath = null;
551         File templatesPath = null;
552 
553         if (!theme.isLoadFromServletContext()) {
554             ThemeLoader themeLoader = ThemeLoaderFactory.getThemeLoader(
555                 servletContextName);
556 
557             if (themeLoader == null) {
558                 _log.error(
559                     servletContextName + " does not map to a theme loader");
560             }
561             else {
562                 String realPath =
563                     themeLoader.getFileStorage().getPath() + "/" +
564                         theme.getName();
565 
566                 cssPath = new File(realPath + "/css");
567                 imagesPath = new File(realPath + "/images");
568                 javaScriptPath = new File(realPath + "/javascript");
569                 templatesPath = new File(realPath + "/templates");
570             }
571         }
572         else {
573             cssPath = new File(servletContext.getRealPath(theme.getCssPath()));
574             imagesPath = new File(
575                 servletContext.getRealPath(theme.getImagesPath()));
576             javaScriptPath = new File(
577                 servletContext.getRealPath(theme.getJavaScriptPath()));
578             templatesPath = new File(
579                 servletContext.getRealPath(theme.getTemplatesPath()));
580         }
581 
582         exportThemeFiles("css", cssPath, zipWriter);
583         exportThemeFiles("images", imagesPath, zipWriter);
584         exportThemeFiles("javascript", javaScriptPath, zipWriter);
585         exportThemeFiles("templates", templatesPath, zipWriter);
586 
587         return zipWriter.finishWithStream();
588     }
589 
590     protected void exportThemeFiles(String path, File dir, ZipWriter zipWriter)
591         throws IOException {
592 
593         if ((dir == null) || (!dir.exists())) {
594             return;
595         }
596 
597         File[] files = dir.listFiles();
598 
599         for (int i = 0; i < files.length; i++) {
600             File file = files[i];
601 
602             if (file.isDirectory()) {
603                 exportThemeFiles(path + "/" + file.getName(), file, zipWriter);
604             }
605             else {
606                 zipWriter.addEntry(
607                     path + "/" + file.getName(), FileUtil.getBytes(file));
608             }
609         }
610     }
611 
612     protected String getLayoutIconPath(
613         PortletDataContext context, Layout layout, Image image) {
614 
615         StringBuilder sb = new StringBuilder();
616 
617         sb.append(context.getLayoutPath(layout.getLayoutId()));
618         sb.append("/icons/");
619         sb.append(image.getImageId());
620         sb.append(StringPool.PERIOD);
621         sb.append(image.getType());
622 
623         return sb.toString();
624     }
625 
626     private static Log _log = LogFactory.getLog(LayoutExporter.class);
627 
628     private PortletExporter _portletExporter = new PortletExporter();
629 
630 }