| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| *******************************************************************************/ |
| package org.ofbiz.product.category; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import javax.servlet.ServletRequest; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpSession; |
| |
| import javolution.util.FastList; |
| |
| import org.ofbiz.base.util.Debug; |
| import org.ofbiz.base.util.UtilDateTime; |
| import org.ofbiz.base.util.UtilFormatOut; |
| import org.ofbiz.base.util.UtilGenerics; |
| import org.ofbiz.base.util.UtilHttp; |
| import org.ofbiz.base.util.UtilMisc; |
| import org.ofbiz.base.util.UtilProperties; |
| import org.ofbiz.base.util.UtilValidate; |
| import org.ofbiz.entity.Delegator; |
| import org.ofbiz.entity.GenericEntityException; |
| import org.ofbiz.entity.GenericValue; |
| import org.ofbiz.entity.condition.EntityCondition; |
| import org.ofbiz.entity.condition.EntityOperator; |
| import org.ofbiz.entity.util.EntityUtil; |
| import org.ofbiz.product.product.ProductWorker; |
| import org.ofbiz.service.DispatchContext; |
| import org.ofbiz.service.ServiceUtil; |
| |
| /** |
| * CategoryWorker - Worker class to reduce code in JSPs. |
| */ |
| public class CategoryWorker { |
| |
| public static final String module = CategoryWorker.class.getName(); |
| |
| private CategoryWorker () {} |
| |
| public static String getCatalogTopCategory(ServletRequest request, String defaultTopCategory) { |
| HttpServletRequest httpRequest = (HttpServletRequest) request; |
| Map<String, Object> requestParameters = UtilHttp.getParameterMap(httpRequest); |
| String topCatName = null; |
| boolean fromSession = false; |
| |
| // first see if a new category was specified as a parameter |
| topCatName = (String) requestParameters.get("CATALOG_TOP_CATEGORY"); |
| // if no parameter, try from session |
| if (topCatName == null) { |
| topCatName = (String) httpRequest.getSession().getAttribute("CATALOG_TOP_CATEGORY"); |
| if (topCatName != null) |
| fromSession = true; |
| } |
| // if nothing else, just use a default top category name |
| if (topCatName == null) |
| topCatName = defaultTopCategory; |
| if (topCatName == null) |
| topCatName = "CATALOG1"; |
| |
| if (!fromSession) { |
| if (Debug.infoOn()) Debug.logInfo("[CategoryWorker.getCatalogTopCategory] Setting new top category: " + topCatName, module); |
| httpRequest.getSession().setAttribute("CATALOG_TOP_CATEGORY", topCatName); |
| } |
| return topCatName; |
| } |
| |
| public static void getCategoriesWithNoParent(ServletRequest request, String attributeName) { |
| Delegator delegator = (Delegator) request.getAttribute("delegator"); |
| Collection<GenericValue> results = FastList.newInstance(); |
| |
| try { |
| Collection<GenericValue> allCategories = delegator.findList("ProductCategory", null, null, null, null, false); |
| |
| for (GenericValue curCat: allCategories) { |
| Collection<GenericValue> parentCats = curCat.getRelated("CurrentProductCategoryRollup", null, null, true); |
| |
| if (parentCats.isEmpty()) results.add(curCat); |
| } |
| } catch (GenericEntityException e) { |
| Debug.logWarning(e, module); |
| } |
| request.setAttribute(attributeName, results); |
| } |
| |
| public static void getRelatedCategories(ServletRequest request, String attributeName, boolean limitView) { |
| Map<String, Object> requestParameters = UtilHttp.getParameterMap((HttpServletRequest) request); |
| String requestId = null; |
| |
| requestId = UtilFormatOut.checkNull((String)requestParameters.get("catalog_id"), (String)requestParameters.get("CATALOG_ID"), |
| (String)requestParameters.get("category_id"), (String)requestParameters.get("CATEGORY_ID")); |
| |
| if (requestId.equals("")) |
| return; |
| if (Debug.infoOn()) Debug.logInfo("[CategoryWorker.getRelatedCategories] RequestID: " + requestId, module); |
| getRelatedCategories(request, attributeName, requestId, limitView); |
| } |
| |
| public static void getRelatedCategories(ServletRequest request, String attributeName, String parentId, boolean limitView) { |
| getRelatedCategories(request, attributeName, parentId, limitView, false); |
| } |
| |
| public static void getRelatedCategories(ServletRequest request, String attributeName, String parentId, boolean limitView, boolean excludeEmpty) { |
| List<GenericValue> categories = getRelatedCategoriesRet(request, attributeName, parentId, limitView, excludeEmpty); |
| |
| if (!categories.isEmpty()) request.setAttribute(attributeName, categories); |
| } |
| |
| public static List<GenericValue> getRelatedCategoriesRet(ServletRequest request, String attributeName, String parentId, boolean limitView) { |
| return getRelatedCategoriesRet(request, attributeName, parentId, limitView, false); |
| } |
| |
| public static List<GenericValue> getRelatedCategoriesRet(ServletRequest request, String attributeName, String parentId, boolean limitView, boolean excludeEmpty) { |
| return getRelatedCategoriesRet(request, attributeName, parentId, limitView, excludeEmpty, false); |
| } |
| |
| public static List<GenericValue> getRelatedCategoriesRet(ServletRequest request, String attributeName, String parentId, boolean limitView, boolean excludeEmpty, boolean recursive) { |
| Delegator delegator = (Delegator) request.getAttribute("delegator"); |
| |
| return getRelatedCategoriesRet(delegator, attributeName, parentId, limitView, excludeEmpty, false); |
| } |
| |
| public static List<GenericValue> getRelatedCategoriesRet(Delegator delegator, String attributeName, String parentId, boolean limitView, boolean excludeEmpty, boolean recursive) { |
| List<GenericValue> categories = FastList.newInstance(); |
| |
| if (Debug.verboseOn()) Debug.logVerbose("[CategoryWorker.getRelatedCategories] ParentID: " + parentId, module); |
| |
| List<GenericValue> rollups = null; |
| |
| try { |
| rollups = delegator.findByAnd("ProductCategoryRollup", |
| UtilMisc.toMap("parentProductCategoryId", parentId), |
| UtilMisc.toList("sequenceNum"), true); |
| if (limitView) { |
| rollups = EntityUtil.filterByDate(rollups, true); |
| } |
| } catch (GenericEntityException e) { |
| Debug.logWarning(e.getMessage(), module); |
| } |
| if (rollups != null) { |
| // Debug.logInfo("Rollup size: " + rollups.size(), module); |
| for (GenericValue parent: rollups) { |
| // Debug.logInfo("Adding child of: " + parent.getString("parentProductCategoryId"), module); |
| GenericValue cv = null; |
| |
| try { |
| cv = parent.getRelatedOne("CurrentProductCategory", true); |
| } catch (GenericEntityException e) { |
| Debug.logWarning(e.getMessage(), module); |
| } |
| if (cv != null) { |
| if (excludeEmpty) { |
| if (!isCategoryEmpty(cv)) { |
| //Debug.logInfo("Child : " + cv.getString("productCategoryId") + " is not empty.", module); |
| categories.add(cv); |
| if (recursive) { |
| categories.addAll(getRelatedCategoriesRet(delegator, attributeName, cv.getString("productCategoryId"), limitView, excludeEmpty, recursive)); |
| } |
| } |
| } else { |
| categories.add(cv); |
| if (recursive) { |
| categories.addAll(getRelatedCategoriesRet(delegator, attributeName, cv.getString("productCategoryId"), limitView, excludeEmpty, recursive)); |
| } |
| } |
| } |
| } |
| } |
| return categories; |
| } |
| |
| public static boolean isCategoryEmpty(GenericValue category) { |
| boolean empty = true; |
| long members = categoryMemberCount(category); |
| //Debug.logInfo("Category : " + category.get("productCategoryId") + " has " + members + " members", module); |
| if (members > 0) { |
| empty = false; |
| } |
| |
| if (empty) { |
| long rollups = categoryRollupCount(category); |
| //Debug.logInfo("Category : " + category.get("productCategoryId") + " has " + rollups + " rollups", module); |
| if (rollups > 0) { |
| empty = false; |
| } |
| } |
| |
| return empty; |
| } |
| |
| public static long categoryMemberCount(GenericValue category) { |
| if (category == null) return 0; |
| Delegator delegator = category.getDelegator(); |
| long count = 0; |
| try { |
| count = delegator.findCountByCondition("ProductCategoryMember", buildCountCondition("productCategoryId", category.getString("productCategoryId")), null, null); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } |
| return count; |
| } |
| |
| public static long categoryRollupCount(GenericValue category) { |
| if (category == null) return 0; |
| Delegator delegator = category.getDelegator(); |
| long count = 0; |
| try { |
| count = delegator.findCountByCondition("ProductCategoryRollup", buildCountCondition("parentProductCategoryId", category.getString("productCategoryId")), null, null); |
| } catch (GenericEntityException e) { |
| Debug.logError(e, module); |
| } |
| return count; |
| } |
| |
| private static EntityCondition buildCountCondition(String fieldName, String fieldValue) { |
| List<EntityCondition> orCondList = FastList.newInstance(); |
| orCondList.add(EntityCondition.makeCondition("thruDate", EntityOperator.GREATER_THAN, UtilDateTime.nowTimestamp())); |
| orCondList.add(EntityCondition.makeCondition("thruDate", EntityOperator.EQUALS, null)); |
| EntityCondition orCond = EntityCondition.makeCondition(orCondList, EntityOperator.OR); |
| |
| List<EntityCondition> andCondList = FastList.newInstance(); |
| andCondList.add(EntityCondition.makeCondition("fromDate", EntityOperator.LESS_THAN, UtilDateTime.nowTimestamp())); |
| andCondList.add(EntityCondition.makeCondition(fieldName, EntityOperator.EQUALS, fieldValue)); |
| andCondList.add(orCond); |
| EntityCondition andCond = EntityCondition.makeCondition(andCondList, EntityOperator.AND); |
| |
| return andCond; |
| } |
| |
| public static void setTrail(ServletRequest request, String currentCategory) { |
| Map<String, Object> requestParameters = UtilHttp.getParameterMap((HttpServletRequest) request); |
| String previousCategory = (String) requestParameters.get("pcategory"); |
| setTrail(request, currentCategory, previousCategory); |
| } |
| |
| public static void setTrail(ServletRequest request, String currentCategory, String previousCategory) { |
| if (Debug.verboseOn()) Debug.logVerbose("[CategoryWorker.setTrail] Start: previousCategory=" + previousCategory + " currentCategory=" + currentCategory, module); |
| |
| // if there is no current category, just return and do nothing to that the last settings will stay |
| if (UtilValidate.isEmpty(currentCategory)) { |
| return; |
| } |
| |
| // always get the last crumb list |
| List<String> crumb = getTrail(request); |
| crumb = adjustTrail(crumb, currentCategory, previousCategory); |
| setTrail(request, crumb); |
| } |
| |
| public static List<String> adjustTrail(List<String> origTrail, String currentCategoryId, String previousCategoryId) { |
| List<String> trail = FastList.newInstance(); |
| if (origTrail != null) { |
| trail.addAll(origTrail); |
| } |
| |
| // if no previous category was specified, check to see if currentCategory is in the list |
| if (UtilValidate.isEmpty(previousCategoryId)) { |
| if (trail.contains(currentCategoryId)) { |
| // if cur category is in crumb, remove everything after it and return |
| int cindex = trail.lastIndexOf(currentCategoryId); |
| |
| if (cindex < (trail.size() - 1)) { |
| for (int i = trail.size() - 1; i > cindex; i--) { |
| trail.remove(i); |
| //FIXME can be removed ? |
| // String deadCat = trail.remove(i); |
| //if (Debug.infoOn()) Debug.logInfo("[CategoryWorker.setTrail] Removed after current category index: " + i + " catname: " + deadCat, module); |
| } |
| } |
| return trail; |
| } else { |
| // current category is not in the list, and no previous category was specified, go back to the beginning |
| trail.clear(); |
| trail.add("TOP"); |
| if (UtilValidate.isNotEmpty(previousCategoryId)) { |
| trail.add(previousCategoryId); |
| } |
| //if (Debug.infoOn()) Debug.logInfo("[CategoryWorker.setTrail] Starting new list, added TOP and previousCategory: " + previousCategoryId, module); |
| } |
| } |
| |
| if (!trail.contains(previousCategoryId)) { |
| // previous category was NOT in the list, ERROR, start over |
| //if (Debug.infoOn()) Debug.logInfo("[CategoryWorker.setTrail] previousCategory (" + previousCategoryId + ") was not in the crumb list, position is lost, starting over with TOP", module); |
| trail.clear(); |
| trail.add("TOP"); |
| if (UtilValidate.isNotEmpty(previousCategoryId)) { |
| trail.add(previousCategoryId); |
| } |
| } else { |
| // remove all categories after the previous category, preparing for adding the current category |
| int index = trail.indexOf(previousCategoryId); |
| if (index < (trail.size() - 1)) { |
| for (int i = trail.size() - 1; i > index; i--) { |
| trail.remove(i); |
| //FIXME can be removed ? |
| // String deadCat = trail.remove(i); |
| //if (Debug.infoOn()) Debug.logInfo("[CategoryWorker.setTrail] Removed after current category index: " + i + " catname: " + deadCat, module); |
| } |
| } |
| } |
| |
| // add the current category to the end of the list |
| trail.add(currentCategoryId); |
| if (Debug.verboseOn()) Debug.logVerbose("[CategoryWorker.setTrail] Continuing list: Added currentCategory: " + currentCategoryId, module); |
| |
| return trail; |
| } |
| |
| public static List<String> getTrail(ServletRequest request) { |
| HttpSession session = ((HttpServletRequest) request).getSession(); |
| List<String> crumb = UtilGenerics.checkList(session.getAttribute("_BREAD_CRUMB_TRAIL_")); |
| return crumb; |
| } |
| |
| public static List<String> setTrail(ServletRequest request, List<String> crumb) { |
| HttpSession session = ((HttpServletRequest) request).getSession(); |
| session.setAttribute("_BREAD_CRUMB_TRAIL_", crumb); |
| return crumb; |
| } |
| |
| public static boolean checkTrailItem(ServletRequest request, String category) { |
| List<String> crumb = getTrail(request); |
| |
| if (crumb != null && crumb.contains(category)) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| public static String lastTrailItem(ServletRequest request) { |
| List<String> crumb = getTrail(request); |
| |
| if (UtilValidate.isNotEmpty(crumb)) { |
| return crumb.get(crumb.size() - 1); |
| } else { |
| return null; |
| } |
| } |
| |
| public static boolean isProductInCategory(Delegator delegator, String productId, String productCategoryId) throws GenericEntityException { |
| if (productCategoryId == null) return false; |
| if (UtilValidate.isEmpty(productId)) return false; |
| |
| List<GenericValue> productCategoryMembers = EntityUtil.filterByDate(delegator.findByAnd("ProductCategoryMember", |
| UtilMisc.toMap("productCategoryId", productCategoryId, "productId", productId), null, true), true); |
| if (UtilValidate.isEmpty(productCategoryMembers)) { |
| //before giving up see if this is a variant product, and if so look up the virtual product and check it... |
| GenericValue product = delegator.findOne("Product", UtilMisc.toMap("productId", productId), true); |
| List<GenericValue> productAssocs = ProductWorker.getVariantVirtualAssocs(product); |
| //this does take into account that a product could be a variant of multiple products, but this shouldn't ever really happen... |
| if (productAssocs != null) { |
| for (GenericValue productAssoc: productAssocs) { |
| if (isProductInCategory(delegator, productAssoc.getString("productId"), productCategoryId)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| public static List<GenericValue> filterProductsInCategory(Delegator delegator, List<GenericValue> valueObjects, String productCategoryId) throws GenericEntityException { |
| return filterProductsInCategory(delegator, valueObjects, productCategoryId, "productId"); |
| } |
| |
| public static List<GenericValue> filterProductsInCategory(Delegator delegator, List<GenericValue> valueObjects, String productCategoryId, String productIdFieldName) throws GenericEntityException { |
| List<GenericValue> newList = FastList.newInstance(); |
| |
| if (productCategoryId == null) return newList; |
| if (valueObjects == null) return null; |
| |
| for (GenericValue curValue: valueObjects) { |
| String productId = curValue.getString(productIdFieldName); |
| if (isProductInCategory(delegator, productId, productCategoryId)) { |
| newList.add(curValue); |
| } |
| } |
| return newList; |
| } |
| |
| public static void getCategoryContentWrappers(Map<String, CategoryContentWrapper> catContentWrappers, List<GenericValue> categoryList, HttpServletRequest request) throws GenericEntityException { |
| if (catContentWrappers == null || categoryList == null) { |
| return; |
| } |
| for (GenericValue cat: categoryList) { |
| String productCategoryId = (String) cat.get("productCategoryId"); |
| |
| if (catContentWrappers.containsKey(productCategoryId)) { |
| // if this ID is already in the Map, skip it (avoids inefficiency, infinite recursion, etc.) |
| continue; |
| } |
| |
| CategoryContentWrapper catContentWrapper = new CategoryContentWrapper(cat, request); |
| catContentWrappers.put(productCategoryId, catContentWrapper); |
| List<GenericValue> subCat = getRelatedCategoriesRet(request, "subCatList", productCategoryId, true); |
| if (subCat != null) { |
| getCategoryContentWrappers(catContentWrappers, subCat, request); |
| } |
| } |
| } |
| |
| /** |
| * Returns a complete category trail - can be used for exporting proper category trees. |
| * This is mostly useful when used in combination with bread-crumbs, for building a |
| * faceted index tree, or to export a category tree for migration to another system. |
| * Will create the tree from root point to categoryId. |
| * |
| * This method is not meant to be run on every request. |
| * Its best use is to generate the trail every so often and store somewhere |
| * (a lucene/solr tree, entities, cache or so). |
| * |
| * @param dctx The DispatchContext that this service is operating in |
| * @param context Map containing the input parameters |
| * @return Map organized trail from root point to categoryId. |
| * */ |
| public static Map getCategoryTrail(DispatchContext dctx, Map context) { |
| String productCategoryId = (String) context.get("productCategoryId"); |
| Map<String, Object> results = ServiceUtil.returnSuccess(); |
| Delegator delegator = dctx.getDelegator(); |
| List<String> trailElements = FastList.newInstance(); |
| trailElements.add(productCategoryId); |
| String parentProductCategoryId = productCategoryId; |
| while (UtilValidate.isNotEmpty(parentProductCategoryId)) { |
| // find product category rollup |
| try { |
| List<EntityCondition> rolllupConds = FastList.newInstance(); |
| rolllupConds.add(EntityCondition.makeCondition("productCategoryId", parentProductCategoryId)); |
| rolllupConds.add(EntityUtil.getFilterByDateExpr()); |
| List<GenericValue> productCategoryRollups = delegator.findList("ProductCategoryRollup", |
| EntityCondition.makeCondition(rolllupConds), null, UtilMisc.toList("sequenceNum"), null, true); |
| if (UtilValidate.isNotEmpty(productCategoryRollups)) { |
| // add only categories that belong to the top category to trail |
| for (GenericValue productCategoryRollup : productCategoryRollups) { |
| String trailCategoryId = productCategoryRollup.getString("parentProductCategoryId"); |
| parentProductCategoryId = trailCategoryId; |
| if (trailElements.contains(trailCategoryId)) { |
| break; |
| } else { |
| trailElements.add(trailCategoryId); |
| } |
| } |
| } else { |
| parentProductCategoryId = null; |
| } |
| } catch (GenericEntityException e) { |
| Map<String, String> messageMap = UtilMisc.toMap("errMessage", ". Cannot generate trail from product category. "); |
| String errMsg = UtilProperties.getMessage("CommonUiLabels", "CommonDatabaseProblem", messageMap, (Locale) context.get("locale")); |
| Debug.logError(e, errMsg, module); |
| return ServiceUtil.returnError(errMsg); |
| } |
| } |
| Collections.reverse(trailElements); |
| results.put("trail", trailElements); |
| return results; |
| } |
| } |