Skip to content

Commit

Permalink
Make static serviceUrls in Java instead of the js assetUrl hack #454
Browse files Browse the repository at this point in the history
  • Loading branch information
anatol-sialitski authored and rymsha committed Dec 11, 2024
1 parent 0d95a05 commit ac888c6
Show file tree
Hide file tree
Showing 6 changed files with 675 additions and 15 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ repositories {

dependencies {
compileOnly "com.enonic.xp:core-api:${xpVersion}"
compileOnly "com.enonic.xp:portal-api:${xpVersion}"
compileOnly "com.enonic.xp:script-api:${xpVersion}"

implementation "com.enonic.xp:lib-io:${xpVersion}"
Expand All @@ -29,6 +30,7 @@ dependencies {
testImplementation "com.enonic.xp:testing:${xpVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.3"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.11.3"
testImplementation 'org.mockito:mockito-core:5.12.0'
}

node {
Expand Down
154 changes: 154 additions & 0 deletions src/main/java/com/enonic/lib/react4xp/url/ContentResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package com.enonic.lib.react4xp.url;

import java.util.concurrent.Callable;

import com.enonic.xp.content.Content;
import com.enonic.xp.content.ContentId;
import com.enonic.xp.content.ContentNotFoundException;
import com.enonic.xp.content.ContentPath;
import com.enonic.xp.content.ContentService;
import com.enonic.xp.context.Context;
import com.enonic.xp.context.ContextAccessor;
import com.enonic.xp.context.ContextBuilder;
import com.enonic.xp.portal.PortalRequest;
import com.enonic.xp.portal.RenderMode;
import com.enonic.xp.security.RoleKeys;
import com.enonic.xp.security.acl.Permission;
import com.enonic.xp.security.auth.AuthenticationInfo;
import com.enonic.xp.site.Site;

public class ContentResolver
{
private final ContentService contentService;

public ContentResolver( final ContentService contentService )
{
this.contentService = contentService;
}

public ContentResolverResult resolve( final PortalRequest request )
{
final ContentPath contentPath = request.getContentPath();

if ( contentPath.isRoot() )
{
return new ContentResolverResult( null, false, null, "/", contentPath.toString() );
}

if ( request.getMode() == RenderMode.EDIT )
{
return resolveInEditMode( contentPath );
}
else
{
return resolveInNonEditMode( contentPath );
}
}

private ContentResolverResult resolveInNonEditMode( final ContentPath contentPath )
{
final Content content = callAsContentAdmin( () -> getContentByPath( contentPath ) );

final Site site = content != null && content.isSite()
? (Site) content
: callAsContentAdmin( () -> this.contentService.findNearestSiteByPath( contentPath ) );

final String siteRelativePath = siteRelativePath( site, contentPath );
return new ContentResolverResult( visibleContent( content ), content != null, site, siteRelativePath, contentPath.toString() );
}

private ContentResolverResult resolveInEditMode( final ContentPath contentPath )
{
final String contentPathString = contentPath.toString();

final ContentId contentId = tryConvertToContentId( contentPathString );

final Content contentById = contentId != null ? callAsContentAdmin( () -> getContentById( contentId ) ) : null;

final Content content = contentById != null ? contentById : callAsContentAdmin( () -> this.getContentByPath( contentPath ) );

if ( content == null )
{
return new ContentResolverResult( null, false, null, contentPathString, contentPathString );
}

if ( content.getPath().isRoot() )
{
return new ContentResolverResult( null, false, null, "/", contentPathString );
}

final Site site =
content.isSite() ? (Site) content : callAsContentAdmin( () -> this.contentService.getNearestSite( content.getId() ) );

final String siteRelativePath = siteRelativePath( site, content.getPath() );
return new ContentResolverResult( visibleContent( content ), true, site, siteRelativePath, contentPathString );
}

private static ContentId tryConvertToContentId( final String contentPathString )
{
try
{
return ContentId.from( contentPathString.substring( 1 ) );
}
catch ( Exception e )
{
return null;
}
}

private Content visibleContent( final Content content )
{
return content == null || content.getPath().isRoot() ||
!content.getPermissions().isAllowedFor( ContextAccessor.current().getAuthInfo().getPrincipals(), Permission.READ )
? null
: content;
}

private Content getContentById( final ContentId contentId )
{
try
{
return this.contentService.getById( contentId );
}
catch ( final ContentNotFoundException e )
{
return null;
}
}

private Content getContentByPath( final ContentPath contentPath )
{
try
{
return this.contentService.getByPath( contentPath );
}
catch ( final ContentNotFoundException e )
{
return null;
}
}

private static <T> T callAsContentAdmin( final Callable<T> callable )
{
final Context context = ContextAccessor.current();
return ContextBuilder.from( context ).authInfo(
AuthenticationInfo.copyOf( context.getAuthInfo() ).principals( RoleKeys.CONTENT_MANAGER_ADMIN ).build() ).build().callWith(
callable );
}

private static String siteRelativePath( final Site site, final ContentPath contentPath )
{
if ( site == null )
{
return contentPath.toString();
}
else if ( site.getPath().equals( contentPath ) )
{
return "/";
}
else
{
return contentPath.toString().substring( site.getPath().toString().length() );
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.enonic.lib.react4xp.url;

import com.enonic.xp.content.Content;
import com.enonic.xp.site.Site;
import com.enonic.xp.web.WebException;

public final class ContentResolverResult
{
private final Content content;

private final Site nearestSite;

private final String siteRelativePath;

private final String notFoundHint;

private final boolean contentExists;

ContentResolverResult( final Content content, final boolean contentExists, final Site nearestSite, final String siteRelativePath,
final String notFoundHint )
{
this.content = content;
this.nearestSite = nearestSite;
this.siteRelativePath = siteRelativePath;
this.notFoundHint = notFoundHint;
this.contentExists = contentExists;
}

public Content getContent()
{
return content;
}

public Content getContentOrElseThrow()
{
if ( content != null )
{
return content;
}
else if ( contentExists )
{
throw WebException.forbidden( String.format( "You don't have permission to access [%s]", notFoundHint ) );
}
else
{
throw WebException.notFound( String.format( "Page [%s] not found", notFoundHint ) );
}

}

public Site getNearestSite()
{
return nearestSite;
}

public Site getNearestSiteOrElseThrow()
{
if ( nearestSite != null )
{
return nearestSite;
}
else
{
throw WebException.notFound( String.format( "Site for [%s] not found", notFoundHint ) );
}
}

public String getSiteRelativePath()
{
return siteRelativePath;
}
}
Loading

0 comments on commit ac888c6

Please sign in to comment.