/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.service.impl; import com.liferay.exportimport.kernel.staging.LayoutStagingUtil; import com.liferay.exportimport.kernel.staging.MergeLayoutPrototypesThreadLocal; import com.liferay.exportimport.kernel.staging.StagingUtil; import com.liferay.portal.kernel.bean.BeanReference; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.Layout; import com.liferay.portal.kernel.model.LayoutRevision; import com.liferay.portal.kernel.model.LayoutSet; import com.liferay.portal.kernel.model.LayoutStagingHandler; import com.liferay.portal.kernel.model.SystemEventConstants; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.security.auth.PrincipalThreadLocal; import com.liferay.portal.kernel.service.LayoutFriendlyURLLocalServiceUtil; import com.liferay.portal.kernel.service.LayoutLocalService; import com.liferay.portal.kernel.service.LayoutRevisionLocalServiceUtil; import com.liferay.portal.kernel.service.ServiceContext; import com.liferay.portal.kernel.service.ServiceContextThreadLocal; import com.liferay.portal.kernel.service.SystemEventLocalServiceUtil; import com.liferay.portal.kernel.service.UserLocalServiceUtil; import com.liferay.portal.kernel.service.persistence.LayoutRevisionUtil; import com.liferay.portal.kernel.service.persistence.LayoutUtil; import com.liferay.portal.kernel.systemevent.SystemEventHierarchyEntry; import com.liferay.portal.kernel.systemevent.SystemEventHierarchyEntryThreadLocal; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.ClassLoaderUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.LocaleUtil; import com.liferay.portal.kernel.util.ObjectValuePair; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.util.ProxyUtil; import com.liferay.portal.kernel.workflow.WorkflowConstants; import com.liferay.portlet.exportimport.staging.ProxiedLayoutsThreadLocal; import com.liferay.portlet.exportimport.staging.StagingAdvicesThreadLocal; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.core.annotation.Order; /** * @author Raymond Augé * @author Brian Wing Shun Chan */ @Order(1) public class LayoutLocalServiceStagingAdvice implements MethodInterceptor { public LayoutLocalServiceStagingAdvice() { if (_log.isDebugEnabled()) { _log.debug("Instantiating " + hashCode()); } } public void deleteLayout( LayoutLocalService layoutLocalService, Layout layout, boolean updateLayoutSet, ServiceContext serviceContext) throws PortalException { long layoutSetBranchId = ParamUtil.getLong( serviceContext, "layoutSetBranchId"); if (layoutSetBranchId > 0) { LayoutRevisionLocalServiceUtil.deleteLayoutRevisions( layoutSetBranchId, layout.getPlid()); List<LayoutRevision> notIncompleteLayoutRevisions = LayoutRevisionUtil.findByP_NotS( layout.getPlid(), WorkflowConstants.STATUS_INCOMPLETE); if (notIncompleteLayoutRevisions.isEmpty()) { LayoutRevisionLocalServiceUtil.deleteLayoutLayoutRevisions( layout.getPlid()); doDeleteLayout( layoutLocalService, layout, updateLayoutSet, serviceContext); } } else { doDeleteLayout( layoutLocalService, layout, updateLayoutSet, serviceContext); } } public void deleteLayout( LayoutLocalService layoutLocalService, long groupId, boolean privateLayout, long layoutId, ServiceContext serviceContext) throws PortalException { Layout layout = layoutLocalService.getLayout( groupId, privateLayout, layoutId); deleteLayout(layoutLocalService, layout, true, serviceContext); } @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { if (!StagingAdvicesThreadLocal.isEnabled()) { return methodInvocation.proceed(); } Method method = methodInvocation.getMethod(); String methodName = method.getName(); if (!_layoutLocalServiceStagingAdviceMethodNames.contains(methodName)) { return wrapReturnValue(methodInvocation.proceed(), false); } Object returnValue = null; Class<?>[] parameterTypes = method.getParameterTypes(); Object thisObject = methodInvocation.getThis(); Object[] arguments = methodInvocation.getArguments(); if (methodName.equals("createLayout")) { return methodInvocation.proceed(); } else if (methodName.equals("deleteLayout")) { if (arguments.length == 3) { deleteLayout( (LayoutLocalService)thisObject, (Layout)arguments[0], (Boolean)arguments[1], (ServiceContext)arguments[2]); } else if (arguments.length == 4) { deleteLayout( (LayoutLocalService)thisObject, (Long)arguments[0], (Boolean)arguments[1], (Long)arguments[2], (ServiceContext)arguments[3]); } else { return wrapReturnValue(methodInvocation.proceed(), false); } } else if (methodName.equals("getLayouts")) { boolean showIncomplete = false; if (arguments.length == 6) { showIncomplete = (Boolean)arguments[3]; } else if (Arrays.equals(parameterTypes, _GET_LAYOUTS_TYPES)) { showIncomplete = true; } return wrapReturnValue(methodInvocation.proceed(), showIncomplete); } else if (methodName.equals("updateLayout") && (arguments.length == 15)) { Map<Locale, String> friendlyURLMap = null; if (Arrays.equals(parameterTypes, _UPDATE_LAYOUT_PARAMETER_TYPES)) { friendlyURLMap = new HashMap<>(); friendlyURLMap.put( LocaleUtil.getSiteDefault(), (String)arguments[11]); } else { friendlyURLMap = (Map<Locale, String>)arguments[11]; } returnValue = updateLayout( (LayoutLocalService)thisObject, (Long)arguments[0], (Boolean)arguments[1], (Long)arguments[2], (Long)arguments[3], (Map<Locale, String>)arguments[4], (Map<Locale, String>)arguments[5], (Map<Locale, String>)arguments[6], (Map<Locale, String>)arguments[7], (Map<Locale, String>)arguments[8], (String)arguments[9], (Boolean)arguments[10], friendlyURLMap, (Boolean)arguments[12], (byte[])arguments[13], (ServiceContext)arguments[14]); } else { try { Class<?> clazz = getClass(); parameterTypes = ArrayUtil.append( new Class<?>[] {LayoutLocalService.class}, parameterTypes); Method layoutLocalServiceStagingAdviceMethod = clazz.getMethod( methodName, parameterTypes); arguments = ArrayUtil.append( new Object[] {thisObject}, arguments); returnValue = layoutLocalServiceStagingAdviceMethod.invoke( this, arguments); } catch (InvocationTargetException ite) { throw ite.getTargetException(); } catch (NoSuchMethodException nsme) { returnValue = methodInvocation.proceed(); } } returnValue = wrapReturnValue(returnValue, false); return returnValue; } public Layout updateLayout( LayoutLocalService layoutLocalService, long groupId, boolean privateLayout, long layoutId, long parentLayoutId, Map<Locale, String> nameMap, Map<Locale, String> titleMap, Map<Locale, String> descriptionMap, Map<Locale, String> keywordsMap, Map<Locale, String> robotsMap, String type, boolean hidden, Map<Locale, String> friendlyURLMap, boolean iconImage, byte[] iconBytes, ServiceContext serviceContext) throws PortalException { // Layout parentLayoutId = layoutLocalServiceHelper.getParentLayoutId( groupId, privateLayout, parentLayoutId); String name = nameMap.get(LocaleUtil.getSiteDefault()); Map<Locale, String> layoutFriendlyURLMap = layoutLocalServiceHelper.getFriendlyURLMap( groupId, privateLayout, layoutId, name, friendlyURLMap); String friendlyURL = layoutFriendlyURLMap.get( LocaleUtil.getSiteDefault()); layoutLocalServiceHelper.validate( groupId, privateLayout, layoutId, parentLayoutId, name, type, hidden, layoutFriendlyURLMap, serviceContext); layoutLocalServiceHelper.validateParentLayoutId( groupId, privateLayout, layoutId, parentLayoutId); Layout layout = LayoutUtil.findByG_P_L( groupId, privateLayout, layoutId); if (LayoutStagingUtil.isBranchingLayout(layout)) { layout = getProxiedLayout(layout); } LayoutRevision layoutRevision = LayoutStagingUtil.getLayoutRevision( layout); if (layoutRevision == null) { return layoutLocalService.updateLayout( groupId, privateLayout, layoutId, parentLayoutId, nameMap, titleMap, descriptionMap, keywordsMap, robotsMap, type, hidden, friendlyURLMap, iconImage, iconBytes, serviceContext); } layoutLocalService.updateAsset( serviceContext.getUserId(), layout, serviceContext.getAssetCategoryIds(), serviceContext.getAssetTagNames()); if (parentLayoutId != layout.getParentLayoutId()) { int priority = layoutLocalServiceHelper.getNextPriority( groupId, privateLayout, parentLayoutId, layout.getSourcePrototypeLayoutUuid(), -1); layout.setPriority(priority); } layout.setParentLayoutId(parentLayoutId); layoutRevision.setNameMap(nameMap); layoutRevision.setTitleMap(titleMap); layoutRevision.setDescriptionMap(descriptionMap); layoutRevision.setKeywordsMap(keywordsMap); layoutRevision.setRobotsMap(robotsMap); layout.setType(type); layout.setHidden(hidden); layout.setFriendlyURL(friendlyURL); PortalUtil.updateImageId( layoutRevision, iconImage, iconBytes, "iconImageId", 0, 0, 0); boolean layoutPrototypeLinkEnabled = ParamUtil.getBoolean( serviceContext, "layoutPrototypeLinkEnabled"); layout.setLayoutPrototypeLinkEnabled(layoutPrototypeLinkEnabled); layout.setExpandoBridgeAttributes(serviceContext); LayoutUtil.update(layout); LayoutFriendlyURLLocalServiceUtil.updateLayoutFriendlyURLs( layout.getUserId(), layout.getCompanyId(), layout.getGroupId(), layout.getPlid(), layout.isPrivateLayout(), layoutFriendlyURLMap, serviceContext); boolean hasWorkflowTask = StagingUtil.hasWorkflowTask( serviceContext.getUserId(), layoutRevision); serviceContext.setAttribute("revisionInProgress", hasWorkflowTask); int workflowAction = serviceContext.getWorkflowAction(); try { serviceContext.setWorkflowAction( WorkflowConstants.ACTION_SAVE_DRAFT); LayoutRevisionLocalServiceUtil.updateLayoutRevision( serviceContext.getUserId(), layoutRevision.getLayoutRevisionId(), layoutRevision.getLayoutBranchId(), layoutRevision.getName(), layoutRevision.getTitle(), layoutRevision.getDescription(), layoutRevision.getKeywords(), layoutRevision.getRobots(), layoutRevision.getTypeSettings(), layoutRevision.getIconImage(), layoutRevision.getIconImageId(), layoutRevision.getThemeId(), layoutRevision.getColorSchemeId(), layoutRevision.getCss(), serviceContext); } finally { serviceContext.setWorkflowAction(workflowAction); } return layout; } public Layout updateLayout( LayoutLocalService layoutLocalService, long groupId, boolean privateLayout, long layoutId, String typeSettings) throws PortalException { Layout layout = LayoutUtil.findByG_P_L( groupId, privateLayout, layoutId); if (LayoutStagingUtil.isBranchingLayout(layout)) { layout = getProxiedLayout(layout); } LayoutRevision layoutRevision = LayoutStagingUtil.getLayoutRevision( layout); if (layoutRevision == null) { return layoutLocalService.updateLayout( groupId, privateLayout, layoutId, typeSettings); } layout.setTypeSettings(typeSettings); ServiceContext serviceContext = ServiceContextThreadLocal.getServiceContext(); boolean hasWorkflowTask = StagingUtil.hasWorkflowTask( serviceContext.getUserId(), layoutRevision); serviceContext.setAttribute("revisionInProgress", hasWorkflowTask); if (!MergeLayoutPrototypesThreadLocal.isInProgress()) { serviceContext.setWorkflowAction( WorkflowConstants.ACTION_SAVE_DRAFT); } LayoutRevisionLocalServiceUtil.updateLayoutRevision( serviceContext.getUserId(), layoutRevision.getLayoutRevisionId(), layoutRevision.getLayoutBranchId(), layoutRevision.getName(), layoutRevision.getTitle(), layoutRevision.getDescription(), layoutRevision.getKeywords(), layoutRevision.getRobots(), layoutRevision.getTypeSettings(), layoutRevision.getIconImage(), layoutRevision.getIconImageId(), layoutRevision.getThemeId(), layoutRevision.getColorSchemeId(), layoutRevision.getCss(), serviceContext); return layout; } public Layout updateLookAndFeel( LayoutLocalService layoutLocalService, long groupId, boolean privateLayout, long layoutId, String themeId, String colorSchemeId, String css) throws PortalException { Layout layout = LayoutUtil.findByG_P_L( groupId, privateLayout, layoutId); if (LayoutStagingUtil.isBranchingLayout(layout)) { layout = getProxiedLayout(layout); } LayoutRevision layoutRevision = LayoutStagingUtil.getLayoutRevision( layout); if (layoutRevision == null) { return layoutLocalService.updateLookAndFeel( groupId, privateLayout, layoutId, themeId, colorSchemeId, css); } layout.setThemeId(themeId); layout.setColorSchemeId(colorSchemeId); layout.setCss(css); ServiceContext serviceContext = ServiceContextThreadLocal.getServiceContext(); boolean hasWorkflowTask = StagingUtil.hasWorkflowTask( serviceContext.getUserId(), layoutRevision); serviceContext.setAttribute("revisionInProgress", hasWorkflowTask); if (!MergeLayoutPrototypesThreadLocal.isInProgress()) { serviceContext.setWorkflowAction( WorkflowConstants.ACTION_SAVE_DRAFT); } LayoutRevisionLocalServiceUtil.updateLayoutRevision( serviceContext.getUserId(), layoutRevision.getLayoutRevisionId(), layoutRevision.getLayoutBranchId(), layoutRevision.getName(), layoutRevision.getTitle(), layoutRevision.getDescription(), layoutRevision.getKeywords(), layoutRevision.getRobots(), layoutRevision.getTypeSettings(), layoutRevision.getIconImage(), layoutRevision.getIconImageId(), layoutRevision.getThemeId(), layoutRevision.getColorSchemeId(), layoutRevision.getCss(), serviceContext); return layout; } public Layout updateName( LayoutLocalService layoutLocalService, Layout layout, String name, String languageId) throws PortalException { layout = wrapLayout(layout); LayoutRevision layoutRevision = LayoutStagingUtil.getLayoutRevision( layout); if (layoutRevision == null) { return layoutLocalService.updateName(layout, name, languageId); } layoutLocalServiceHelper.validateName(name, languageId); layout.setName(name, LocaleUtil.fromLanguageId(languageId)); ServiceContext serviceContext = ServiceContextThreadLocal.getServiceContext(); boolean hasWorkflowTask = StagingUtil.hasWorkflowTask( serviceContext.getUserId(), layoutRevision); serviceContext.setAttribute("revisionInProgress", hasWorkflowTask); serviceContext.setWorkflowAction(WorkflowConstants.ACTION_SAVE_DRAFT); LayoutRevisionLocalServiceUtil.updateLayoutRevision( serviceContext.getUserId(), layoutRevision.getLayoutRevisionId(), layoutRevision.getLayoutBranchId(), layoutRevision.getName(), layoutRevision.getTitle(), layoutRevision.getDescription(), layoutRevision.getKeywords(), layoutRevision.getRobots(), layoutRevision.getTypeSettings(), layoutRevision.getIconImage(), layoutRevision.getIconImageId(), layoutRevision.getThemeId(), layoutRevision.getColorSchemeId(), layoutRevision.getCss(), serviceContext); return layout; } protected void doDeleteLayout( LayoutLocalService layoutLocalService, Layout layout, boolean updateLayoutSet, ServiceContext serviceContext) throws PortalException { if (SystemEventHierarchyEntryThreadLocal.push( Layout.class, layout.getPlid()) == null) { layoutLocalService.deleteLayout( layout, updateLayoutSet, serviceContext); } else { try { layoutLocalService.deleteLayout( layout, updateLayoutSet, serviceContext); SystemEventHierarchyEntry systemEventHierarchyEntry = SystemEventHierarchyEntryThreadLocal.peek(); SystemEventLocalServiceUtil.addSystemEvent( 0, layout.getGroupId(), Layout.class.getName(), layout.getPlid(), layout.getUuid(), null, SystemEventConstants.TYPE_DELETE, systemEventHierarchyEntry.getExtraData()); } finally { SystemEventHierarchyEntryThreadLocal.pop( Layout.class, layout.getPlid()); } } } protected Layout getProxiedLayout(Layout layout) { ObjectValuePair<ServiceContext, Map<Layout, Object>> objectValuePair = ProxiedLayoutsThreadLocal.getProxiedLayouts(); ServiceContext currentServiceContext = ServiceContextThreadLocal.getServiceContext(); if (objectValuePair != null) { ServiceContext serviceContext = objectValuePair.getKey(); if (serviceContext == currentServiceContext) { Map<Layout, Object> proxiedLayouts = objectValuePair.getValue(); Object proxiedLayout = proxiedLayouts.get(layout); if (proxiedLayout != null) { return (Layout)proxiedLayout; } proxiedLayout = ProxyUtil.newProxyInstance( ClassLoaderUtil.getPortalClassLoader(), new Class<?>[] {Layout.class}, new LayoutStagingHandler(layout)); proxiedLayouts.put(layout, proxiedLayout); return (Layout)proxiedLayout; } } Object proxiedLayout = ProxyUtil.newProxyInstance( ClassLoaderUtil.getPortalClassLoader(), new Class<?>[] {Layout.class}, new LayoutStagingHandler(layout)); Map<Layout, Object> proxiedLayouts = new HashMap<>(); proxiedLayouts.put(layout, proxiedLayout); ProxiedLayoutsThreadLocal.setProxiedLayouts( new ObjectValuePair<>(currentServiceContext, proxiedLayouts)); return (Layout)proxiedLayout; } protected Layout unwrapLayout(Layout layout) { LayoutStagingHandler layoutStagingHandler = LayoutStagingUtil.getLayoutStagingHandler(layout); if (layoutStagingHandler == null) { return layout; } return layoutStagingHandler.getLayout(); } protected Layout wrapLayout(Layout layout) { LayoutStagingHandler layoutStagingHandler = LayoutStagingUtil.getLayoutStagingHandler(layout); if (layoutStagingHandler != null) { return layout; } if (!LayoutStagingUtil.isBranchingLayout(layout)) { return layout; } return getProxiedLayout(layout); } protected List<Layout> wrapLayouts( List<Layout> layouts, boolean showIncomplete) { if (layouts.isEmpty()) { return layouts; } Layout firstLayout = layouts.get(0); Layout wrappedFirstLayout = wrapLayout(firstLayout); if (wrappedFirstLayout == firstLayout) { return layouts; } long layoutSetBranchId = 0; if (!showIncomplete) { long userId = 0; try { userId = GetterUtil.getLong(PrincipalThreadLocal.getName()); if (userId > 0) { User user = UserLocalServiceUtil.getUser(userId); LayoutSet layoutSet = firstLayout.getLayoutSet(); layoutSetBranchId = StagingUtil.getRecentLayoutSetBranchId( user, layoutSet.getLayoutSetId()); } } catch (Exception e) { if (_log.isDebugEnabled()) { _log.debug("No layout set branch found for user " + userId); } } } List<Layout> wrappedLayouts = new ArrayList<>(layouts.size()); for (int i = 0; i < layouts.size(); i++) { Layout wrappedLayout = wrapLayout(layouts.get(i)); if (showIncomplete || !StagingUtil.isIncomplete(wrappedLayout, layoutSetBranchId)) { wrappedLayouts.add(wrappedLayout); } } return wrappedLayouts; } protected Object wrapReturnValue( Object returnValue, boolean showIncomplete) { if (returnValue instanceof Layout) { returnValue = wrapLayout((Layout)returnValue); } else if (returnValue instanceof List<?>) { List<?> list = (List<?>)returnValue; if (!list.isEmpty()) { Object object = list.get(0); if (object instanceof Layout) { returnValue = wrapLayouts( (List<Layout>)returnValue, showIncomplete); } } } return returnValue; } @BeanReference(type = LayoutLocalServiceHelper.class) protected LayoutLocalServiceHelper layoutLocalServiceHelper; private static final Class<?>[] _GET_LAYOUTS_TYPES = { Long.TYPE, Boolean.TYPE, Long.TYPE }; private static final Class<?>[] _UPDATE_LAYOUT_PARAMETER_TYPES = { long.class, boolean.class, long.class, long.class, Map.class, Map.class, Map.class, Map.class, Map.class, String.class, boolean.class, String.class, Boolean.class, byte[].class, ServiceContext.class }; private static final Log _log = LogFactoryUtil.getLog( LayoutLocalServiceStagingAdvice.class); private static final Set<String> _layoutLocalServiceStagingAdviceMethodNames = new HashSet<>(); static { _layoutLocalServiceStagingAdviceMethodNames.add("createLayout"); _layoutLocalServiceStagingAdviceMethodNames.add("deleteLayout"); _layoutLocalServiceStagingAdviceMethodNames.add("getLayouts"); _layoutLocalServiceStagingAdviceMethodNames.add("updateLayout"); _layoutLocalServiceStagingAdviceMethodNames.add("updateLookAndFeel"); _layoutLocalServiceStagingAdviceMethodNames.add("updateName"); } }