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