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