Skip to content

Commit

Permalink
Improved: ContentWrapper empty string result breaks simple FTL null
Browse files Browse the repository at this point in the history
check and default syntax (OFBIZ-10194)
  • Loading branch information
Adrian-Wolf authored and mbrohl committed Jan 23, 2024
1 parent d17d06f commit a41f054
Show file tree
Hide file tree
Showing 7 changed files with 375 additions and 309 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,99 @@
*******************************************************************************/
package org.apache.ofbiz.content.content;

import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.StringUtil;
import org.apache.ofbiz.base.util.UtilCodec;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.entity.condition.EntityCondition;
import org.apache.ofbiz.entity.model.ModelEntity;
import org.apache.ofbiz.entity.model.ModelUtil;
import org.apache.ofbiz.entity.util.EntityQuery;
import org.apache.ofbiz.entity.util.EntityUtilProperties;

/**
* ContentWrapper Interface
*/

public interface ContentWrapper {

String MDOULE = ContentWrapper.class.getName();

StringUtil.StringWrapper get(String contentTypeId, String encoderType);

/**
* Get the configured default for content mimeTypeId.
* @param delegator
* @return
*/
static String getDefaultMimeTypeId(Delegator delegator) {
return EntityUtilProperties.getPropertyValue("content", "defaultMimeType", "text/html; charset=utf-8", delegator);
}

/**
* Check modelObject for existance of a field named like given contentTypeId and
* return its value as String.
* @param modelObject
* @param contentTypeId
* @return
*/
static String getCandidateFieldValue(GenericValue modelObject, String contentTypeId) {
if (modelObject != null) {
String candidateFieldName = ModelUtil.dbNameToVarName(contentTypeId);
if (modelObject.getModelEntity().isField(candidateFieldName)) {
return modelObject.getString(candidateFieldName);
}
}
return null;
}

/**
* Check if modelEntityName is an existing entity and has a field named like
* given contentTypeId and get the unique modelObject entry by modelObjectPk and
* return the candidate field value as String.
* @param delegator
* @param modelEntityName
* @param modelObjectPk
* @param contentTypeId
* @param useCache
* @return
* @throws GenericEntityException
*/
static String getCandidateFieldValue(Delegator delegator, String modelEntityName, EntityCondition modelObjectPk,
String contentTypeId, boolean useCache) throws GenericEntityException {

ModelEntity modelEntity = delegator.getModelEntity(modelEntityName);
if (modelEntity != null) {
String candidateFieldName = ModelUtil.dbNameToVarName(contentTypeId);

if (modelEntity.isField(candidateFieldName)) {
GenericValue modelObject = EntityQuery.use(delegator).from(modelEntityName).where(modelObjectPk).cache(useCache).queryOne();
if (modelObject != null) {
return modelObject.getString(candidateFieldName);
}
}
}
return null;
}

/**
* Encode given content string via given encoderType.
* @param value
* @param encoderType
* @return
*/
static String encodeContentValue(String value, String encoderType) {
if (UtilValidate.isNotEmpty(value)) {
UtilCodec.SimpleEncoder encoder = UtilCodec.getEncoder(encoderType);
if (encoder != null) {
value = encoder.sanitize(value, null);
} else {
Debug.logWarning("Unknown encoderType %s for encoding content value!", MDOULE, encoderType);
}
}
return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.GeneralException;
import org.apache.ofbiz.base.util.StringUtil;
import org.apache.ofbiz.base.util.UtilCodec;
import org.apache.ofbiz.base.util.UtilHttp;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.base.util.cache.UtilCache;
Expand All @@ -51,8 +50,8 @@ public class OrderContentWrapper implements ContentWrapper {
private static final String MODULE = OrderContentWrapper.class.getName();
private static final String SEPARATOR = "::"; // cache key separator

private static final UtilCache<String, String> ORDER_CONTENT_CACHE = UtilCache.createUtilCache("order.content", true);
// use soft reference to free up memory if needed
private static final UtilCache<String, String> ORDER_CONTENT_CACHE = UtilCache.createUtilCache(
"order.content.rendered", true); // use soft reference to free up memory if needed

public static OrderContentWrapper makeOrderContentWrapper(GenericValue order, HttpServletRequest request) {
return new OrderContentWrapper(order, request);
Expand All @@ -74,8 +73,7 @@ public OrderContentWrapper(GenericValue order, HttpServletRequest request) {
this.dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
this.order = order;
this.locale = UtilHttp.getLocale(request);
this.mimeTypeId = EntityUtilProperties.getPropertyValue("content", "defaultMimeType", "text/html; charset=utf-8",
(Delegator) request.getAttribute("delegator"));
this.mimeTypeId = ContentWrapper.getDefaultMimeTypeId((Delegator) request.getAttribute("delegator"));
}

@Override
Expand All @@ -86,7 +84,7 @@ public StringUtil.StringWrapper get(String orderContentTypeId, String encoderTyp

public static String getOrderContentAsText(GenericValue order, String orderContentTypeId, HttpServletRequest request, String encoderType) {
LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
String mimeTypeId = EntityUtilProperties.getPropertyValue("content", "defaultMimeType", "text/html; charset=utf-8", order.getDelegator());
String mimeTypeId = ContentWrapper.getDefaultMimeTypeId(order.getDelegator());
return getOrderContentAsText(order, orderContentTypeId, UtilHttp.getLocale(request), mimeTypeId, order.getDelegator(), dispatcher,
encoderType);
}
Expand All @@ -98,32 +96,40 @@ public static String getOrderContentAsText(GenericValue order, String orderConte

public static String getOrderContentAsText(GenericValue order, String orderContentTypeId, Locale locale, String mimeTypeId, Delegator delegator,
LocalDispatcher dispatcher, String encoderType) {
if (order == null) {
return null;
}
String orderItemSeqId = ("OrderItem".equals(order.getEntityName()) ? order.getString("orderItemSeqId")
: "_NA_");

/* Look for a previously cached entry (may also be an entry with null value if
* there was no content to retrieve)
*/
/* caching: there is one cache created, "order.content" Each order's content is cached with a key of
* contentTypeId::locale::mimeType::orderId::orderItemSeqId, or whatever the SEPARATOR is defined above to be.
*/
UtilCodec.SimpleEncoder encoder = UtilCodec.getEncoder(encoderType);

String orderItemSeqId = ("OrderItem".equals(order.getEntityName()) ? order.getString("orderItemSeqId") : "_NA_");
String cacheKey = orderContentTypeId + SEPARATOR + locale + SEPARATOR + mimeTypeId + SEPARATOR + order.get(
"orderId") + SEPARATOR + orderItemSeqId + SEPARATOR + encoderType + SEPARATOR + delegator;
String cachedValue = ORDER_CONTENT_CACHE.get(cacheKey);
if (cachedValue != null || ORDER_CONTENT_CACHE.containsKey(cacheKey)) {
return cachedValue;
}

// Get content of given contentTypeId
String outString = null;

String cacheKey = orderContentTypeId + SEPARATOR + locale + SEPARATOR + mimeTypeId + SEPARATOR + order.get("orderId") + SEPARATOR
+ orderItemSeqId + SEPARATOR + encoderType + SEPARATOR + delegator;
try {
String cachedValue = ORDER_CONTENT_CACHE.get(cacheKey);
if (cachedValue != null) {
return cachedValue;
}

Writer outWriter = new StringWriter();
getOrderContentAsText(null, null, order, orderContentTypeId, locale, mimeTypeId, delegator, dispatcher, outWriter, false);
String outString = outWriter.toString();
outString = encoder.sanitize(outString, null);
getOrderContentAsText(null, orderItemSeqId, order, orderContentTypeId, locale, mimeTypeId, delegator,
dispatcher, outWriter, false);
// Encode found content via given encoderType
outString = ContentWrapper.encodeContentValue(outWriter.toString(), encoderType);
ORDER_CONTENT_CACHE.put(cacheKey, outString);
return outString;

} catch (GeneralException | IOException e) {
Debug.logError(e, "Error rendering OrderContent, inserting empty String", MODULE);
return "";
Debug.logError(e, "Error rendering OrderContent", MODULE);
}
return outString;
}

public static void getOrderContentAsText(String orderId, String orderItemSeqId, GenericValue order, String orderContentTypeId, Locale locale,
Expand Down Expand Up @@ -165,4 +171,3 @@ public static void getOrderContentAsText(String orderId, String orderItemSeqId,
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,16 @@
import org.apache.ofbiz.base.util.GeneralException;
import org.apache.ofbiz.base.util.GeneralRuntimeException;
import org.apache.ofbiz.base.util.StringUtil;
import org.apache.ofbiz.base.util.UtilCodec;
import org.apache.ofbiz.base.util.UtilHttp;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.base.util.cache.UtilCache;
import org.apache.ofbiz.content.content.ContentWorker;
import org.apache.ofbiz.content.content.ContentWrapper;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.entity.model.ModelEntity;
import org.apache.ofbiz.entity.model.ModelUtil;
import org.apache.ofbiz.entity.condition.EntityCondition;
import org.apache.ofbiz.entity.util.EntityQuery;
import org.apache.ofbiz.entity.util.EntityUtil;
import org.apache.ofbiz.entity.util.EntityUtilProperties;
import org.apache.ofbiz.service.LocalDispatcher;

/**
Expand Down Expand Up @@ -75,8 +72,7 @@ public PartyContentWrapper(GenericValue party, HttpServletRequest request) {
this.dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
this.party = party;
this.locale = UtilHttp.getLocale(request);
this.mimeTypeId = EntityUtilProperties.getPropertyValue("content", "defaultMimeType", "text/html; charset=utf-8",
(Delegator) request.getAttribute("delegator"));
this.mimeTypeId = ContentWrapper.getDefaultMimeTypeId(party.getDelegator());
}

/**
Expand Down Expand Up @@ -147,7 +143,7 @@ public String getContent(String contentId, String encoderType) {
// static methods
public static String getPartyContentAsText(GenericValue party, String partyContentId, HttpServletRequest request, String encoderType) {
LocalDispatcher dispatcher = (LocalDispatcher) request.getAttribute("dispatcher");
String mimeTypeId = EntityUtilProperties.getPropertyValue("content", "defaultMimeType", "text/html; charset=utf-8", party.getDelegator());
String mimeTypeId = ContentWrapper.getDefaultMimeTypeId(party.getDelegator());
return getPartyContentAsText(party, partyContentId, null, UtilHttp.getLocale(request), mimeTypeId, party.getDelegator(), dispatcher,
true, encoderType);
}
Expand All @@ -168,44 +164,47 @@ public static String getPartyContentAsText(GenericValue party, String contentId,
return null;
}

UtilCodec.SimpleEncoder encoder = UtilCodec.getEncoder(encoderType);
String candidateFieldName = ModelUtil.dbNameToVarName(partyContentTypeId);
String cacheKey;
if (contentId != null) {
cacheKey = contentId + CACHE_KEY_SEPARATOR + locale + CACHE_KEY_SEPARATOR + mimeTypeId
+ CACHE_KEY_SEPARATOR + party.get("partyId");
} else {
cacheKey = partyContentTypeId + CACHE_KEY_SEPARATOR + locale + CACHE_KEY_SEPARATOR + mimeTypeId
+ CACHE_KEY_SEPARATOR + party.get("partyId");
}
String cacheKey = null;
if (useCache) {
if (contentId != null) {
cacheKey = contentId + CACHE_KEY_SEPARATOR + locale + CACHE_KEY_SEPARATOR + mimeTypeId
+ CACHE_KEY_SEPARATOR + party.get("partyId");
} else {
cacheKey = partyContentTypeId + CACHE_KEY_SEPARATOR + locale + CACHE_KEY_SEPARATOR + mimeTypeId
+ CACHE_KEY_SEPARATOR + party.get("partyId");
}

try {
if (useCache) {
String cachedValue = PARTY_CONTENT_CACHE.get(cacheKey);
if (cachedValue != null) {
return cachedValue;
}
String cachedValue = PARTY_CONTENT_CACHE.get(cacheKey);
if (cachedValue != null || PARTY_CONTENT_CACHE.containsKey(cacheKey)) {
return cachedValue;
}
}

// Get content of given contentTypeId
String outString = null;
try {
Writer outWriter = new StringWriter();
getPartyContentAsText(contentId, party.getString("partyId"), party, partyContentTypeId, locale, mimeTypeId, delegator, dispatcher,
outWriter, false);

String outString = outWriter.toString();
if (UtilValidate.isEmpty(outString)) {
outString = party.getModelEntity().isField(candidateFieldName) ? party.getString(candidateFieldName) : "";
outString = outString == null ? "" : outString;
}
outString = encoder.sanitize(outString, null);
if (PARTY_CONTENT_CACHE != null) {
PARTY_CONTENT_CACHE.put(cacheKey, outString);
}
return outString;
getPartyContentAsText(contentId, party.getString("partyId"), party, partyContentTypeId, locale, mimeTypeId,
delegator, dispatcher, outWriter, false);
outString = outWriter.toString();
} catch (GeneralException | IOException e) {
Debug.logError(e, "Error rendering PartyContent, inserting empty String", MODULE);
String candidateOut = party.getModelEntity().isField(candidateFieldName) ? party.getString(candidateFieldName) : "";
return candidateOut == null ? "" : encoder.sanitize(candidateOut, null);
Debug.logError(e, "Error rendering PartyContent", MODULE);
useCache = false;
}

/* If we did not found any content (or got an error), get the content of a
* candidateFieldName matching the given contentTypeId
*/
if (UtilValidate.isEmpty(outString)) {
outString = ContentWrapper.getCandidateFieldValue(party, partyContentTypeId);
}
// Encode found content via given encoderType
outString = ContentWrapper.encodeContentValue(outString, encoderType);

if (useCache) {
PARTY_CONTENT_CACHE.put(cacheKey, outString);
}
return outString;
}

public static void getPartyContentAsText(String contentId, String partyId, GenericValue party, String partyContentTypeId, Locale locale,
Expand All @@ -223,15 +222,14 @@ public static void getPartyContentAsText(String contentId, String partyId, Gener
if (delegator == null && party != null) {
delegator = party.getDelegator();
}

if (UtilValidate.isEmpty(mimeTypeId)) {
mimeTypeId = EntityUtilProperties.getPropertyValue("content", "defaultMimeType", "text/html; charset=utf-8", delegator);
}

if (delegator == null) {
throw new GeneralRuntimeException("Unable to find a delegator to use!");
}

if (UtilValidate.isEmpty(mimeTypeId)) {
mimeTypeId = ContentWrapper.getDefaultMimeTypeId(delegator);
}

// Honor party content over Party entity fields.
GenericValue partyContent;
if (contentId != null) {
Expand All @@ -240,46 +238,26 @@ public static void getPartyContentAsText(String contentId, String partyId, Gener
partyContent = getFirstPartyContentByType(partyId, party, partyContentTypeId, delegator);
}
if (partyContent != null) {
// when rendering the product content, always include the Product and ProductContent records that this comes from
/* when rendering the party content, always include the Party and PartyContent
* records that this comes from
*/
Map<String, Object> inContext = new HashMap<>();
inContext.put("party", party);
inContext.put("partyContent", partyContent);
ContentWorker.renderContentAsText(dispatcher, partyContent.getString("contentId"), outWriter, inContext, locale, mimeTypeId,
null, null, cache);
return;
}

if (partyContentTypeId != null) {
String candidateFieldName = ModelUtil.dbNameToVarName(partyContentTypeId);

// check person and group entity fields, if no content was found
} else if (partyContentTypeId != null) {
// first check for a person field
ModelEntity partyPersonModel = delegator.getModelEntity("PartyAndPerson");
if (partyPersonModel != null && partyPersonModel.isField(candidateFieldName)) {
if (party == null) {
party = EntityQuery.use(delegator).from("PartyAndPerson").where("partyId", partyId).cache().queryOne();
}
if (party != null) {
String candidateValue = party.getString(candidateFieldName);
if (UtilValidate.isNotEmpty(candidateValue)) {
outWriter.write(candidateValue);
return;
}
}
String candidateValue = ContentWrapper.getCandidateFieldValue(delegator, "PartyAndPerson", EntityCondition
.makeCondition("partyId", partyId), partyContentTypeId, cache);
if (UtilValidate.isEmpty(candidateValue)) {
// next check for group field
candidateValue = ContentWrapper.getCandidateFieldValue(delegator, "PartyAndGroup", EntityCondition
.makeCondition("partyId", partyId), partyContentTypeId, cache);
}

// next check for group field
ModelEntity partyGroupModel = delegator.getModelEntity("PartyAndGroup");
if (partyGroupModel != null && partyGroupModel.isField(candidateFieldName)) {
if (party == null) {
party = EntityQuery.use(delegator).from("PartyAndGroup").where("partyId", partyId).cache().queryOne();
}
if (party != null) {
String candidateValue = party.getString(candidateFieldName);
if (UtilValidate.isNotEmpty(candidateValue)) {
outWriter.write(candidateValue);
return;
}
}
if (UtilValidate.isNotEmpty(candidateValue)) {
outWriter.write(candidateValue);
}
}
}
Expand Down
Loading

0 comments on commit a41f054

Please sign in to comment.