Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Page) Working/Live Images loading inconsistenly Refs: 31362 #31408

Merged
merged 12 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@
import com.dotmarketing.util.WebKeys;
import com.google.common.annotations.VisibleForTesting;
import com.liferay.portal.model.User;
import javax.servlet.http.HttpSession;
import org.apache.velocity.exception.ResourceNotFoundException;

import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.http.HttpSession;
import org.apache.velocity.exception.ResourceNotFoundException;

public class VelocityServlet extends HttpServlet {

Expand Down Expand Up @@ -70,24 +69,37 @@ public static PageMode processPageMode (final User user, final HttpServletReques
}

if (LoginMode.UNKNOWN == loginMode) {

return user.isFrontendUser()
? PageMode.setPageMode(request, PageMode.LIVE)
: useNavigateMode(request, loginMode)
? PageMode.setPageMode(request, PageMode.NAVIGATE_EDIT_MODE)
: PageMode.setPageMode(request, PageMode.LIVE);
return determinePageMode(request, user, LoginMode.UNKNOWN);
}

if ( LoginMode.FE == loginMode) {
return PageMode.setPageMode(request, PageMode.LIVE);
return PageMode.setPageMode(request, PageMode.LIVE, false);
}

return useNavigateMode(request, loginMode) ?
PageMode.setPageMode(request, PageMode.NAVIGATE_EDIT_MODE) : PageMode.setPageMode(request, PageMode.PREVIEW_MODE);
PageMode.setPageMode(request, PageMode.NAVIGATE_EDIT_MODE, false) : PageMode.setPageMode(request, PageMode.PREVIEW_MODE, false);
}

