Skip to content

Commit

Permalink
Fragment and Image component return 200 when content is missing #10822 (
Browse files Browse the repository at this point in the history
  • Loading branch information
rymsha authored Dec 20, 2024
1 parent 631f853 commit e5e9202
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 247 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ public HtmlBuilder closeEmpty()
public HtmlBuilder attribute( final String name, final String value )
{
this.str.append( ' ' );
this.str.append( name );
this.str.append( escaper.escape( name ) );
this.str.append( "=\"" );
this.str.append( value );
this.str.append( escaper.escape( value ) );
this.str.append( '"' );
this.addedInner = false;
this.closedEmptyTag = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.enonic.xp.portal.impl.rendering;

import java.text.MessageFormat;

import com.google.common.html.HtmlEscapers;
import com.google.common.base.Function;
import com.google.common.net.MediaType;

import com.enonic.xp.app.ApplicationKey;
Expand All @@ -12,33 +10,39 @@
import com.enonic.xp.portal.RenderMode;
import com.enonic.xp.portal.controller.ControllerScript;
import com.enonic.xp.portal.controller.ControllerScriptFactory;
import com.enonic.xp.portal.impl.html.HtmlBuilder;
import com.enonic.xp.region.Component;
import com.enonic.xp.region.ComponentDescriptor;
import com.enonic.xp.region.DescriptorBasedComponent;
import com.enonic.xp.resource.ResourceKey;
import com.enonic.xp.web.HttpStatus;

import static com.google.common.base.Strings.nullToEmpty;
import static java.util.Objects.requireNonNullElse;

public abstract class DescriptorBasedComponentRenderer<R extends DescriptorBasedComponent>
implements Renderer<R>
{
private static final String EMPTY_COMPONENT_EDIT_MODE_HTML =
"<div " + RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE + "=\"{0}\"></div>";

private static final String EMPTY_COMPONENT_PREVIEW_MODE_HTML =
"<div " + RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE + "=\"{0}\"></div>";

private static final String COMPONENT_PLACEHOLDER_ERROR_HTML = "<div " + RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE +
"=\"{0}\" data-portal-placeholder=\"true\" data-portal-placeholder-error=\"true\"><span class=\"data-portal-placeholder-error\">{1}</span></div>";

private static final LiveEditAttributeInjection LIVE_EDIT_ATTRIBUTE_INJECTION = new LiveEditAttributeInjection();

private final ControllerScriptFactory controllerScriptFactory;

public DescriptorBasedComponentRenderer( final ControllerScriptFactory controllerScriptFactory )
private final Class<R> type;

private final Function<DescriptorKey, ComponentDescriptor> componentDescriptorGetter;

public DescriptorBasedComponentRenderer( final ControllerScriptFactory controllerScriptFactory, final Class<R> type,
final Function<DescriptorKey, ComponentDescriptor> componentDescriptorGetter )
{
this.controllerScriptFactory = controllerScriptFactory;
this.type = type;
this.componentDescriptorGetter = componentDescriptorGetter;
}

@Override
public Class<R> getType()
{
return type;
}

@Override
Expand All @@ -64,7 +68,7 @@ private PortalResponse doRender( final R component, final PortalRequest portalRe
final ComponentDescriptor descriptor = resolveDescriptor( component );
if ( descriptor == null )
{
return renderEmptyComponent( component, portalRequest );
return renderEmptyComponent( component );
}

final ResourceKey script = descriptor.getComponentPath().resolve( descriptor.getComponentPath().getName() + ".js" );
Expand All @@ -89,11 +93,10 @@ private PortalResponse doRender( final R component, final PortalRequest portalRe
{
if ( portalResponse.getStatus().equals( HttpStatus.METHOD_NOT_ALLOWED ) )
{
final String errorMessage = "No method provided to handle request";
return renderErrorComponentPlaceHolder( component, errorMessage );
return renderErrorComponentPlaceHolder( component, "No method provided to handle request" );
}

return renderEmptyComponent( component, portalRequest );
return renderEmptyComponent( component );
}
}

Expand All @@ -108,32 +111,13 @@ private PortalResponse doRender( final R component, final PortalRequest portalRe
}
}

private PortalResponse renderEmptyComponent( final DescriptorBasedComponent component, final PortalRequest portalRequest )
private PortalResponse renderEmptyComponent( final DescriptorBasedComponent component )
{
final RenderMode renderMode = portalRequest.getMode();
if ( renderMode == RenderMode.EDIT )
{
return renderEmptyComponentEditMode( component );
}
else
{
return renderEmptyComponentPreviewMode( component );
}
}

private PortalResponse renderEmptyComponentEditMode( final DescriptorBasedComponent component )
{
final String html = MessageFormat.format( EMPTY_COMPONENT_EDIT_MODE_HTML, component.getType().toString() );

return PortalResponse.create().
contentType( MediaType.HTML_UTF_8 ).
body( html ).
build();
}

private PortalResponse renderEmptyComponentPreviewMode( final DescriptorBasedComponent component )
{
final String html = MessageFormat.format( EMPTY_COMPONENT_PREVIEW_MODE_HTML, component.getType().toString() );
final String html = new HtmlBuilder().open( "div" )
.attribute( RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE, component.getType().toString() )
.text( "" )
.close()
.toString();

return PortalResponse.create().
contentType( MediaType.HTML_UTF_8 ).
Expand All @@ -143,9 +127,16 @@ private PortalResponse renderEmptyComponentPreviewMode( final DescriptorBasedCom

private PortalResponse renderErrorComponentPlaceHolder( final DescriptorBasedComponent component, final String errorMessage )
{
final String escapedMessage = errorMessage == null ? "" : HtmlEscapers.htmlEscaper().escape( errorMessage );
final String html = MessageFormat.format( COMPONENT_PLACEHOLDER_ERROR_HTML, component.getType().toString(), escapedMessage );

final String html = new HtmlBuilder().open( "div" )
.attribute( RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE, component.getType().toString() )
.attribute( "data-portal-placeholder", "true" )
.attribute( "data-portal-placeholder-error", "true" )
.open( "span" )
.attribute( "class", "data-portal-placeholder-error" )
.escapedText( requireNonNullElse( errorMessage, "" ) )
.close()
.close()
.toString();
return PortalResponse.create().
contentType( MediaType.HTML_UTF_8 ).
body( html ).
Expand All @@ -155,8 +146,6 @@ private PortalResponse renderErrorComponentPlaceHolder( final DescriptorBasedCom
private ComponentDescriptor resolveDescriptor( final DescriptorBasedComponent component )
{
final DescriptorKey descriptorKey = component.getDescriptor();
return descriptorKey == null ? null : getComponentDescriptor( descriptorKey );
return descriptorKey == null ? null : componentDescriptorGetter.apply( descriptorKey );
}

protected abstract ComponentDescriptor getComponentDescriptor( DescriptorKey descriptorKey );
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.enonic.xp.portal.impl.rendering;

import java.text.MessageFormat;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.html.HtmlEscapers;
import com.google.common.net.MediaType;

import com.enonic.xp.content.Content;
Expand All @@ -15,24 +12,18 @@
import com.enonic.xp.portal.PortalRequest;
import com.enonic.xp.portal.PortalResponse;
import com.enonic.xp.portal.RenderMode;
import com.enonic.xp.portal.impl.html.HtmlBuilder;
import com.enonic.xp.region.Component;
import com.enonic.xp.region.FragmentComponent;
import com.enonic.xp.region.LayoutComponent;
import com.enonic.xp.region.LayoutDescriptor;
import com.enonic.xp.region.LayoutDescriptorService;
import com.enonic.xp.region.LayoutRegions;
import com.enonic.xp.region.Region;
import com.enonic.xp.web.HttpStatus;

public final class FragmentRenderer
{
private static final String EMPTY_FRAGMENT_HTML = "<div " + RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE + "=\"{0}\"></div>";

private static final String EDIT_MODE_FRAGMENT_WRAPPER_HTML =
"<div " + RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE + "=\"{0}\">{1}</div>";

private static final String COMPONENT_PLACEHOLDER_ERROR_HTML = "<div " + RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE +
"=\"{0}\" data-portal-placeholder=\"true\" data-portal-placeholder-error=\"true\"><span class=\"data-portal-placeholder-error\">{1}</span></div>";

private static final Logger LOG = LoggerFactory.getLogger( FragmentRenderer.class );

private final ContentService contentService;
Expand All @@ -58,22 +49,28 @@ public PortalResponse render( final FragmentComponent component, final PortalReq

if ( component.getFragment() == null )
{
return renderEmptyFragment( renderMode, component );
if ( renderMode == RenderMode.EDIT )
{
return renderEmptyFragmentInEdit( component );
}
else
{
return renderResponse( "", HttpStatus.NOT_FOUND );
}
}

final Component fragmentComponent = getFragmentComponent( component );
if ( fragmentComponent == null )
{
LOG.warn( "Fragment content could not be found. ContentId: " + component.getFragment().toString() );
LOG.warn( "Fragment content could not be found. ContentId: {}", component.getFragment() );

if ( renderMode == RenderMode.EDIT )
{
final String errorMessage = "Fragment content could not be found";
return renderErrorComponentPlaceHolder( component, errorMessage );
return renderErrorComponentPlaceHolderInEdit( component, "Fragment content could not be found" );
}
else
{
return renderEmptyFragment( renderMode, component );
return renderResponse( "", HttpStatus.NOT_FOUND );
}
}

Expand All @@ -98,7 +95,7 @@ public PortalResponse render( final FragmentComponent component, final PortalReq

if ( body.contains( noMethodErrorMessage ) )
{
return renderErrorComponentPlaceHolder( component, noMethodErrorMessage );
return renderErrorComponentPlaceHolderInEdit( component, noMethodErrorMessage );
}

return wrapFragmentForEditMode( fragmentResponse, type );
Expand All @@ -108,8 +105,11 @@ public PortalResponse render( final FragmentComponent component, final PortalReq

private PortalResponse wrapFragmentForEditMode( final PortalResponse response, final String type )
{
final String body = (String) response.getBody();
final String wrappedBody = MessageFormat.format( EDIT_MODE_FRAGMENT_WRAPPER_HTML, type, body );
final String wrappedBody = new HtmlBuilder().open( "div" )
.attribute( RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE, type )
.text( (String) response.getBody() )
.close()
.toString();
return PortalResponse.create( response ).body( wrappedBody ).build();
}

Expand Down Expand Up @@ -176,17 +176,34 @@ private LayoutComponent buildLayoutWithRegions( final LayoutComponent existingLa
return layoutBuilder.regions( regionsBuilder.build() ).build();
}

private PortalResponse renderEmptyFragment( final RenderMode renderMode, final FragmentComponent component )
private PortalResponse renderEmptyFragmentInEdit( final FragmentComponent component )
{
final String type = component.getType().toString();
final String html = renderMode == RenderMode.EDIT ? MessageFormat.format( EMPTY_FRAGMENT_HTML, type ) : "";
return PortalResponse.create().body( html ).contentType( MediaType.HTML_UTF_8 ).postProcess( false ).build();
final String html = new HtmlBuilder().open( "div" )
.attribute( RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE, component.getType().toString() )
.text( "" )
.close()
.toString();
return renderResponse( html, HttpStatus.OK );
}

private static PortalResponse renderResponse( final String html, HttpStatus status )
{
return PortalResponse.create().body( html ).status( status ).contentType( MediaType.HTML_UTF_8 ).postProcess( false ).build();
}

private PortalResponse renderErrorComponentPlaceHolder( final FragmentComponent component, final String errorMessage )
private PortalResponse renderErrorComponentPlaceHolderInEdit( final FragmentComponent component, final String errorMessage )
{
final String escapedMessage = HtmlEscapers.htmlEscaper().escape( errorMessage );
final String html = MessageFormat.format( COMPONENT_PLACEHOLDER_ERROR_HTML, component.getType().toString(), escapedMessage );
return PortalResponse.create().contentType( MediaType.HTML_UTF_8 ).postProcess( false ).body( html ).build();
final String html = new HtmlBuilder().open( "div" )
.attribute( RenderingConstants.PORTAL_COMPONENT_ATTRIBUTE, component.getType().toString() )
.attribute( "data-portal-placeholder", "true" )
.attribute( "data-portal-placeholder-error", "true" )
.open( "span" )
.attribute( "class", "data-portal-placeholder-error" )
.escapedText( errorMessage )
.close()
.close()
.toString();

return renderResponse( html, HttpStatus.OK );
}
}
Loading

0 comments on commit e5e9202

Please sign in to comment.