001    /**
002     * Copyright (c) 2000-present 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.model;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryUtil;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.staging.MergeLayoutPrototypesThreadLocal;
022    import com.liferay.portal.kernel.staging.StagingUtil;
023    import com.liferay.portal.kernel.util.ParamUtil;
024    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
025    import com.liferay.portal.kernel.util.ProxyUtil;
026    import com.liferay.portal.kernel.util.ReflectionUtil;
027    import com.liferay.portal.kernel.workflow.WorkflowConstants;
028    import com.liferay.portal.service.LayoutBranchLocalServiceUtil;
029    import com.liferay.portal.service.LayoutRevisionLocalServiceUtil;
030    import com.liferay.portal.service.LayoutSetBranchLocalServiceUtil;
031    import com.liferay.portal.service.ServiceContext;
032    import com.liferay.portal.service.ServiceContextThreadLocal;
033    import com.liferay.portal.service.UserLocalServiceUtil;
034    import com.liferay.portal.util.LayoutTypePortletFactoryUtil;
035    import com.liferay.portal.util.comparator.LayoutRevisionCreateDateComparator;
036    
037    import java.io.Serializable;
038    
039    import java.lang.reflect.InvocationHandler;
040    import java.lang.reflect.InvocationTargetException;
041    import java.lang.reflect.Method;
042    
043    import java.util.HashSet;
044    import java.util.List;
045    import java.util.Set;
046    
047    import javax.servlet.http.HttpServletRequest;
048    
049    /**
050     * @author Raymond Aug??
051     * @author Brian Wing Shun Chan
052     */
053    public class LayoutStagingHandler implements InvocationHandler, Serializable {
054    
055            public LayoutStagingHandler(Layout layout) {
056                    this(layout, null);
057            }
058    
059            public Layout getLayout() {
060                    return _layout;
061            }
062    
063            public LayoutRevision getLayoutRevision() {
064                    return _layoutRevision;
065            }
066    
067            @Override
068            public Object invoke(Object proxy, Method method, Object[] arguments)
069                    throws Throwable {
070    
071                    try {
072                            if (_layoutRevision == null) {
073                                    return method.invoke(_layout, arguments);
074                            }
075    
076                            String methodName = method.getName();
077    
078                            if (methodName.equals("getLayoutType")) {
079                                    return _getLayoutType();
080                            }
081                            else if (methodName.equals("getRegularURL")) {
082                                    Class<?> layoutRevisionClass = _layoutRevision.getClass();
083    
084                                    method = layoutRevisionClass.getMethod(
085                                            methodName, HttpServletRequest.class);
086    
087                                    return method.invoke(_layoutRevision, arguments);
088                            }
089                            else if (methodName.equals("toEscapedModel")) {
090                                    if (_layout.isEscapedModel()) {
091                                            return this;
092                                    }
093    
094                                    return _toEscapedModel();
095                            }
096    
097                            if (methodName.equals("clone")) {
098                                    return _clone();
099                            }
100    
101                            Object bean = _layout;
102    
103                            if (_layoutRevisionMethodNames.contains(methodName)) {
104                                    try {
105                                            Class<?> layoutRevisionClass = _layoutRevision.getClass();
106    
107                                            method = layoutRevisionClass.getMethod(
108                                                    methodName,
109                                                    ReflectionUtil.getParameterTypes(arguments));
110    
111                                            bean = _layoutRevision;
112                                    }
113                                    catch (NoSuchMethodException nsme) {
114                                            _log.error(nsme, nsme);
115                                    }
116                            }
117    
118                            return method.invoke(bean, arguments);
119                    }
120                    catch (InvocationTargetException ite) {
121                            throw ite.getTargetException();
122                    }
123            }
124    
125            public void setLayoutRevision(LayoutRevision layoutRevision) {
126                    _layoutRevision = layoutRevision;
127            }
128    
129            private LayoutStagingHandler(Layout layout, LayoutRevision layoutRevision) {
130                    _layout = layout;
131    
132                    try {
133                            _layoutRevision = _getLayoutRevision(layout, layoutRevision);
134                    }
135                    catch (Exception e) {
136                            _log.error(e, e);
137    
138                            throw new IllegalStateException(e);
139                    }
140            }
141    
142            private Object _clone() {
143                    return ProxyUtil.newProxyInstance(
144                            PortalClassLoaderUtil.getClassLoader(), new Class[] {Layout.class},
145                            new LayoutStagingHandler(_layout, _layoutRevision));
146            }
147    
148            private LayoutRevision _getLayoutRevision(
149                            Layout layout, LayoutRevision layoutRevision)
150                    throws PortalException {
151    
152                    if (layoutRevision != null) {
153                            return layoutRevision;
154                    }
155    
156                    ServiceContext serviceContext =
157                            ServiceContextThreadLocal.getServiceContext();
158    
159                    if ((serviceContext == null) || !serviceContext.isSignedIn()) {
160                            LayoutRevision lastLayoutRevision = null;
161    
162                            lastLayoutRevision =
163                                    LayoutRevisionLocalServiceUtil.fetchLastLayoutRevision(
164                                            layout.getPlid(), true);
165    
166                            if (lastLayoutRevision == null) {
167                                    lastLayoutRevision =
168                                            LayoutRevisionLocalServiceUtil.fetchLastLayoutRevision(
169                                                    layout.getPlid(), false);
170                            }
171    
172                            return lastLayoutRevision;
173                    }
174    
175                    User user = UserLocalServiceUtil.getUser(serviceContext.getUserId());
176    
177                    long layoutSetBranchId = ParamUtil.getLong(
178                            serviceContext, "layoutSetBranchId");
179    
180                    LayoutSet layoutSet = layout.getLayoutSet();
181    
182                    LayoutSetBranch layoutSetBranch =
183                            LayoutSetBranchLocalServiceUtil.getUserLayoutSetBranch(
184                                    serviceContext.getUserId(), layout.getGroupId(),
185                                    layout.isPrivateLayout(), layoutSet.getLayoutSetId(),
186                                    layoutSetBranchId);
187    
188                    layoutSetBranchId = layoutSetBranch.getLayoutSetBranchId();
189    
190                    long layoutRevisionId = ParamUtil.getLong(
191                            serviceContext, "layoutRevisionId");
192    
193                    if (layoutRevisionId > 0) {
194                            layoutRevision = LayoutRevisionLocalServiceUtil.fetchLayoutRevision(
195                                    layoutRevisionId);
196                    }
197    
198                    if ((layoutRevisionId <= 0) ||
199                            !_isBelongsToLayout(layoutRevision, layout)) {
200    
201                            layoutRevisionId = StagingUtil.getRecentLayoutRevisionId(
202                                    user, layoutSetBranchId, layout.getPlid());
203    
204                            layoutRevision = LayoutRevisionLocalServiceUtil.fetchLayoutRevision(
205                                    layoutRevisionId);
206                    }
207    
208                    if ((layoutRevision != null) && !layoutRevision.isInactive()) {
209                            return layoutRevision;
210                    }
211    
212                    layoutRevision = null;
213    
214                    List<LayoutRevision> layoutRevisions =
215                            LayoutRevisionLocalServiceUtil.getLayoutRevisions(
216                                    layoutSetBranchId, layout.getPlid(), QueryUtil.ALL_POS,
217                                    QueryUtil.ALL_POS,
218                                    new LayoutRevisionCreateDateComparator(true));
219    
220                    if (!layoutRevisions.isEmpty()) {
221                            layoutRevision = layoutRevisions.get(0);
222    
223                            for (LayoutRevision curLayoutRevision : layoutRevisions) {
224                                    if (curLayoutRevision.isHead()) {
225                                            layoutRevision = curLayoutRevision;
226    
227                                            break;
228                                    }
229                            }
230                    }
231    
232                    if (layoutRevision != null) {
233                            StagingUtil.setRecentLayoutRevisionId(
234                                    user, layoutSetBranchId, layout.getPlid(),
235                                    layoutRevision.getLayoutRevisionId());
236    
237                            return layoutRevision;
238                    }
239    
240                    LayoutBranch layoutBranch =
241                            LayoutBranchLocalServiceUtil.getMasterLayoutBranch(
242                                    layoutSetBranchId, layout.getPlid(), serviceContext);
243    
244                    if (!MergeLayoutPrototypesThreadLocal.isInProgress()) {
245                            serviceContext.setWorkflowAction(
246                                    WorkflowConstants.ACTION_SAVE_DRAFT);
247                    }
248    
249                    layoutRevision = LayoutRevisionLocalServiceUtil.addLayoutRevision(
250                            serviceContext.getUserId(), layoutSetBranchId,
251                            layoutBranch.getLayoutBranchId(),
252                            LayoutRevisionConstants.DEFAULT_PARENT_LAYOUT_REVISION_ID, false,
253                            layout.getPlid(), LayoutConstants.DEFAULT_PLID,
254                            layout.isPrivateLayout(), layout.getName(), layout.getTitle(),
255                            layout.getDescription(), layout.getKeywords(), layout.getRobots(),
256                            layout.getTypeSettings(), layout.getIconImage(),
257                            layout.getIconImageId(), layout.getThemeId(),
258                            layout.getColorSchemeId(), layout.getWapThemeId(),
259                            layout.getWapColorSchemeId(), layout.getCss(), serviceContext);
260    
261                    boolean explicitCreation = ParamUtil.getBoolean(
262                            serviceContext, "explicitCreation");
263    
264                    if (!explicitCreation) {
265                            LayoutRevisionLocalServiceUtil.updateStatus(
266                                    serviceContext.getUserId(),
267                                    layoutRevision.getLayoutRevisionId(),
268                                    WorkflowConstants.STATUS_INCOMPLETE, serviceContext);
269                    }
270    
271                    return layoutRevision;
272            }
273    
274            private LayoutType _getLayoutType() {
275                    return LayoutTypePortletFactoryUtil.create(
276                            (Layout)ProxyUtil.newProxyInstance(
277                                    PortalClassLoaderUtil.getClassLoader(),
278                                    new Class[] {Layout.class},
279                                    new LayoutStagingHandler(_layout, _layoutRevision)));
280            }
281    
282            private boolean _isBelongsToLayout(
283                    LayoutRevision layoutRevision, Layout layout) {
284    
285                    if (layoutRevision == null) {
286                            return false;
287                    }
288    
289                    if (layoutRevision.getPlid() == layout.getPlid()) {
290                            return true;
291                    }
292    
293                    return false;
294            }
295    
296            private Object _toEscapedModel() {
297                    return ProxyUtil.newProxyInstance(
298                            PortalClassLoaderUtil.getClassLoader(), new Class[] {Layout.class},
299                            new LayoutStagingHandler(
300                                    _layout.toEscapedModel(), _layoutRevision.toEscapedModel()));
301            }
302    
303            private static final Log _log = LogFactoryUtil.getLog(
304                    LayoutStagingHandler.class);
305    
306            private static final Set<String> _layoutRevisionMethodNames =
307                    new HashSet<String>();
308    
309            static {
310                    _layoutRevisionMethodNames.add("getColorScheme");
311                    _layoutRevisionMethodNames.add("getColorSchemeId");
312                    _layoutRevisionMethodNames.add("getCss");
313                    _layoutRevisionMethodNames.add("getCssText");
314                    _layoutRevisionMethodNames.add("getDescription");
315                    _layoutRevisionMethodNames.add("getGroupId");
316                    _layoutRevisionMethodNames.add("getHTMLTitle");
317                    _layoutRevisionMethodNames.add("getIconImage");
318                    _layoutRevisionMethodNames.add("getIconImageId");
319                    _layoutRevisionMethodNames.add("getKeywords");
320                    _layoutRevisionMethodNames.add("getLayoutSet");
321                    _layoutRevisionMethodNames.add("getName");
322                    _layoutRevisionMethodNames.add("getRobots");
323                    _layoutRevisionMethodNames.add("getTheme");
324                    _layoutRevisionMethodNames.add("getThemeId");
325                    _layoutRevisionMethodNames.add("getThemeSetting");
326                    _layoutRevisionMethodNames.add("getTitle");
327                    _layoutRevisionMethodNames.add("getTypeSettings");
328                    _layoutRevisionMethodNames.add("getTypeSettingsProperties");
329                    _layoutRevisionMethodNames.add("getTypeSettingsProperty");
330                    _layoutRevisionMethodNames.add("getWapColorScheme");
331                    _layoutRevisionMethodNames.add("getWapColorSchemeId");
332                    _layoutRevisionMethodNames.add("getWapTheme");
333                    _layoutRevisionMethodNames.add("getWapThemeId");
334                    _layoutRevisionMethodNames.add("isContentDisplayPage");
335                    _layoutRevisionMethodNames.add("isEscapedModel");
336                    _layoutRevisionMethodNames.add("isIconImage");
337                    _layoutRevisionMethodNames.add("isInheritLookAndFeel");
338                    _layoutRevisionMethodNames.add("isInheritWapLookAndFeel");
339                    _layoutRevisionMethodNames.add("setColorSchemeId");
340                    _layoutRevisionMethodNames.add("setCss");
341                    _layoutRevisionMethodNames.add("setDescription");
342                    _layoutRevisionMethodNames.add("setDescriptionMap");
343                    _layoutRevisionMethodNames.add("setEscapedModel");
344                    _layoutRevisionMethodNames.add("setGroupId");
345                    _layoutRevisionMethodNames.add("setIconImage");
346                    _layoutRevisionMethodNames.add("setIconImageId");
347                    _layoutRevisionMethodNames.add("setKeywords");
348                    _layoutRevisionMethodNames.add("setKeywordsMap");
349                    _layoutRevisionMethodNames.add("setName");
350                    _layoutRevisionMethodNames.add("setNameMap");
351                    _layoutRevisionMethodNames.add("setRobots");
352                    _layoutRevisionMethodNames.add("setRobotsMap");
353                    _layoutRevisionMethodNames.add("setThemeId");
354                    _layoutRevisionMethodNames.add("setTitle");
355                    _layoutRevisionMethodNames.add("setTitleMap");
356                    _layoutRevisionMethodNames.add("setTypeSettings");
357                    _layoutRevisionMethodNames.add("setTypeSettingsProperties");
358                    _layoutRevisionMethodNames.add("setWapColorSchemeId");
359                    _layoutRevisionMethodNames.add("setWapThemeId");
360            }
361    
362            private final Layout _layout;
363            private LayoutRevision _layoutRevision;
364    
365    }