Native 404 handling with content dimensions (multi-language sites)

With PR #2374 Neos got the ability to render status messages for the status codes 403, 404 and 410 using Fusion.

The matcher receives the context values exception , renderingOptions , statusCode ,
statusMessage and referenceCode and will by default render the previous template.

By extending the error case you can add custom 404 rendering like in the example below.

#
# Extend error matcher to render the document with uriPathSegment `notfound`
# for exceptions with 4xx status code
#
error {
	@context.notfoundDocument = ${q(site).children('[instanceof Neos.Neos:Document]').filter('[uriPathSegment="notfound"]').get(0)}

	4xx {
		@position = 'start'
		condition = ${statusCode >= 400 && statusCode < 500 && notfoundDocument}
		renderer = Neos.Fusion:Renderer {
			@context.node = ${notfoundDocument}
			renderPath = '/root'
		}
	}
}

But the to have fully correct rendering of the error, especially when content dimensions are involved, the node is not enough (hum 007 theme along). Add site and documentNode , too. And you must adjust the context to have the correct dimensions, since contrary to what you might expect, the site will have default dimensions in it’s context in the error case.

To fetch the dimensions, you can do something like this (https://gist.github.com/kdambekalns/d9a7f8f09f1c69179816f42d82c68c8c):

// Reads the dimensions country and language from the request path and
// returns a DataStructure with dimensions and targetDimensions for use
// with the Eel context() operation.

prototype(Acme.Com:ExtractDimensions) < prototype(Neos.Fusion:Value) {
    trimmedPath = ${String.trim(request.httpRequest.uri.path, '/')}
    pathParts = ${String.split(this.trimmedPath, '/', 2)}
    @context{
        dimensionParts = ${String.split(this.pathParts[0], '_')}
        defaultPresets = Neos.Fusion:DataStructure {
            country = ${Configuration.setting('Neos.ContentRepository.contentDimensions.country.defaultPreset')}
            language = ${Configuration.setting('Neos.ContentRepository.contentDimensions.language.defaultPreset')}
        }
    }
    value = Neos.Fusion:Case {
        dimensionsFound {
            condition = ${Array.length(dimensionParts) == 2 && Configuration.setting('Neos.ContentRepository.contentDimensions.country.presets.' + dimensionParts[0] + '.values') && Configuration.setting('Neos.ContentRepository.contentDimensions.language.presets.' + dimensionParts[1] + '.values')}
            renderer = Neos.Fusion:DataStructure {
                dimensions = Neos.Fusion:DataStructure {
                    country = ${Configuration.setting('Neos.ContentRepository.contentDimensions.country.presets.' + dimensionParts[0] + '.values')}
                    language = ${Configuration.setting('Neos.ContentRepository.contentDimensions.language.presets.' + dimensionParts[1] + '.values')}
                }
                targetDimensions = Neos.Fusion:DataStructure {
                    country = ${dimensionParts[0]}
                    language = ${dimensionParts[1]}
                }
            }
        }

        noDimensionsFound {
            condition = true
            renderer = Neos.Fusion:DataStructure {
                dimensions = Neos.Fusion:DataStructure {
                    country = ${Configuration.setting('Neos.ContentRepository.contentDimensions.country.presets.' + defaultPresets.country + '.values')}
                    language = ${Configuration.setting('Neos.ContentRepository.contentDimensions.language.presets.' + defaultPresets.language + '.values')}
                }
                targetDimensions = Neos.Fusion:DataStructure {
                    country = ${defaultPresets.country}
                    language = ${defaultPresets.language}
                }
            }
        }
    }
}

And then use it like this:

error {
    4xx {
        dimensions = Acme.Com:ExtractDimensions
        @context.notfoundDocument = ${q(site).context({'dimensions': this.dimensions.dimensions, 'targetDimensions': this.dimensions.targetDimensions}).children('[instanceof Neos.Neos:Document]').filter('[uriPathSegment="404"]').get(0)}
        @position = 'start'
        condition = ${statusCode >= 400 && statusCode < 500 && notfoundDocument}
        renderer = Neos.Fusion:Renderer {
            @context {
                site = ${notfoundDocument.context.currentSiteNode}
                node = ${notfoundDocument}
                documentNode = ${notfoundDocument}
            }
            renderPath = '/root'
        }
    }
}
5 Likes

Thank you!
This worked for me :slight_smile:

Ok, my solution from above misses one thing: For strings translated via XLIFF files, the languages do not match.

See https://github.com/neos/neos-development-collection/issues/3190 for details and a workaround.