private static boolean useNavigateMode(final HttpServletRequest request, LoginMode loginMode) {
/**
* This method will determine the page mode based on the user and the login mode
* @param request HttpServletRequest
* @param user User
* @param loginMode LoginMode
* @return PageMode
*/
private static PageMode determinePageMode(HttpServletRequest request, User user, LoginMode loginMode) {
if (user.isFrontendUser()) {
return PageMode.setPageMode(request, PageMode.LIVE, false);
}

if (useNavigateMode(request, loginMode)) {
return PageMode.setPageMode(request, PageMode.NAVIGATE_EDIT_MODE,false);
}

return PageMode.setPageMode(request, PageMode.LIVE, false);
}

private static boolean useNavigateMode(final HttpServletRequest request, LoginMode loginMode) {

if (LoginMode.FE == loginMode) {
return false;
Expand Down Expand Up @@ -161,7 +173,9 @@ protected final void service(HttpServletRequest req, HttpServletResponse respons
request,
response
);

Logger.debug(this, "VelocityServlet_service pageHtml: " + pageHtml);

response.getOutputStream().write(pageHtml.getBytes());
} catch (ResourceNotFoundException rnfe) {
Logger.warnAndDebug(this.getClass(), "ResourceNotFoundException" + rnfe.toString(), rnfe);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
if(req.getSession().getAttribute("tm_date")!=null && urlUtil.amISomething(uri,(Host)req.getSession().getAttribute("tm_host")
,Long.parseLong((String)req.getSession().getAttribute("tm_lang")))) {
com.liferay.portal.model.User user = null;
PageMode.setPageMode(req, PageMode.PREVIEW_MODE);
PageMode.setPageMode(req, PageMode.PREVIEW_MODE,false);
try {
user = com.liferay.portal.util.PortalUtil.getUser((HttpServletRequest) request);
if(!APILocator.getLayoutAPI().doesUserHaveAccessToPortlet("time-machine", user)){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public PageView getPageRendered(

final PageMode mode = context.getPageMode();

PageMode.setPageMode(request, mode);
PageMode.setPageMode(request, mode, false);

final Host host = this.hostWebAPI.getCurrentHost(request, context.getUser());
final HTMLPageUrl htmlPageUrl = context.getPage() != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,9 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws Servl

} else {



// Set the expiration time
if (!mode.isAdmin) {
// Set the expiration time
// Sometimes when The PageMode flag is LIVE, and we are serving a live view of the page from within the admin tool we might end up caching images that we shouldn't
if (!isDotAdminRequest(req) && !mode.isAdmin) {

int _daysCache = 365;
GregorianCalendar expiration = new GregorianCalendar();
Expand Down Expand Up @@ -685,6 +684,16 @@ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws Servl

}

/**
* Test the request to see if it is a dotAdmin request
* @param request the request to test
* @return true if the request is a dotAdmin request
*/
public static boolean isDotAdminRequest(HttpServletRequest request) {
final String referer = request.getHeader("referer");
return referer != null && referer.contains("/dotAdmin");
}

private Contentlet getContentletByIdentifier(final PageMode pageMode,
final String identifier, final long languageId, final User user)
throws DotDataException, DotSecurityException {
Expand Down
49 changes: 17 additions & 32 deletions dotCMS/src/main/java/com/dotmarketing/util/PageMode.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,51 +119,36 @@ public static PageMode setPageMode(final HttpServletRequest request, boolean con
* @param mode
* @return
*/
public static PageMode setPageMode(final HttpServletRequest request, final PageMode mode) {
public static PageMode setPageMode(final HttpServletRequest request, final PageMode mode){
return setPageMode(request, mode, true);
}

/**
* Page mode can only be set for back end users, not for front end users (even logged in Front end users)
* We should avoid setting the mode in the session.
* Session should be used to keep immutable data. like the user of company. But not state data that might dictate the behavior of the UI.
* I'm introducing this method to avoid setting the mode in the session.
* Many times passing stuff down the request is enough.
* @param request HttpServletRequest
* @param mode PageMode to set
* @param setSession if true, the mode will be set in the session
* @return
*/
public static PageMode setPageMode(final HttpServletRequest request, final PageMode mode, final boolean setSession) {
if (DEFAULT_PAGE_MODE != mode) {
final User user = PortalUtil.getUser(request);
if (user == null || !user.isBackendUser()) {
return DEFAULT_PAGE_MODE;
}
}

if(request.getSession(false)!=null) {
//here we're saying that... we want to set page mode in the request and not override the session actual value
if( setSession && null != request.getSession(false)) {
request.getSession().setAttribute(WebKeys.PAGE_MODE_SESSION, mode);
}
request.setAttribute(WebKeys.PAGE_MODE_PARAMETER, mode);
return mode;
}

private static boolean isPageModeSet(final HttpSession ses) {
return (ses != null && ses.getAttribute(com.dotmarketing.util.WebKeys.PAGE_MODE_SESSION) != null);
}

private static PageMode getCurrentPageMode(final HttpSession ses) {
PageMode sessionPageMode = ses==null ? DEFAULT_PAGE_MODE : (PageMode) ses.getAttribute(WebKeys.PAGE_MODE_SESSION);

if (isNavigateEditMode(ses)) {
return PageMode.NAVIGATE_EDIT_MODE;
} else {
return sessionPageMode;
}
}

private static boolean isNavigateEditMode(final HttpSession ses) {
PageMode sessionPageMode = ses==null ? DEFAULT_PAGE_MODE : (PageMode) ses.getAttribute(WebKeys.PAGE_MODE_SESSION);
HttpServletRequest request = HttpServletRequestThreadLocal.INSTANCE.getRequest();

final User user = PortalUtil.getUser(request);
if (user == null || !user.isBackendUser()) {
return false;
}


return sessionPageMode != PageMode.LIVE &&
request != null &&
request.getAttribute(WebKeys.PAGE_MODE_PARAMETER) == null ;
}

/**
* Checks if the current Page Mode belongs to Edit Mode.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
import static com.dotcms.datagen.TestDataUtils.getNewsLikeContentType;
import static com.dotmarketing.util.WebKeys.LOGIN_MODE_PARAMETER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.anyInt;
Expand Down Expand Up @@ -592,6 +594,49 @@ private void testServerPageFor(final User user, final LoginMode mode)
verifyPageServed(pageContent, outputStream);
}

/**
* Method to test: {@link VelocityServlet#service(HttpServletRequest, HttpServletResponse)}
* Given scenario: A backend user is logged in and request a page in preview mode
* Expected result: The page should be served in preview mode and the attribute should be set at request level never at session level
* @throws Exception
*/
@Test
public void Test_Page_Mode_Scope() throws Exception {

final User loginUser = mock(User.class);
when(loginUser.hasConsoleAccess()).thenReturn(true);
when(loginUser.isAnonymousUser()).thenReturn(false);
when(loginUser.isBackendUser()).thenReturn(true);
when(loginUser.isFrontendUser()).thenReturn(false);
when(loginUser.isActive()).thenReturn(true);

final HttpServletRequest mockRequest = createMockRequest("/dotAdmin/blog/index",
loginUser, LoginMode.BE, true);

final ServletOutputStream outputStream = mock(ServletOutputStream.class);
when(response.getOutputStream()).thenReturn(outputStream);

final HTMLPageAssetRenderedAPI pageAssetRenderedAPI = createHtmlPageAssetRenderedAPIMock(
loginUser, "<html>lol</html>", PageMode.PREVIEW_MODE);

final VelocityServlet velocityServlet = new VelocityServlet(WebAPILocator.getUserWebAPI(),
pageAssetRenderedAPI);
velocityServlet.service(mockRequest, response);

verifyPageServed("<html>lol</html>", outputStream);

//Page Mode can be recovered from the PageMode utility class
assertEquals("Page Mode should be returned accurately by PageMode.get", PageMode.PREVIEW_MODE, PageMode.get(mockRequest));
//The attribute can be recovered at request level consistently
assertEquals("Test the attribute only exists at request level", PageMode.PREVIEW_MODE, mockRequest.getAttribute(WebKeys.PAGE_MODE_PARAMETER));

//The attribute should not be set at session level
final HttpSession session = mockRequest.getSession(false);
assertNotNull(session);
assertNull(session.getAttribute(WebKeys.PAGE_MODE_SESSION));

}

private static void verifyPageServed(final String pageContent, final ServletOutputStream outputStream)
throws IOException {
verify(outputStream, times(1)).write(pageContent.getBytes());
Expand All @@ -600,19 +645,66 @@ private static void verifyPageServed(final String pageContent, final ServletOutp
public HttpServletRequest createMockRequest(final String referer, final User user, final LoginMode loginMode) {
return createMockRequest(referer, user, loginMode, false);
}

public HttpServletRequest createMockRequest(final String referer, final User user,
final LoginMode loginMode, final boolean disabledNavigateMode) {

// Create the mock of HttpServletRequest (VelocityRequestWrapper simulated)
VelocityRequestWrapper velocityRequest = mock(VelocityRequestWrapper.class);

// Map to store request attributes
final Map<String, Object> requestAttributes = new HashMap<>();

// Preload basic request attributes
requestAttributes.put("requestURI", "/lol");
requestAttributes.put(com.liferay.portal.util.WebKeys.USER, user);
requestAttributes.put("disabledNavigateMode", String.valueOf(disabledNavigateMode));

// Configure dynamic behavior of setAttribute and getAttribute for the request
doAnswer(invocation -> {
String key = invocation.getArgument(0);
Object value = invocation.getArgument(1);
requestAttributes.put(key, value);
return null;
}).when(velocityRequest).setAttribute(anyString(), any());

when(velocityRequest.getAttribute(anyString())).thenAnswer(invocation -> {
String key = invocation.getArgument(0);
return requestAttributes.get(key);
});

// Simulate getRequestURI with a direct response
when(velocityRequest.getRequestURI()).thenReturn("/lol");
when(velocityRequest.getAttribute(com.liferay.portal.util.WebKeys.USER)).thenReturn(user);

// Simulate getParameter directly
when(velocityRequest.getParameter("disabledNavigateMode"))
.thenReturn(String.valueOf(disabledNavigateMode));

// Map to store session attributes
final Map<String, Object> sessionAttributes = new HashMap<>();

// Create the mock for HttpSession
final HttpSession session = mock(HttpSession.class);
when(session.getAttribute(LOGIN_MODE_PARAMETER)).thenReturn(loginMode);
when(velocityRequest.getSession(Mockito.anyBoolean())).thenReturn(session);
when(velocityRequest.getSession()).thenReturn(session);

when(velocityRequest.getParameter("disabledNavigateMode")).thenReturn(String.valueOf(disabledNavigateMode));
// Preload the loginMode attribute in the session
sessionAttributes.put(LOGIN_MODE_PARAMETER, loginMode);

// Configure dynamic behavior of setAttribute and getAttribute for the session
doAnswer(invocation -> {
String key = invocation.getArgument(0);
Object value = invocation.getArgument(1);
sessionAttributes.put(key, value);
return null;
}).when(session).setAttribute(anyString(), any());

when(session.getAttribute(anyString())).thenAnswer(invocation -> {
String key = invocation.getArgument(0);
return sessionAttributes.get(key);
});

// Set the referer header if provided
if (referer != null) {
when(velocityRequest.getHeader("referer")).thenReturn(referer);
}
Expand Down
Loading
Loading