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