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