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