001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.service.impl;
016    
017    import com.liferay.portal.kernel.dao.orm.DynamicQuery;
018    import com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil;
019    import com.liferay.portal.kernel.dao.orm.Property;
020    import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil;
021    import com.liferay.portal.kernel.exception.SystemException;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.staging.MergeLayoutPrototypesThreadLocal;
025    import com.liferay.portal.kernel.util.AutoResetThreadLocal;
026    import com.liferay.portal.kernel.util.ListUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.kernel.workflow.WorkflowThreadLocal;
029    import com.liferay.portal.model.Group;
030    import com.liferay.portal.model.GroupConstants;
031    import com.liferay.portal.model.Layout;
032    import com.liferay.portal.model.LayoutConstants;
033    import com.liferay.portal.model.LayoutSet;
034    import com.liferay.portal.model.UserGroup;
035    import com.liferay.portal.model.impl.VirtualLayout;
036    import com.liferay.portal.service.GroupLocalServiceUtil;
037    import com.liferay.portal.service.LayoutLocalServiceUtil;
038    import com.liferay.portal.service.LayoutSetLocalServiceUtil;
039    import com.liferay.portal.service.UserGroupLocalServiceUtil;
040    import com.liferay.portal.util.PropsValues;
041    import com.liferay.portlet.sites.util.SitesUtil;
042    
043    import java.lang.reflect.Method;
044    
045    import java.util.ArrayList;
046    import java.util.Arrays;
047    import java.util.List;
048    
049    import org.aopalliance.intercept.MethodInterceptor;
050    import org.aopalliance.intercept.MethodInvocation;
051    
052    import org.springframework.core.annotation.Order;
053    
054    /**
055     * @author Raymond Augé
056     * @author Jorge Ferrer
057     */
058    @Order(2)
059    public class LayoutLocalServiceVirtualLayoutsAdvice
060            implements MethodInterceptor {
061    
062            public Object invoke(MethodInvocation methodInvocation) throws Throwable {
063                    if (MergeLayoutPrototypesThreadLocal.isInProgress()) {
064                            return methodInvocation.proceed();
065                    }
066    
067                    Method method = methodInvocation.getMethod();
068    
069                    String methodName = method.getName();
070    
071                    Object[] arguments = methodInvocation.getArguments();
072    
073                    Class<?>[] parameterTypes = method.getParameterTypes();
074    
075                    if (MergeLayoutPrototypesThreadLocal.isMergeComplete(
076                                    methodName, arguments, parameterTypes)) {
077    
078                            return methodInvocation.proceed();
079                    }
080    
081                    boolean workflowEnabled = WorkflowThreadLocal.isEnabled();
082    
083                    if (methodName.equals("getLayout") &&
084                            (Arrays.equals(parameterTypes, _TYPES_L) ||
085                             Arrays.equals(parameterTypes, _TYPES_L_B_L))) {
086    
087                            Layout layout = (Layout)methodInvocation.proceed();
088    
089                            if (Validator.isNull(layout.getLayoutPrototypeUuid()) &&
090                                    Validator.isNull(layout.getSourcePrototypeLayoutUuid())) {
091    
092                                    return layout;
093                            }
094    
095                            Group group = layout.getGroup();
096                            LayoutSet layoutSet = layout.getLayoutSet();
097    
098                            try {
099                                    MergeLayoutPrototypesThreadLocal.setInProgress(true);
100                                    WorkflowThreadLocal.setEnabled(false);
101    
102                                    SitesUtil.mergeLayoutPrototypeLayout(group, layout);
103    
104                                    if (Validator.isNotNull(
105                                                    layout.getSourcePrototypeLayoutUuid())) {
106    
107                                            if (!SitesUtil.isLayoutModifiedSinceLastMerge(layout)) {
108                                                    SitesUtil.mergeLayoutSetPrototypeLayouts(
109                                                            group, layoutSet);
110                                            }
111                                    }
112                            }
113                            finally {
114                                    MergeLayoutPrototypesThreadLocal.setMergeComplete(
115                                            methodName, arguments, parameterTypes);
116                                    WorkflowThreadLocal.setEnabled(workflowEnabled);
117                            }
118                    }
119                    else if (methodName.equals("getLayouts") &&
120                                     (Arrays.equals(parameterTypes, _TYPES_L_B_L) ||
121                                      Arrays.equals(parameterTypes, _TYPES_L_B_L_B_I_I))) {
122    
123                            long groupId = (Long)arguments[0];
124                            boolean privateLayout = (Boolean)arguments[1];
125                            long parentLayoutId = (Long)arguments[2];
126    
127                            try {
128                                    Group group = GroupLocalServiceUtil.getGroup(groupId);
129    
130                                    LayoutSet layoutSet = LayoutSetLocalServiceUtil.getLayoutSet(
131                                            groupId, privateLayout);
132    
133                                    mergeLayoutSetPrototypeLayouts(
134                                            methodName, arguments, parameterTypes, group, layoutSet,
135                                            privateLayout, workflowEnabled);
136    
137                                    List<Layout> layouts = (List<Layout>)methodInvocation.proceed();
138    
139                                    if (PropsValues.
140                                                    USER_GROUPS_COPY_LAYOUTS_TO_USER_PERSONAL_SITE) {
141    
142                                            return layouts;
143                                    }
144    
145                                    if (group.isUser()) {
146                                            _virtualLayoutTargetGroupId.set(group.getGroupId());
147    
148                                            if (parentLayoutId ==
149                                                            LayoutConstants.DEFAULT_PARENT_LAYOUT_ID) {
150    
151                                                    return addUserGroupLayouts(
152                                                            group, layoutSet, layouts, parentLayoutId);
153                                            }
154                                            else {
155                                                    return addChildUserGroupLayouts(group, layouts);
156                                            }
157                                    }
158                                    else if (group.isUserGroup() &&
159                                                     (parentLayoutId !=
160                                                             LayoutConstants.DEFAULT_PARENT_LAYOUT_ID)) {
161    
162                                            long targetGroupId = _virtualLayoutTargetGroupId.get();
163    
164                                            if (targetGroupId != GroupConstants.DEFAULT_LIVE_GROUP_ID) {
165                                                    Group targetGroup = GroupLocalServiceUtil.getGroup(
166                                                            targetGroupId);
167    
168                                                    return addChildUserGroupLayouts(targetGroup, layouts);
169                                            }
170                                    }
171    
172                                    return layouts;
173                            }
174                            catch (Exception e) {
175                                    _log.error(e, e);
176    
177                                    throw e;
178                            }
179                    }
180    
181                    return methodInvocation.proceed();
182            }
183    
184            protected List<Layout> addChildUserGroupLayouts(
185                            Group group, List<Layout> layouts)
186                    throws Exception {
187    
188                    layouts = ListUtil.copy(layouts);
189    
190                    List<Layout> childLayouts = new ArrayList<Layout>();
191    
192                    for (Layout layout : layouts) {
193                            Layout childLayout = layout;
194    
195                            Group layoutGroup = layout.getGroup();
196    
197                            if (layoutGroup.isUserGroup()) {
198                                    childLayout = new VirtualLayout(layout, group);
199                            }
200    
201                            childLayouts.add(childLayout);
202                    }
203    
204                    return childLayouts;
205            }
206    
207            protected List<Layout> addUserGroupLayouts(
208                            Group group, LayoutSet layoutSet, List<Layout> layouts,
209                            long parentLayoutId)
210                    throws Exception {
211    
212                    layouts = ListUtil.copy(layouts);
213    
214                    List<UserGroup> userUserGroups =
215                            UserGroupLocalServiceUtil.getUserUserGroups(group.getClassPK());
216    
217                    for (UserGroup userGroup : userUserGroups) {
218                            Group userGroupGroup = userGroup.getGroup();
219    
220                            List<Layout> userGroupLayouts = LayoutLocalServiceUtil.getLayouts(
221                                    userGroupGroup.getGroupId(), layoutSet.isPrivateLayout(),
222                                    parentLayoutId);
223    
224                            for (Layout userGroupLayout : userGroupLayouts) {
225                                    Layout virtualLayout = new VirtualLayout(
226                                            userGroupLayout, group);
227    
228                                    layouts.add(virtualLayout);
229                            }
230                    }
231    
232                    return layouts;
233            }
234    
235            protected List<Layout> getPrototypeLinkedLayouts(
236                            long groupId, boolean privateLayout)
237                    throws SystemException {
238    
239                    Class<?> clazz = getClass();
240    
241                    DynamicQuery dynamicQuery = DynamicQueryFactoryUtil.forClass(
242                            Layout.class, clazz.getClassLoader());
243    
244                    Property groupIdProperty = PropertyFactoryUtil.forName("groupId");
245    
246                    dynamicQuery.add(groupIdProperty.eq(groupId));
247    
248                    Property layoutPrototypeUuidProperty = PropertyFactoryUtil.forName(
249                            "layoutPrototypeUuid");
250    
251                    dynamicQuery.add(layoutPrototypeUuidProperty.isNotNull());
252    
253                    Property privateLayoutProperty = PropertyFactoryUtil.forName(
254                            "privateLayout");
255    
256                    dynamicQuery.add(privateLayoutProperty.eq(privateLayout));
257    
258                    Property sourcePrototypeLayoutUuidProperty =
259                            PropertyFactoryUtil.forName("sourcePrototypeLayoutUuid");
260    
261                    dynamicQuery.add(sourcePrototypeLayoutUuidProperty.isNotNull());
262    
263                    return LayoutLocalServiceUtil.dynamicQuery(dynamicQuery);
264            }
265    
266            protected void mergeLayoutSetPrototypeLayouts(
267                    String methodName, Object[] arguments, Class<?>[] parameterTypes,
268                    Group group, LayoutSet layoutSet, boolean privateLayout,
269                    boolean workflowEnabled) {
270    
271                    try {
272                            if (!SitesUtil.isLayoutSetMergeable(group, layoutSet)) {
273                                    return;
274                            }
275    
276                            MergeLayoutPrototypesThreadLocal.setInProgress(true);
277                            WorkflowThreadLocal.setEnabled(false);
278    
279                            int count = LayoutLocalServiceUtil.getLayoutsCount(
280                                    group, privateLayout);
281    
282                            if (count == 0) {
283                                    SitesUtil.mergeLayoutSetPrototypeLayouts(group, layoutSet);
284    
285                                    return;
286                            }
287    
288                            List<Layout> layouts = getPrototypeLinkedLayouts(
289                                    group.getGroupId(), privateLayout);
290    
291                            for (Layout layout : layouts) {
292                                    if (SitesUtil.isLayoutModifiedSinceLastMerge(layout)) {
293                                            return;
294                                    }
295                            }
296    
297                            SitesUtil.mergeLayoutSetPrototypeLayouts(group, layoutSet);
298                    }
299                    catch (Exception e) {
300                            if (_log.isWarnEnabled()) {
301                                    _log.warn("Unable to merge layouts for site template", e);
302                            }
303                    }
304                    finally {
305                            MergeLayoutPrototypesThreadLocal.setMergeComplete(
306                                    methodName, arguments, parameterTypes);
307                            WorkflowThreadLocal.setEnabled(workflowEnabled);
308                    }
309            }
310    
311            private static final Class<?>[] _TYPES_L = {Long.TYPE};
312    
313            private static final Class<?>[] _TYPES_L_B_L = {
314                    Long.TYPE, Boolean.TYPE, Long.TYPE
315            };
316    
317            private static final Class<?>[] _TYPES_L_B_L_B_I_I = {
318                    Long.TYPE, Boolean.TYPE, Long.TYPE, Boolean.TYPE, Integer.TYPE,
319                    Integer.TYPE
320            };
321    
322            private static Log _log = LogFactoryUtil.getLog(
323                    LayoutLocalServiceVirtualLayoutsAdvice.class);
324    
325            private static ThreadLocal<Long> _virtualLayoutTargetGroupId =
326                    new AutoResetThreadLocal<Long>(
327                            LayoutLocalServiceVirtualLayoutsAdvice.class +
328                                    "._virtualLayoutTargetGroupId",
329                            GroupConstants.DEFAULT_LIVE_GROUP_ID);
330    
331    }