001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.action;
016    
017    import com.liferay.portal.kernel.audit.AuditMessage;
018    import com.liferay.portal.kernel.audit.AuditRouterUtil;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.exception.SystemException;
021    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
022    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
023    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
024    import com.liferay.portal.kernel.language.LanguageUtil;
025    import com.liferay.portal.kernel.log.Log;
026    import com.liferay.portal.kernel.log.LogFactoryUtil;
027    import com.liferay.portal.kernel.messaging.DestinationNames;
028    import com.liferay.portal.kernel.messaging.MessageBusUtil;
029    import com.liferay.portal.kernel.portlet.LiferayPortletMode;
030    import com.liferay.portal.kernel.portlet.PortletModeFactory;
031    import com.liferay.portal.kernel.portlet.WindowStateFactory;
032    import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
033    import com.liferay.portal.kernel.servlet.HeaderCacheServletResponse;
034    import com.liferay.portal.kernel.servlet.HttpHeaders;
035    import com.liferay.portal.kernel.servlet.PipingServletResponse;
036    import com.liferay.portal.kernel.servlet.ServletResponseUtil;
037    import com.liferay.portal.kernel.servlet.StringServletResponse;
038    import com.liferay.portal.kernel.servlet.TempAttributesServletRequest;
039    import com.liferay.portal.kernel.struts.LastPath;
040    import com.liferay.portal.kernel.upload.UploadServletRequest;
041    import com.liferay.portal.kernel.util.ArrayUtil;
042    import com.liferay.portal.kernel.util.Base64;
043    import com.liferay.portal.kernel.util.ContentTypes;
044    import com.liferay.portal.kernel.util.HttpUtil;
045    import com.liferay.portal.kernel.util.JavaConstants;
046    import com.liferay.portal.kernel.util.ParamUtil;
047    import com.liferay.portal.kernel.util.PropsKeys;
048    import com.liferay.portal.kernel.util.ServerDetector;
049    import com.liferay.portal.kernel.util.StringBundler;
050    import com.liferay.portal.kernel.util.StringPool;
051    import com.liferay.portal.kernel.util.Validator;
052    import com.liferay.portal.kernel.webdav.WebDAVStorage;
053    import com.liferay.portal.kernel.xml.QName;
054    import com.liferay.portal.model.Layout;
055    import com.liferay.portal.model.LayoutConstants;
056    import com.liferay.portal.model.LayoutTypePortlet;
057    import com.liferay.portal.model.Portlet;
058    import com.liferay.portal.model.PortletPreferencesIds;
059    import com.liferay.portal.model.PublicRenderParameter;
060    import com.liferay.portal.model.User;
061    import com.liferay.portal.security.auth.AuthTokenUtil;
062    import com.liferay.portal.security.permission.ActionKeys;
063    import com.liferay.portal.security.permission.PermissionChecker;
064    import com.liferay.portal.security.permission.PermissionThreadLocal;
065    import com.liferay.portal.service.LayoutLocalServiceUtil;
066    import com.liferay.portal.service.PortletLocalServiceUtil;
067    import com.liferay.portal.service.PortletPreferencesLocalServiceUtil;
068    import com.liferay.portal.service.ServiceContext;
069    import com.liferay.portal.service.ServiceContextFactory;
070    import com.liferay.portal.service.ServiceContextThreadLocal;
071    import com.liferay.portal.service.permission.PortletPermissionUtil;
072    import com.liferay.portal.struts.ActionConstants;
073    import com.liferay.portal.struts.StrutsUtil;
074    import com.liferay.portal.theme.PortletDisplay;
075    import com.liferay.portal.theme.ThemeDisplay;
076    import com.liferay.portal.upload.UploadServletRequestImpl;
077    import com.liferay.portal.util.PortalUtil;
078    import com.liferay.portal.util.PrefsPropsUtil;
079    import com.liferay.portal.util.PropsValues;
080    import com.liferay.portal.util.WebKeys;
081    import com.liferay.portlet.ActionRequestFactory;
082    import com.liferay.portlet.ActionRequestImpl;
083    import com.liferay.portlet.ActionResponseFactory;
084    import com.liferay.portlet.ActionResponseImpl;
085    import com.liferay.portlet.EventImpl;
086    import com.liferay.portlet.EventRequestFactory;
087    import com.liferay.portlet.EventRequestImpl;
088    import com.liferay.portlet.EventResponseFactory;
089    import com.liferay.portlet.EventResponseImpl;
090    import com.liferay.portlet.InvokerPortlet;
091    import com.liferay.portlet.InvokerPortletImpl;
092    import com.liferay.portlet.PortletConfigFactoryUtil;
093    import com.liferay.portlet.PortletConfigImpl;
094    import com.liferay.portlet.PortletInstanceFactoryUtil;
095    import com.liferay.portlet.PortletPreferencesFactoryUtil;
096    import com.liferay.portlet.PortletQName;
097    import com.liferay.portlet.PortletQNameUtil;
098    import com.liferay.portlet.PortletRequestImpl;
099    import com.liferay.portlet.PortletURLImpl;
100    import com.liferay.portlet.PublicRenderParametersPool;
101    import com.liferay.portlet.RenderParametersPool;
102    import com.liferay.portlet.RenderRequestImpl;
103    import com.liferay.portlet.RenderResponseImpl;
104    import com.liferay.portlet.ResourceRequestFactory;
105    import com.liferay.portlet.ResourceRequestImpl;
106    import com.liferay.portlet.ResourceResponseFactory;
107    import com.liferay.portlet.ResourceResponseImpl;
108    import com.liferay.portlet.StateAwareResponseImpl;
109    import com.liferay.portlet.layoutconfiguration.util.RuntimePortletUtil;
110    import com.liferay.portlet.login.util.LoginUtil;
111    import com.liferay.util.servlet.filters.CacheResponseUtil;
112    
113    import java.io.InputStream;
114    import java.io.Serializable;
115    
116    import java.util.ArrayList;
117    import java.util.Enumeration;
118    import java.util.HashMap;
119    import java.util.List;
120    import java.util.Map;
121    
122    import javax.portlet.Event;
123    import javax.portlet.PortletConfig;
124    import javax.portlet.PortletContext;
125    import javax.portlet.PortletMode;
126    import javax.portlet.PortletPreferences;
127    import javax.portlet.PortletRequest;
128    import javax.portlet.PortletURL;
129    import javax.portlet.UnavailableException;
130    import javax.portlet.WindowState;
131    
132    import javax.servlet.RequestDispatcher;
133    import javax.servlet.ServletContext;
134    import javax.servlet.http.HttpServletRequest;
135    import javax.servlet.http.HttpServletResponse;
136    import javax.servlet.http.HttpSession;
137    
138    import org.apache.struts.action.Action;
139    import org.apache.struts.action.ActionForm;
140    import org.apache.struts.action.ActionForward;
141    import org.apache.struts.action.ActionMapping;
142    
143    /**
144     * @author Brian Wing Shun Chan
145     */
146    public class LayoutAction extends Action {
147    
148            @Override
149            public ActionForward execute(
150                            ActionMapping mapping, ActionForm form, HttpServletRequest request,
151                            HttpServletResponse response)
152                    throws Exception {
153    
154                    HeaderCacheServletResponse headerCacheServletResponse = null;
155    
156                    if (response instanceof HeaderCacheServletResponse) {
157                            headerCacheServletResponse = (HeaderCacheServletResponse)response;
158                    }
159                    else {
160                            headerCacheServletResponse = new HeaderCacheServletResponse(
161                                    response);
162                    }
163    
164                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
165                            WebKeys.THEME_DISPLAY);
166    
167                    Layout layout = themeDisplay.getLayout();
168    
169                    Boolean layoutDefault = (Boolean)request.getAttribute(
170                            WebKeys.LAYOUT_DEFAULT);
171    
172                    if ((layoutDefault != null) && layoutDefault.booleanValue()) {
173                            Layout requestedLayout = (Layout)request.getAttribute(
174                                    WebKeys.REQUESTED_LAYOUT);
175    
176                            if (requestedLayout != null) {
177                                    String redirectParam = "redirect";
178    
179                                    if (Validator.isNotNull(PropsValues.AUTH_LOGIN_PORTLET_NAME)) {
180                                            redirectParam =
181                                                    PortalUtil.getPortletNamespace(
182                                                            PropsValues.AUTH_LOGIN_PORTLET_NAME) +
183                                                    redirectParam;
184                                    }
185    
186                                    String authLoginURL = null;
187    
188                                    if (PrefsPropsUtil.getBoolean(
189                                                    themeDisplay.getCompanyId(), PropsKeys.CAS_AUTH_ENABLED,
190                                                    PropsValues.CAS_AUTH_ENABLED) ||
191                                            PrefsPropsUtil.getBoolean(
192                                                    themeDisplay.getCompanyId(),
193                                                    PropsKeys.OPEN_SSO_AUTH_ENABLED,
194                                                    PropsValues.OPEN_SSO_AUTH_ENABLED)) {
195    
196                                            authLoginURL = themeDisplay.getURLSignIn();
197                                    }
198    
199                                    if (Validator.isNull(authLoginURL)) {
200                                            authLoginURL = PortalUtil.getSiteLoginURL(themeDisplay);
201                                    }
202    
203                                    if (Validator.isNull(authLoginURL)) {
204                                            authLoginURL = PropsValues.AUTH_LOGIN_URL;
205                                    }
206    
207                                    if (Validator.isNull(authLoginURL)) {
208                                            PortletURL loginURL = LoginUtil.getLoginURL(
209                                                    request, themeDisplay.getPlid());
210    
211                                            authLoginURL = loginURL.toString();
212                                    }
213    
214                                    authLoginURL = HttpUtil.setParameter(
215                                            authLoginURL, "p_p_id",
216                                            PropsValues.AUTH_LOGIN_PORTLET_NAME);
217    
218                                    String currentURL = PortalUtil.getCurrentURL(request);
219    
220                                    authLoginURL = HttpUtil.setParameter(
221                                            authLoginURL, redirectParam, currentURL);
222    
223                                    if (_log.isDebugEnabled()) {
224                                            _log.debug("Redirect requested layout to " + authLoginURL);
225                                    }
226    
227                                    headerCacheServletResponse.sendRedirect(authLoginURL);
228                            }
229                            else {
230                                    String redirect = PortalUtil.getLayoutURL(layout, themeDisplay);
231    
232                                    if (_log.isDebugEnabled()) {
233                                            _log.debug("Redirect default layout to " + redirect);
234                                    }
235    
236                                    headerCacheServletResponse.sendRedirect(redirect);
237                            }
238    
239                            return null;
240                    }
241    
242                    long plid = ParamUtil.getLong(request, "p_l_id");
243    
244                    if (_log.isDebugEnabled()) {
245                            _log.debug("p_l_id is " + plid);
246                    }
247    
248                    if (plid > 0) {
249                            ActionForward actionForward = processLayout(
250                                    mapping, request, headerCacheServletResponse, plid);
251    
252                            String contentType = response.getContentType();
253    
254                            CacheResponseUtil.setHeaders(
255                                    response, headerCacheServletResponse.getHeaders());
256    
257                            if (contentType != null) {
258                                    response.setContentType(contentType);
259                            }
260    
261                            return actionForward;
262                    }
263                    else {
264                            try {
265                                    forwardLayout(request);
266    
267                                    return mapping.findForward(ActionConstants.COMMON_FORWARD_JSP);
268                            }
269                            catch (Exception e) {
270                                    PortalUtil.sendError(e, request, headerCacheServletResponse);
271    
272                                    CacheResponseUtil.setHeaders(
273                                            response, headerCacheServletResponse.getHeaders());
274    
275                                    return null;
276                            }
277                    }
278            }
279    
280            protected void forwardLayout(HttpServletRequest request) throws Exception {
281                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
282    
283                    long plid = LayoutConstants.DEFAULT_PLID;
284    
285                    String layoutFriendlyURL = null;
286    
287                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
288                            WebKeys.THEME_DISPLAY);
289    
290                    if (layout != null) {
291                            plid = layout.getPlid();
292    
293                            layoutFriendlyURL = PortalUtil.getLayoutFriendlyURL(
294                                    layout, themeDisplay);
295                    }
296    
297                    String forwardURL = layoutFriendlyURL;
298    
299                    if (Validator.isNull(forwardURL)) {
300                            forwardURL =
301                                    themeDisplay.getPathMain() + "/portal/layout?p_l_id=" + plid;
302                    }
303    
304                    if (Validator.isNotNull(themeDisplay.getDoAsUserId())) {
305                            forwardURL = HttpUtil.addParameter(
306                                    forwardURL, "doAsUserId", themeDisplay.getDoAsUserId());
307                    }
308    
309                    if (Validator.isNotNull(themeDisplay.getDoAsUserLanguageId())) {
310                            forwardURL = HttpUtil.addParameter(
311                                    forwardURL, "doAsUserLanguageId",
312                                    themeDisplay.getDoAsUserLanguageId());
313                    }
314    
315                    if (_log.isDebugEnabled()) {
316                            _log.debug("Forward layout to " + forwardURL);
317                    }
318    
319                    request.setAttribute(WebKeys.FORWARD_URL, forwardURL);
320            }
321    
322            protected List<LayoutTypePortlet> getLayoutTypePortlets(
323                            long groupId, boolean privateLayout)
324                    throws Exception {
325    
326                    List<LayoutTypePortlet> layoutTypePortlets =
327                            new ArrayList<LayoutTypePortlet>();
328    
329                    List<Layout> layouts = LayoutLocalServiceUtil.getLayouts(
330                            groupId, privateLayout, LayoutConstants.TYPE_PORTLET);
331    
332                    for (Layout layout : layouts) {
333                            if (!layout.isTypePortlet()) {
334                                    continue;
335                            }
336    
337                            LayoutTypePortlet layoutTypePortlet =
338                                    (LayoutTypePortlet)layout.getLayoutType();
339    
340                            layoutTypePortlets.add(layoutTypePortlet);
341                    }
342    
343                    return layoutTypePortlets;
344            }
345    
346            protected HttpServletRequest getOwnerLayoutRequestWrapper(
347                            HttpServletRequest request, Portlet portlet)
348                    throws Exception {
349    
350                    if (!PropsValues.PORTLET_EVENT_DISTRIBUTION_LAYOUT_SET) {
351                            return request;
352                    }
353    
354                    ThemeDisplay themeDisplay =
355                            (ThemeDisplay)request.getAttribute(WebKeys.THEME_DISPLAY);
356    
357                    Layout currentLayout = themeDisplay.getLayout();
358    
359                    Layout requestLayout = (Layout)request.getAttribute(WebKeys.LAYOUT);
360    
361                    List<LayoutTypePortlet> layoutTypePortlets = getLayoutTypePortlets(
362                            requestLayout.getGroupId(), requestLayout.isPrivateLayout());
363    
364                    Layout ownerLayout = null;
365                    LayoutTypePortlet ownerLayoutTypePortlet = null;
366    
367                    for (LayoutTypePortlet layoutTypePortlet : layoutTypePortlets) {
368                            if (layoutTypePortlet.hasPortletId(portlet.getPortletId())) {
369                                    ownerLayoutTypePortlet = layoutTypePortlet;
370    
371                                    ownerLayout = layoutTypePortlet.getLayout();
372    
373                                    break;
374                            }
375                    }
376    
377                    if ((ownerLayout != null) && !currentLayout.equals(ownerLayout)) {
378                            ThemeDisplay themeDisplayClone = (ThemeDisplay)themeDisplay.clone();
379    
380                            themeDisplayClone.setLayout(ownerLayout);
381                            themeDisplayClone.setLayoutTypePortlet(ownerLayoutTypePortlet);
382    
383                            TempAttributesServletRequest tempAttributesServletRequest =
384                                    new TempAttributesServletRequest(request);
385    
386                            tempAttributesServletRequest.setTempAttribute(
387                                    WebKeys.THEME_DISPLAY, themeDisplayClone);
388                            tempAttributesServletRequest.setTempAttribute(
389                                    WebKeys.LAYOUT, ownerLayout);
390    
391                            return tempAttributesServletRequest;
392                    }
393    
394                    return request;
395            }
396    
397            protected long getScopeGroupId(
398                            HttpServletRequest request, LayoutTypePortlet layoutTypePortlet,
399                            String portletId)
400                    throws PortalException, SystemException {
401    
402                    long scopeGroupId = 0;
403    
404                    Layout layoutTypePortletLayout = layoutTypePortlet.getLayout();
405    
406                    Layout requestLayout = (Layout)request.getAttribute(WebKeys.LAYOUT);
407    
408                    try {
409                            request.setAttribute(WebKeys.LAYOUT, layoutTypePortletLayout);
410    
411                            scopeGroupId = PortalUtil.getScopeGroupId(request, portletId);
412                    }
413                    finally {
414                            request.setAttribute(WebKeys.LAYOUT, requestLayout);
415                    }
416    
417                    if (scopeGroupId <= 0) {
418                            scopeGroupId = PortalUtil.getScopeGroupId(
419                                    layoutTypePortletLayout, portletId);
420                    }
421    
422                    return scopeGroupId;
423            }
424    
425            protected void includeLayoutContent(
426                            HttpServletRequest request, HttpServletResponse response,
427                            ThemeDisplay themeDisplay, Layout layout)
428                    throws Exception {
429    
430                    ServletContext servletContext = (ServletContext)request.getAttribute(
431                            WebKeys.CTX);
432    
433                    String path = StrutsUtil.TEXT_HTML_DIR;
434    
435                    if (BrowserSnifferUtil.isWap(request)) {
436                            path = StrutsUtil.TEXT_WAP_DIR;
437                    }
438    
439                    // Manually check the p_p_id. See LEP-1724.
440    
441                    if (themeDisplay.isStateExclusive() ||
442                            Validator.isNotNull(ParamUtil.getString(request, "p_p_id"))) {
443    
444                            if (layout.isTypePanel()) {
445                                    path += "/portal/layout/view/panel.jsp";
446                            }
447                            else if (layout.isTypeControlPanel()) {
448                                    path += "/portal/layout/view/control_panel.jsp";
449                            }
450                            else {
451                                    path += "/portal/layout/view/portlet.jsp";
452                            }
453                    }
454                    else {
455                            path += PortalUtil.getLayoutViewPage(layout);
456                    }
457    
458                    RequestDispatcher requestDispatcher =
459                            servletContext.getRequestDispatcher(path);
460    
461                    UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
462    
463                    PipingServletResponse pipingServletResponse = new PipingServletResponse(
464                            response, unsyncStringWriter);
465    
466                    String contentType = pipingServletResponse.getContentType();
467    
468                    requestDispatcher.include(request, pipingServletResponse);
469    
470                    if (contentType != null) {
471                            response.setContentType(contentType);
472                    }
473    
474                    request.setAttribute(
475                            WebKeys.LAYOUT_CONTENT, unsyncStringWriter.getStringBundler());
476            }
477    
478            protected void processEvent(
479                            PortletRequestImpl portletRequestImpl,
480                            StateAwareResponseImpl stateAwareResponseImpl,
481                            List<LayoutTypePortlet> layoutTypePortlets,
482                            LayoutTypePortlet layoutTypePortlet, Portlet portlet, Event event)
483                    throws Exception {
484    
485                    HttpServletRequest request = portletRequestImpl.getHttpServletRequest();
486                    HttpServletResponse response =
487                            stateAwareResponseImpl.getHttpServletResponse();
488                    HttpSession session = request.getSession();
489    
490                    String portletId = portlet.getPortletId();
491    
492                    ServletContext servletContext =
493                            (ServletContext)request.getAttribute(WebKeys.CTX);
494    
495                    InvokerPortlet invokerPortlet = PortletInstanceFactoryUtil.create(
496                            portlet, servletContext);
497    
498                    PortletConfig portletConfig = PortletConfigFactoryUtil.create(
499                            portlet, servletContext);
500                    PortletContext portletContext = portletConfig.getPortletContext();
501    
502                    WindowState windowState = null;
503    
504                    if (layoutTypePortlet.hasStateMaxPortletId(portletId)) {
505                            windowState = WindowState.MAXIMIZED;
506                    }
507                    else if (layoutTypePortlet.hasStateMinPortletId(portletId)) {
508                            windowState = WindowState.MINIMIZED;
509                    }
510                    else {
511                            windowState = WindowState.NORMAL;
512                    }
513    
514                    PortletMode portletMode = null;
515    
516                    if (layoutTypePortlet.hasModeAboutPortletId(portletId)) {
517                            portletMode = LiferayPortletMode.ABOUT;
518                    }
519                    else if (layoutTypePortlet.hasModeConfigPortletId(portletId)) {
520                            portletMode = LiferayPortletMode.CONFIG;
521                    }
522                    else if (layoutTypePortlet.hasModeEditPortletId(portletId)) {
523                            portletMode = PortletMode.EDIT;
524                    }
525                    else if (layoutTypePortlet.hasModeEditDefaultsPortletId(portletId)) {
526                            portletMode = LiferayPortletMode.EDIT_DEFAULTS;
527                    }
528                    else if (layoutTypePortlet.hasModeEditGuestPortletId(portletId)) {
529                            portletMode = LiferayPortletMode.EDIT_GUEST;
530                    }
531                    else if (layoutTypePortlet.hasModeHelpPortletId(portletId)) {
532                            portletMode = PortletMode.HELP;
533                    }
534                    else if (layoutTypePortlet.hasModePreviewPortletId(portletId)) {
535                            portletMode = LiferayPortletMode.PREVIEW;
536                    }
537                    else if (layoutTypePortlet.hasModePrintPortletId(portletId)) {
538                            portletMode = LiferayPortletMode.PRINT;
539                    }
540                    else {
541                            portletMode = PortletMode.VIEW;
542                    }
543    
544                    long scopeGroupId = getScopeGroupId(
545                            request, layoutTypePortlet, portletId);
546    
547                    Layout layoutTypePortletLayout = layoutTypePortlet.getLayout();
548    
549                    PortletPreferences portletPreferences =
550                            PortletPreferencesFactoryUtil.getPortletSetup(
551                                    scopeGroupId, layoutTypePortletLayout, portletId, null);
552    
553                    EventRequestImpl eventRequestImpl = EventRequestFactory.create(
554                            request, portlet, invokerPortlet, portletContext, windowState,
555                            portletMode, portletPreferences, layoutTypePortletLayout.getPlid());
556    
557                    eventRequestImpl.setEvent(
558                            serializeEvent(event, invokerPortlet.getPortletClassLoader()));
559    
560                    Layout layout = stateAwareResponseImpl.getLayout();
561    
562                    EventResponseImpl eventResponseImpl = EventResponseFactory.create(
563                            eventRequestImpl, response, portletId,
564                            stateAwareResponseImpl.getUser(), layout);
565    
566                    eventRequestImpl.defineObjects(portletConfig, eventResponseImpl);
567    
568                    try {
569                            try {
570                                    InvokerPortletImpl.clearResponse(
571                                            session, layout.getPrimaryKey(), portletId,
572                                            LanguageUtil.getLanguageId(eventRequestImpl));
573    
574                                    invokerPortlet.processEvent(
575                                            eventRequestImpl, eventResponseImpl);
576    
577                                    if (eventResponseImpl.isCalledSetRenderParameter()) {
578                                            Map<String, String[]> renderParameterMap =
579                                                    new HashMap<String, String[]>();
580    
581                                            renderParameterMap.putAll(
582                                                    eventResponseImpl.getRenderParameterMap());
583    
584                                            RenderParametersPool.put(
585                                                    request, layout.getPlid(), portletId,
586                                                    renderParameterMap);
587                                    }
588                            }
589                            catch (UnavailableException ue) {
590                                    throw ue;
591                            }
592    
593                            processEvents(
594                                    eventRequestImpl, eventResponseImpl, layoutTypePortlets);
595                    }
596                    finally {
597                            eventRequestImpl.cleanUp();
598                    }
599            }
600    
601            protected void processEvents(
602                            PortletRequestImpl portletRequestImpl,
603                            StateAwareResponseImpl stateAwareResponseImpl,
604                            List<LayoutTypePortlet> layoutTypePortlets)
605                    throws Exception {
606    
607                    List<Event> events = stateAwareResponseImpl.getEvents();
608    
609                    if (events.size() == 0) {
610                            return;
611                    }
612    
613                    for (Event event : events) {
614                            javax.xml.namespace.QName qName = event.getQName();
615    
616                            for (LayoutTypePortlet layoutTypePortlet : layoutTypePortlets) {
617                                    List<Portlet> portlets = layoutTypePortlet.getAllPortlets();
618    
619                                    for (Portlet portlet : portlets) {
620                                            QName processingQName = portlet.getProcessingEvent(
621                                                    qName.getNamespaceURI(), qName.getLocalPart());
622    
623                                            if (processingQName != null) {
624                                                    processEvent(
625                                                            portletRequestImpl, stateAwareResponseImpl,
626                                                            layoutTypePortlets, layoutTypePortlet, portlet,
627                                                            event);
628                                            }
629                                    }
630                            }
631                    }
632            }
633    
634            protected ActionForward processLayout(
635                            ActionMapping mapping, HttpServletRequest request,
636                            HttpServletResponse response, long plid)
637                    throws Exception {
638    
639                    HttpSession session = request.getSession();
640    
641                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
642                            WebKeys.THEME_DISPLAY);
643    
644                    try {
645                            Layout layout = themeDisplay.getLayout();
646    
647                            boolean resetLayout = ParamUtil.getBoolean(
648                                    request, "p_l_reset", PropsValues.LAYOUT_DEFAULT_P_L_RESET);
649    
650                            String portletId = ParamUtil.getString(request, "p_p_id");
651    
652                            Layout previousLayout = (Layout)session.getAttribute(
653                                    WebKeys.PREVIOUS_LAYOUT);
654    
655                            if ((previousLayout == null) ||
656                                    (layout.getPlid() != previousLayout.getPlid())) {
657    
658                                    session.setAttribute(WebKeys.PREVIOUS_LAYOUT, layout);
659    
660                                    if (themeDisplay.isSignedIn() &&
661                                            PropsValues.
662                                                    AUDIT_MESSAGE_COM_LIFERAY_PORTAL_MODEL_LAYOUT_VIEW &&
663                                            MessageBusUtil.hasMessageListener(DestinationNames.AUDIT)) {
664    
665                                            User user = themeDisplay.getUser();
666    
667                                            AuditMessage auditMessage = new AuditMessage(
668                                                    ActionKeys.VIEW, user.getCompanyId(), user.getUserId(),
669                                                    user.getFullName(), Layout.class.getName(),
670                                                    String.valueOf(layout.getPlid()));
671    
672                                            AuditRouterUtil.route(auditMessage);
673                                    }
674                            }
675    
676                            if (!PropsValues.TCK_URL && resetLayout &&
677                                    (Validator.isNull(portletId) ||
678                                     ((previousLayout != null) &&
679                                      (layout.getPlid() != previousLayout.getPlid())))) {
680    
681                                    // Always clear render parameters on a layout url, but do not
682                                    // clear on portlet urls invoked on the same layout
683    
684                                    RenderParametersPool.clear(request, plid);
685                            }
686    
687                            if (themeDisplay.isLifecycleAction()) {
688                                    Portlet portlet = processPortletRequest(
689                                            request, response, PortletRequest.ACTION_PHASE);
690    
691                                    if (portlet != null) {
692                                            ActionResponseImpl actionResponseImpl =
693                                                    (ActionResponseImpl)request.getAttribute(
694                                                            JavaConstants.JAVAX_PORTLET_RESPONSE);
695    
696                                            String redirectLocation =
697                                                    actionResponseImpl.getRedirectLocation();
698    
699                                            if (Validator.isNotNull(redirectLocation)) {
700                                                    response.sendRedirect(redirectLocation);
701    
702                                                    return null;
703                                            }
704    
705                                            if (portlet.isActionURLRedirect()) {
706                                                    redirectActionURL(
707                                                            request, response, actionResponseImpl, portlet);
708    
709                                                    return null;
710                                            }
711                                    }
712                            }
713                            else if (themeDisplay.isLifecycleRender()) {
714                                    processPortletRequest(
715                                            request, response, PortletRequest.RENDER_PHASE);
716                            }
717    
718                            if (themeDisplay.isLifecycleResource()) {
719                                    processPortletRequest(
720                                            request, response, PortletRequest.RESOURCE_PHASE);
721    
722                                    return null;
723                            }
724                            else {
725                                    if (response.isCommitted()) {
726                                            return null;
727                                    }
728    
729                                    if (layout != null) {
730    
731                                            // Include layout content before the page loads because
732                                            // portlets on the page can set the page title and page
733                                            // subtitle
734    
735                                            includeLayoutContent(
736                                                    request, response, themeDisplay, layout);
737    
738                                            if (themeDisplay.isStateExclusive()) {
739                                                    renderExclusive(request, response, themeDisplay);
740    
741                                                    return null;
742                                            }
743                                    }
744    
745                                    return mapping.findForward("portal.layout");
746                            }
747                    }
748                    catch (Exception e) {
749                            PortalUtil.sendError(e, request, response);
750    
751                            return null;
752                    }
753                    finally {
754                            if (!ServerDetector.isResin()) {
755                                    PortletRequest portletRequest =
756                                            (PortletRequest)request.getAttribute(
757                                                    JavaConstants.JAVAX_PORTLET_REQUEST);
758    
759                                    if (portletRequest != null) {
760                                            PortletRequestImpl portletRequestImpl =
761                                                    (PortletRequestImpl)portletRequest;
762    
763                                            portletRequestImpl.cleanUp();
764                                    }
765                            }
766                    }
767            }
768    
769            protected Portlet processPortletRequest(
770                            HttpServletRequest request, HttpServletResponse response,
771                            String lifecycle)
772                    throws Exception {
773    
774                    HttpSession session = request.getSession();
775    
776                    long companyId = PortalUtil.getCompanyId(request);
777                    User user = PortalUtil.getUser(request);
778                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
779    
780                    String portletId = ParamUtil.getString(request, "p_p_id");
781    
782                    if (Validator.isNull(portletId)) {
783                            return null;
784                    }
785    
786                    Portlet portlet = PortletLocalServiceUtil.getPortletById(
787                            companyId, portletId);
788    
789                    if (portlet == null) {
790                            return null;
791                    }
792    
793                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
794                            WebKeys.THEME_DISPLAY);
795    
796                    long scopeGroupId = PortalUtil.getScopeGroupId(request, portletId);
797    
798                    themeDisplay.setScopeGroupId(scopeGroupId);
799    
800                    ServletContext servletContext = (ServletContext)request.getAttribute(
801                            WebKeys.CTX);
802    
803                    if (user != null) {
804                            InvokerPortletImpl.clearResponse(
805                                    session, layout.getPrimaryKey(), portletId,
806                                    LanguageUtil.getLanguageId(request));
807                    }
808    
809                    PortletConfig portletConfig = PortletConfigFactoryUtil.create(
810                            portlet, servletContext);
811                    PortletContext portletContext = portletConfig.getPortletContext();
812    
813                    WindowState windowState = WindowStateFactory.getWindowState(
814                            ParamUtil.getString(request, "p_p_state"));
815    
816                    if (layout.isTypeControlPanel() &&
817                            ((windowState == null) || windowState.equals(WindowState.NORMAL) ||
818                             Validator.isNull(windowState.toString()))) {
819    
820                            windowState = WindowState.MAXIMIZED;
821                    }
822    
823                    PortletMode portletMode = PortletModeFactory.getPortletMode(
824                            ParamUtil.getString(request, "p_p_mode"));
825    
826                    HttpServletRequest ownerLayoutRequest = getOwnerLayoutRequestWrapper(
827                            request, portlet);
828    
829                    Layout ownerLayout = (Layout)ownerLayoutRequest.getAttribute(
830                            WebKeys.LAYOUT);
831    
832                    boolean allowAddPortletDefaultResource =
833                            PortalUtil.isAllowAddPortletDefaultResource(
834                                    ownerLayoutRequest, portlet);
835    
836                    PortletPreferencesIds portletPreferencesIds =
837                            PortletPreferencesFactoryUtil.getPortletPreferencesIds(
838                                    request, portletId);
839    
840                    PortletPreferences portletPreferences = null;
841    
842                    if (allowAddPortletDefaultResource) {
843                            portletPreferences =
844                                    PortletPreferencesLocalServiceUtil.getPreferences(
845                                            portletPreferencesIds);
846                    }
847                    else {
848                            portletPreferences =
849                                    PortletPreferencesLocalServiceUtil.getStrictPreferences(
850                                            portletPreferencesIds);
851                    }
852    
853                    processPublicRenderParameters(request, layout, portlet);
854    
855                    PermissionChecker permissionChecker =
856                            PermissionThreadLocal.getPermissionChecker();
857    
858                    if (lifecycle.equals(PortletRequest.ACTION_PHASE)) {
859                            if (!allowAddPortletDefaultResource) {
860                                    String url = null;
861    
862                                    LastPath lastPath = (LastPath)request.getAttribute(
863                                            WebKeys.LAST_PATH);
864    
865                                    if (lastPath != null) {
866                                            StringBundler sb = new StringBundler(3);
867    
868                                            sb.append(PortalUtil.getPortalURL(request));
869                                            sb.append(lastPath.getContextPath());
870                                            sb.append(lastPath.getPath());
871    
872                                            url = sb.toString();
873                                    }
874                                    else {
875                                            url = String.valueOf(request.getRequestURI());
876                                    }
877    
878                                    _log.error(
879                                            "Reject processAction for " + url + " on " +
880                                                    portlet.getPortletId());
881    
882                                    return null;
883                            }
884    
885                            InvokerPortlet invokerPortlet = PortletInstanceFactoryUtil.create(
886                                    portlet, servletContext);
887    
888                            String contentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
889    
890                            if (_log.isDebugEnabled()) {
891                                    _log.debug("Content type " + contentType);
892                            }
893    
894                            UploadServletRequest uploadServletRequest = null;
895    
896                            try {
897                                    if ((contentType != null) &&
898                                            contentType.startsWith(ContentTypes.MULTIPART_FORM_DATA)) {
899    
900                                            PortletConfigImpl invokerPortletConfigImpl =
901                                                    (PortletConfigImpl)invokerPortlet.getPortletConfig();
902    
903                                            if (invokerPortlet.isStrutsPortlet() ||
904                                                    ((invokerPortletConfigImpl != null) &&
905                                                     !invokerPortletConfigImpl.isWARFile())) {
906    
907                                                    uploadServletRequest = new UploadServletRequestImpl(
908                                                            request);
909    
910                                                    request = uploadServletRequest;
911                                            }
912                                    }
913    
914                                    if (PropsValues.AUTH_TOKEN_CHECK_ENABLED &&
915                                            invokerPortlet.isCheckAuthToken()) {
916    
917                                            AuthTokenUtil.check(request);
918                                    }
919    
920                                    ActionRequestImpl actionRequestImpl =
921                                            ActionRequestFactory.create(
922                                                    request, portlet, invokerPortlet, portletContext,
923                                                    windowState, portletMode, portletPreferences,
924                                                    layout.getPlid());
925    
926                                    ActionResponseImpl actionResponseImpl =
927                                            ActionResponseFactory.create(
928                                                    actionRequestImpl, response, portletId, user, layout,
929                                                    windowState, portletMode);
930    
931                                    actionRequestImpl.defineObjects(
932                                            portletConfig, actionResponseImpl);
933    
934                                    ServiceContext serviceContext =
935                                            ServiceContextFactory.getInstance(actionRequestImpl);
936    
937                                    ServiceContextThreadLocal.pushServiceContext(serviceContext);
938    
939                                    boolean access = PortletPermissionUtil.hasAccessPermission(
940                                            permissionChecker, scopeGroupId, ownerLayout, portlet,
941                                            portletMode);
942    
943                                    if (access) {
944                                            invokerPortlet.processAction(
945                                                    actionRequestImpl, actionResponseImpl);
946    
947                                            actionResponseImpl.transferHeaders(response);
948                                    }
949    
950                                    RenderParametersPool.put(
951                                            request, layout.getPlid(), portletId,
952                                            actionResponseImpl.getRenderParameterMap());
953    
954                                    List<LayoutTypePortlet> layoutTypePortlets = null;
955    
956                                    if (!actionResponseImpl.getEvents().isEmpty()) {
957                                            if (PropsValues.PORTLET_EVENT_DISTRIBUTION_LAYOUT_SET) {
958                                                    layoutTypePortlets = getLayoutTypePortlets(
959                                                            layout.getGroupId(), layout.isPrivateLayout());
960                                            }
961                                            else {
962                                                    if (layout.isTypePortlet()) {
963                                                            LayoutTypePortlet layoutTypePortlet =
964                                                                    (LayoutTypePortlet)layout.getLayoutType();
965    
966                                                            layoutTypePortlets =
967                                                                    new ArrayList<LayoutTypePortlet>();
968    
969                                                            layoutTypePortlets.add(layoutTypePortlet);
970                                                    }
971                                            }
972    
973                                            processEvents(
974                                                    actionRequestImpl, actionResponseImpl,
975                                                    layoutTypePortlets);
976    
977                                            actionRequestImpl.defineObjects(
978                                                    portletConfig, actionResponseImpl);
979                                    }
980                            }
981                            finally {
982                                    if (uploadServletRequest != null) {
983                                            uploadServletRequest.cleanUp();
984                                    }
985    
986                                    ServiceContextThreadLocal.popServiceContext();
987                            }
988                    }
989                    else if (lifecycle.equals(PortletRequest.RENDER_PHASE) ||
990                                     lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
991    
992                            PortalUtil.updateWindowState(
993                                    portletId, user, layout, windowState, request);
994    
995                            PortalUtil.updatePortletMode(
996                                    portletId, user, layout, portletMode, request);
997                    }
998    
999                    if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
1000                            if (!allowAddPortletDefaultResource) {
1001                                    String url = null;
1002    
1003                                    LastPath lastPath = (LastPath)request.getAttribute(
1004                                            WebKeys.LAST_PATH);
1005    
1006                                    if (lastPath != null) {
1007                                            StringBundler sb = new StringBundler(3);
1008    
1009                                            sb.append(PortalUtil.getPortalURL(request));
1010                                            sb.append(lastPath.getContextPath());
1011                                            sb.append(lastPath.getPath());
1012    
1013                                            url = sb.toString();
1014                                    }
1015                                    else {
1016                                            url = String.valueOf(request.getRequestURI());
1017                                    }
1018    
1019                                    _log.error(
1020                                            "Reject serveResource for " + url + " on " +
1021                                                    portlet.getPortletId());
1022    
1023                                    return null;
1024                            }
1025    
1026                            InvokerPortlet invokerPortlet = PortletInstanceFactoryUtil.create(
1027                                    portlet, servletContext);
1028    
1029                            PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
1030    
1031                            String portletPrimaryKey = PortletPermissionUtil.getPrimaryKey(
1032                                    layout.getPlid(), portletId);
1033    
1034                            portletDisplay.setId(portletId);
1035                            portletDisplay.setRootPortletId(portlet.getRootPortletId());
1036                            portletDisplay.setInstanceId(portlet.getInstanceId());
1037                            portletDisplay.setResourcePK(portletPrimaryKey);
1038                            portletDisplay.setPortletName(portletConfig.getPortletName());
1039                            portletDisplay.setNamespace(
1040                                    PortalUtil.getPortletNamespace(portletId));
1041    
1042                            WebDAVStorage webDAVStorage = portlet.getWebDAVStorageInstance();
1043    
1044                            if (webDAVStorage != null) {
1045                                    portletDisplay.setWebDAVEnabled(true);
1046                            }
1047                            else {
1048                                    portletDisplay.setWebDAVEnabled(false);
1049                            }
1050    
1051                            ResourceRequestImpl resourceRequestImpl =
1052                                    ResourceRequestFactory.create(
1053                                            request, portlet, invokerPortlet, portletContext,
1054                                            windowState, portletMode, portletPreferences,
1055                                            layout.getPlid());
1056    
1057                            ResourceResponseImpl resourceResponseImpl =
1058                                    ResourceResponseFactory.create(
1059                                            resourceRequestImpl, response, portletId, companyId);
1060    
1061                            resourceRequestImpl.defineObjects(
1062                                    portletConfig, resourceResponseImpl);
1063    
1064                            try {
1065                                    ServiceContext serviceContext =
1066                                            ServiceContextFactory.getInstance(resourceRequestImpl);
1067    
1068                                    ServiceContextThreadLocal.pushServiceContext(serviceContext);
1069    
1070                                    boolean access = PortletPermissionUtil.hasAccessPermission(
1071                                            permissionChecker, scopeGroupId, ownerLayout, portlet,
1072                                            portletMode);
1073    
1074                                    if (access) {
1075                                            invokerPortlet.serveResource(
1076                                                    resourceRequestImpl, resourceResponseImpl);
1077    
1078                                            resourceResponseImpl.transferHeaders(response);
1079                                    }
1080                            }
1081                            finally {
1082                                    ServiceContextThreadLocal.popServiceContext();
1083                            }
1084                    }
1085    
1086                    return portlet;
1087            }
1088    
1089            protected void processPublicRenderParameters(
1090                    HttpServletRequest request, Layout layout, Portlet portlet) {
1091    
1092                    ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
1093                            WebKeys.THEME_DISPLAY);
1094    
1095                    Map<String, String[]> publicRenderParameters =
1096                            PublicRenderParametersPool.get(request, layout.getPlid());
1097    
1098                    Enumeration<String> enu = request.getParameterNames();
1099    
1100                    while (enu.hasMoreElements()) {
1101                            String name = enu.nextElement();
1102    
1103                            String[] values = request.getParameterValues(name);
1104    
1105                            QName qName = PortletQNameUtil.getQName(name);
1106    
1107                            if (qName == null) {
1108                                    continue;
1109                            }
1110    
1111                            PublicRenderParameter publicRenderParameter =
1112                                    portlet.getPublicRenderParameter(
1113                                            qName.getNamespaceURI(), qName.getLocalPart());
1114    
1115                            if (publicRenderParameter == null) {
1116                                    continue;
1117                            }
1118    
1119                            String publicRenderParameterName =
1120                                    PortletQNameUtil.getPublicRenderParameterName(qName);
1121    
1122                            if (name.startsWith(
1123                                            PortletQName.PUBLIC_RENDER_PARAMETER_NAMESPACE)) {
1124    
1125                                    if (themeDisplay.isLifecycleAction()) {
1126                                            String[] oldValues = publicRenderParameters.get(
1127                                                    publicRenderParameterName);
1128    
1129                                            if ((oldValues != null) && (oldValues.length != 0)) {
1130                                                    values = ArrayUtil.append(values, oldValues);
1131                                            }
1132                                    }
1133    
1134                                    publicRenderParameters.put(publicRenderParameterName, values);
1135                            }
1136                            else {
1137                                    publicRenderParameters.remove(publicRenderParameterName);
1138                            }
1139                    }
1140            }
1141    
1142            protected void redirectActionURL(
1143                            HttpServletRequest request, HttpServletResponse response,
1144                            ActionResponseImpl actionResponseImpl, Portlet portlet)
1145                    throws Exception {
1146    
1147                    ActionRequestImpl actionRequestImpl =
1148                            (ActionRequestImpl)request.getAttribute(
1149                                    JavaConstants.JAVAX_PORTLET_REQUEST);
1150    
1151                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
1152    
1153                    PortletURL portletURL = new PortletURLImpl(
1154                            actionRequestImpl, actionRequestImpl.getPortletName(),
1155                            layout.getPlid(), PortletRequest.RENDER_PHASE);
1156    
1157                    Map<String, String[]> renderParameters =
1158                            actionResponseImpl.getRenderParameterMap();
1159    
1160                    for (Map.Entry<String, String[]> entry : renderParameters.entrySet()) {
1161                            String key = entry.getKey();
1162                            String[] value = entry.getValue();
1163    
1164                            portletURL.setParameter(key, value);
1165                    }
1166    
1167                    response.sendRedirect(portletURL.toString());
1168            }
1169    
1170            protected void renderExclusive(
1171                            HttpServletRequest request, HttpServletResponse response,
1172                            ThemeDisplay themeDisplay)
1173                    throws Exception {
1174    
1175                    RenderRequestImpl renderRequestImpl =
1176                            (RenderRequestImpl)request.getAttribute(
1177                                    JavaConstants.JAVAX_PORTLET_REQUEST);
1178    
1179                    RenderResponseImpl renderResponseImpl =
1180                            (RenderResponseImpl)request.getAttribute(
1181                                    JavaConstants.JAVAX_PORTLET_RESPONSE);
1182    
1183                    StringServletResponse stringResponse =
1184                            (StringServletResponse)renderRequestImpl.getAttribute(
1185                                    WebKeys.STRING_SERVLET_RESPONSE);
1186    
1187                    if (stringResponse == null) {
1188                            stringResponse = (StringServletResponse)
1189                                    renderResponseImpl.getHttpServletResponse();
1190    
1191                            ServletContext servletContext =
1192                                    (ServletContext)request.getAttribute(WebKeys.CTX);
1193    
1194                            Portlet portlet = processPortletRequest(
1195                                    request, response, PortletRequest.RENDER_PHASE);
1196    
1197                            RuntimePortletUtil.processPortlet(
1198                                    servletContext, request, stringResponse, renderRequestImpl,
1199                                    renderResponseImpl, portlet.getPortletId(), null, true);
1200    
1201                            if (Validator.isNull(stringResponse.getString())) {
1202                                    stringResponse.setString(null);
1203                            }
1204                    }
1205    
1206                    renderResponseImpl.transferHeaders(response);
1207    
1208                    if (stringResponse.isCalledGetOutputStream()) {
1209                            UnsyncByteArrayOutputStream ubaos =
1210                                    stringResponse.getUnsyncByteArrayOutputStream();
1211    
1212                            InputStream is = new UnsyncByteArrayInputStream(
1213                                    ubaos.unsafeGetByteArray(), 0, ubaos.size());
1214    
1215                            ServletResponseUtil.sendFile(
1216                                    request, response, renderResponseImpl.getResourceName(), is,
1217                                    renderResponseImpl.getContentType());
1218                    }
1219                    else if (stringResponse.isCalledGetWriter()) {
1220                            byte[] content = stringResponse.getString().getBytes(
1221                                    StringPool.UTF8);
1222    
1223                            ServletResponseUtil.sendFile(
1224                                    request, response, renderResponseImpl.getResourceName(),
1225                                    content, renderResponseImpl.getContentType());
1226                    }
1227    
1228                    renderRequestImpl.cleanUp();
1229            }
1230    
1231            protected Event serializeEvent(
1232                    Event event, ClassLoader portletClassLoader) {
1233    
1234                    Serializable value = event.getValue();
1235    
1236                    if (value == null) {
1237                            return event;
1238                    }
1239    
1240                    Class<?> valueClass = value.getClass();
1241    
1242                    String valueClassName = valueClass.getName();
1243    
1244                    try {
1245                            Class<?> loadedValueClass = portletClassLoader.loadClass(
1246                                    valueClassName);
1247    
1248                            if (loadedValueClass.equals(valueClass)) {
1249                                    return event;
1250                            }
1251                    }
1252                    catch (ClassNotFoundException cnfe) {
1253                            if (_log.isWarnEnabled()) {
1254                                    _log.warn(
1255                                            portletClassLoader.toString() + " does not contain " +
1256                                                    valueClassName,
1257                                            cnfe);
1258                            }
1259                    }
1260    
1261                    EventImpl eventImpl = (EventImpl)event;
1262    
1263                    String base64Value = eventImpl.getBase64Value();
1264    
1265                    value = (Serializable)Base64.stringToObject(
1266                            base64Value, portletClassLoader);
1267    
1268                    return new EventImpl(event.getName(), event.getQName(), value);
1269            }
1270    
1271            private static Log _log = LogFactoryUtil.getLog(LayoutAction.class);
1272    
1273    }