Author: hansbak
Date: Fri Aug 28 02:27:46 2009 New Revision: 808734 URL: http://svn.apache.org/viewvc?rev=808734&view=rev Log: add list/map formats Modified: ofbiz/trunk/applications/content/src/org/ofbiz/content/content/ContentWorker.java Modified: ofbiz/trunk/applications/content/src/org/ofbiz/content/content/ContentWorker.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/content/src/org/ofbiz/content/content/ContentWorker.java?rev=808734&r1=808733&r2=808734&view=diff ============================================================================== --- ofbiz/trunk/applications/content/src/org/ofbiz/content/content/ContentWorker.java (original) +++ ofbiz/trunk/applications/content/src/org/ofbiz/content/content/ContentWorker.java Fri Aug 28 02:27:46 2009 @@ -23,8 +23,6 @@ import java.io.StringWriter; import java.io.Writer; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -62,6 +60,7 @@ import org.ofbiz.service.LocalDispatcher; import org.ofbiz.service.ModelService; import org.ofbiz.service.ServiceUtil; +import org.ofbiz.webapp.view.ApacheFopWorker; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -85,24 +84,24 @@ return getCurrentContent(delegator, trail, userLogin, ctx, nullThruDatesOnly, contentAssocPredicateId); } - public String getMimeTypeIdExt(GenericDelegator delegator, GenericValue view, Map ctx) { + public String getMimeTypeIdExt(GenericDelegator delegator, GenericValue view, Map<String, Object> ctx) { return getMimeTypeId(delegator, view, ctx); } // new rendering methods - public void renderContentAsTextExt(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Appendable out, Map templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { + public void renderContentAsTextExt(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Appendable out, Map<String, Object> templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { renderContentAsText(dispatcher, delegator, contentId, out, templateContext, locale, mimeTypeId, null, null, cache); } - public void renderSubContentAsTextExt(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Appendable out, String mapKey, Map templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { + public void renderSubContentAsTextExt(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Appendable out, String mapKey, Map<String, Object> templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { renderSubContentAsText(dispatcher, delegator, contentId, out, mapKey, templateContext, locale, mimeTypeId, cache); } - public String renderSubContentAsTextExt(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, String mapKey, Map templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { + public String renderSubContentAsTextExt(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, String mapKey, Map<String, Object> templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { return renderSubContentAsText(dispatcher, delegator, contentId, mapKey, templateContext, locale, mimeTypeId, cache); } - public String renderContentAsTextExt(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Map templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { + public String renderContentAsTextExt(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Map<String, Object> templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { return renderContentAsText(dispatcher, delegator, contentId, templateContext, locale, mimeTypeId, cache); } @@ -156,7 +155,7 @@ // check for alternate content per party if (partyId != null && roleTypeId != null) { - List alternateViews = null; + List<GenericValue> alternateViews = null; try { alternateViews = content.getRelated("ContentAssocDataResourceViewTo", UtilMisc.toMap("caContentAssocTypeId", "ALTERNATE_ROLE"), UtilMisc.toList("-caFromDate")); } catch (GenericEntityException e) { @@ -164,9 +163,9 @@ } alternateViews = EntityUtil.filterByDate(alternateViews, UtilDateTime.nowTimestamp(), "caFromDate", "caThruDate", true); - Iterator alternateViewIter = alternateViews.iterator(); + Iterator<GenericValue> alternateViewIter = alternateViews.iterator(); while (alternateViewIter.hasNext()) { - GenericValue thisView = (GenericValue) alternateViewIter.next(); + GenericValue thisView = alternateViewIter.next(); GenericValue altContentRole = EntityUtil.getFirst(EntityUtil.filterByDate(thisView.getRelatedByAnd("ContentRole", UtilMisc.toMap("partyId", partyId, "roleTypeId", roleTypeId)))); GenericValue altContent = null; if (UtilValidate.isNotEmpty(altContentRole)) { @@ -181,16 +180,18 @@ } public static void renderContentAsText(LocalDispatcher dispatcher, GenericDelegator delegator, GenericValue content, Appendable out, - Map templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { - + Map<String,Object>templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { // if the content has a service attached run the service + + Debug.log("=====================mimetypeId" + mimeTypeId); + String serviceName = content.getString("serviceName"); if (dispatcher != null && UtilValidate.isNotEmpty(serviceName)) { DispatchContext dctx = dispatcher.getDispatchContext(); ModelService service = dctx.getModelService(serviceName); if (service != null) { - Map serviceCtx = service.makeValid(templateContext, ModelService.IN_PARAM); - Map serviceRes; + Map<String,Object> serviceCtx = service.makeValid(templateContext, ModelService.IN_PARAM); + Map<String,Object> serviceRes; try { serviceRes = dispatcher.runSync(serviceName, serviceCtx); } catch (GenericServiceException e) { @@ -277,18 +278,24 @@ // This part is using an xml file as the input data and an ftl file to present it. if (UtilValidate.isNotEmpty(mimeType)) { - if (mimeType.toLowerCase().indexOf("xml") >= 0) { - StringReader sr = new StringReader(textData); - try { - NodeModel nodeModel = NodeModel.parse(new InputSource(sr)); - templateContext.put("doc", nodeModel); - } catch (SAXException e) { - throw new GeneralException(e.getMessage()); - } catch (ParserConfigurationException e2) { - throw new GeneralException(e2.getMessage()); - } - } else { - // must be text + if (mimeType.toLowerCase().indexOf("xml") >= 0) { + GenericValue dataResource = delegator.findByPrimaryKey("DataResource", UtilMisc.toMap("dataResourceId", dataResourceId)); + GenericValue templateDataResource = delegator.findByPrimaryKey("DataResource", UtilMisc.toMap("dataResourceId", templateDataResourceId)); + if ("FTL".equals(templateDataResource.getString("dataTemplateTypeId"))) { + StringReader sr = new StringReader(textData); + try { + NodeModel nodeModel = NodeModel.parse(new InputSource(sr)); + templateContext.put("doc", nodeModel) ; + } catch (SAXException e) { + throw new GeneralException(e.getMessage()); + } catch (ParserConfigurationException e2) { + throw new GeneralException(e2.getMessage()); + } + } else { + templateContext.put("docFile", DataResourceWorker.getContentFile(dataResource.getString("dataResourceTypeId"), dataResource.getString("objectInfo"), (String) templateContext.get("contextRoot")).getAbsoluteFile().toString()); + } + } else { + // must be text templateContext.put("textData", textData); } } else { @@ -301,7 +308,7 @@ } } - public static String renderContentAsText(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Map templateContext, + public static String renderContentAsText(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Map<String, Object> templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { Writer writer = new StringWriter(); renderContentAsText(dispatcher, delegator, contentId, writer, templateContext, locale, mimeTypeId, null, null, cache); @@ -309,12 +316,12 @@ } public static void renderContentAsText(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Appendable out, - Map templateContext, Locale locale, String mimeTypeId, String partyId, String roleTypeId, boolean cache) throws GeneralException, IOException { + Map<String, Object> templateContext, Locale locale, String mimeTypeId, String partyId, String roleTypeId, boolean cache) throws GeneralException, IOException { GenericValue content = ContentWorker.findContentForRendering(delegator, contentId, locale, partyId, roleTypeId, cache); ContentWorker.renderContentAsText(dispatcher, delegator, content, out, templateContext, locale, mimeTypeId, cache); } - public static String renderSubContentAsText(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, String mapKey, Map templateContext, + public static String renderSubContentAsText(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, String mapKey, Map<String, Object> templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { Writer writer = new StringWriter(); renderSubContentAsText(dispatcher, delegator, contentId, writer, mapKey, templateContext, locale, mimeTypeId, cache); @@ -322,16 +329,16 @@ } public static void renderSubContentAsText(LocalDispatcher dispatcher, GenericDelegator delegator, String contentId, Appendable out, String mapKey, - Map templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { + Map<String,Object> templateContext, Locale locale, String mimeTypeId, boolean cache) throws GeneralException, IOException { // find the sub-content with matching mapKey - List orderBy = UtilMisc.toList("-fromDate"); - List exprs = UtilMisc.toList(EntityCondition.makeCondition("contentId", EntityOperator.EQUALS, contentId)); + List<String> orderBy = UtilMisc.toList("-fromDate"); + List<EntityExpr> exprs = UtilMisc.toList(EntityCondition.makeCondition("contentId", EntityOperator.EQUALS, contentId)); if (UtilValidate.isNotEmpty(mapKey)) { exprs.add(EntityCondition.makeCondition("mapKey", EntityOperator.EQUALS, mapKey)); } - List assocs; + List<GenericValue> assocs; assocs = delegator.findList("ContentAssoc", EntityCondition.makeCondition(exprs, EntityOperator.AND), null, orderBy, null, cache); assocs = EntityUtil.filterByDate(assocs); GenericValue subContent = EntityUtil.getFirst(assocs); @@ -355,7 +362,7 @@ String localeStr = locale.toString(); boolean isTwoLetterLocale = localeStr.length() == 2; - List alternateViews = null; + List<GenericValue> alternateViews = null; try { alternateViews = view.getRelated("ContentAssocDataResourceViewTo", UtilMisc.toMap("caContentAssocTypeId", "ALTERNATE_LOCALE"), UtilMisc.toList("-caFromDate")); } catch (GenericEntityException e) { @@ -364,9 +371,9 @@ } alternateViews = EntityUtil.filterByDate(alternateViews, UtilDateTime.nowTimestamp(), "caFromDate", "caThruDate", true); - Iterator alternateViewIter = alternateViews.iterator(); + Iterator<GenericValue> alternateViewIter = alternateViews.iterator(); while (alternateViewIter.hasNext()) { - GenericValue thisView = (GenericValue) alternateViewIter.next(); + GenericValue thisView = alternateViewIter.next(); String currentLocaleString = thisView.getString("localeString"); if (UtilValidate.isEmpty(currentLocaleString)) { continue; @@ -407,7 +414,7 @@ return contentAssocDataResourceViewFrom; } - public static void traverse(GenericDelegator delegator, GenericValue content, Timestamp fromDate, Timestamp thruDate, Map whenMap, int depthIdx, Map masterNode, String contentAssocTypeId, List pickList, String direction) { + public static void traverse(GenericDelegator delegator, GenericValue content, Timestamp fromDate, Timestamp thruDate, Map<String, Object> whenMap, int depthIdx, Map<String, Object> masterNode, String contentAssocTypeId, List<GenericValue> pickList, String direction) { //String startContentAssocTypeId = null; String contentTypeId = null; @@ -418,28 +425,28 @@ } contentId = (String) content.get("contentId"); contentTypeId = (String) content.get("contentTypeId"); - List topicList = content.getRelatedByAnd("ToContentAssoc", UtilMisc.toMap("contentAssocTypeId", "TOPIC")); - List topics = FastList.newInstance(); + List<GenericValue> topicList = content.getRelatedByAnd("ToContentAssoc", UtilMisc.toMap("contentAssocTypeId", "TOPIC")); + List<String> topics = FastList.newInstance(); for (int i = 0; i < topicList.size(); i++) { GenericValue assoc = (GenericValue) topicList.get(i); - topics.add(assoc.get("contentId")); + topics.add(assoc.getString("contentId")); } - List keywordList = content.getRelatedByAnd("ToContentAssoc", UtilMisc.toMap("contentAssocTypeId", "KEYWORD")); - List keywords = FastList.newInstance(); + List<GenericValue> keywordList = content.getRelatedByAnd("ToContentAssoc", UtilMisc.toMap("contentAssocTypeId", "KEYWORD")); + List<String> keywords = FastList.newInstance(); for (int i = 0; i < keywordList.size(); i++) { GenericValue assoc = (GenericValue) keywordList.get(i); - keywords.add(assoc.get("contentId")); + keywords.add(assoc.getString("contentId")); } - List purposeValueList = content.getRelatedCache("ContentPurpose"); - List purposes = FastList.newInstance(); + List<GenericValue> purposeValueList = content.getRelatedCache("ContentPurpose"); + List<String> purposes = FastList.newInstance(); for (int i = 0; i < purposeValueList.size(); i++) { GenericValue purposeValue = (GenericValue) purposeValueList.get(i); - purposes.add(purposeValue.get("contentPurposeTypeId")); + purposes.add(purposeValue.getString("contentPurposeTypeId")); } - List contentTypeAncestry = FastList.newInstance(); + List<GenericValue> contentTypeAncestry = FastList.newInstance(); getContentTypeAncestry(delegator, contentTypeId, contentTypeAncestry); - Map context = FastMap.newInstance(); + Map<String, Object> context = FastMap.newInstance(); context.put("content", content); context.put("contentAssocTypeId", contentAssocTypeId); //context.put("related", related); @@ -449,13 +456,13 @@ context.put("typeAncestry", contentTypeAncestry); boolean isPick = checkWhen(context, (String) whenMap.get("pickWhen")); boolean isReturnBefore = checkReturnWhen(context, (String) whenMap.get("returnBeforePickWhen")); - Map thisNode = null; + Map<String, Object> thisNode = null; if (isPick || !isReturnBefore) { thisNode = FastMap.newInstance(); thisNode.put("contentId", contentId); thisNode.put("contentTypeId", contentTypeId); thisNode.put("contentAssocTypeId", contentAssocTypeId); - List kids = (List) masterNode.get("kids"); + List<Map<String, Object>> kids = (List) masterNode.get("kids"); if (kids == null) { kids = FastList.newInstance(); masterNode.put("kids", kids); @@ -469,9 +476,9 @@ boolean isReturnAfter = checkReturnWhen(context, (String) whenMap.get("returnAfterPickWhen")); if (!isReturnAfter) { - List relatedAssocs = getContentAssocsWithId(delegator, contentId, fromDate, thruDate, direction, FastList.newInstance()); - Iterator it = relatedAssocs.iterator(); - Map assocContext = FastMap.newInstance(); + List<GenericValue> relatedAssocs = getContentAssocsWithId(delegator, contentId, fromDate, thruDate, direction, FastList.newInstance()); + Iterator<GenericValue> it = relatedAssocs.iterator(); + Map<String, Object> assocContext = FastMap.newInstance(); assocContext.put("related", relatedAssocs); while (it.hasNext()) { GenericValue assocValue = (GenericValue) it.next(); @@ -504,27 +511,27 @@ } } - public static boolean traverseSubContent(Map ctx) { + public static boolean traverseSubContent(Map<String, Object> ctx) { boolean inProgress = false; - List nodeTrail = (List)ctx.get("nodeTrail"); + List<Map <String, Object>> nodeTrail = (List<Map <String, Object>>)ctx.get("nodeTrail"); ContentWorker.traceNodeTrail("11",nodeTrail); int sz = nodeTrail.size(); if (sz == 0) { return false; } - Map currentNode = (Map)nodeTrail.get(sz - 1); + Map<String, Object> currentNode = (Map<String, Object>)nodeTrail.get(sz - 1); Boolean isReturnAfter = (Boolean)currentNode.get("isReturnAfter"); if (isReturnAfter != null && isReturnAfter.booleanValue()) { return false; } - List kids = (List)currentNode.get("kids"); + List<Map <String, Object>> kids = (List<Map <String, Object>>)currentNode.get("kids"); if (UtilValidate.isNotEmpty(kids)) { int idx = 0; while (idx < kids.size()) { - currentNode = (Map)kids.get(idx); + currentNode = (Map<String, Object>)kids.get(idx); ContentWorker.traceNodeTrail("12",nodeTrail); Boolean isPick = (Boolean)currentNode.get("isPick"); @@ -552,17 +559,17 @@ if (!inProgress) { // look for next sibling while (sz > 1) { - currentNode = (Map)nodeTrail.remove(--sz); + currentNode = (Map<String, Object>)nodeTrail.remove(--sz); ContentWorker.traceNodeTrail("15",nodeTrail); - Map parentNode = (Map)nodeTrail.get(sz - 1); - kids = (List)parentNode.get("kids"); + Map<String, Object> parentNode = (Map<String, Object>) nodeTrail.get(sz - 1); + kids = (List<Map <String, Object>>)parentNode.get("kids"); if (kids == null) { continue; } int idx = kids.indexOf(currentNode); while (idx < (kids.size() - 1)) { - currentNode = (Map)kids.get(idx + 1); + currentNode = (Map<String, Object>)kids.get(idx + 1); Boolean isFollow = (Boolean)currentNode.get("isFollow"); if (isFollow == null || !isFollow.booleanValue()) { idx++; @@ -715,7 +722,7 @@ return isWhen; } - public static boolean checkReturnWhen(Map context, String whenStr) { + public static boolean checkReturnWhen(Map<String, Object> context, String whenStr) { boolean isWhen = false; //opposite default from checkWhen if (whenStr != null && whenStr.length() > 0) { FlexibleStringExpander fse = FlexibleStringExpander.getInstance(whenStr); @@ -735,13 +742,13 @@ throws GenericEntityException { GenericDelegator delegator = currentContent.getDelegator(); - List assocList = getAssociations(currentContent, linkDir, assocTypes, fromDate, thruDate); + List<GenericValue> assocList = getAssociations(currentContent, linkDir, assocTypes, fromDate, thruDate); if (UtilValidate.isEmpty(assocList)) { return assocList; } if (Debug.infoOn()) Debug.logInfo("assocList:" + assocList.size() + " contentId:" + currentContent.getString("contentId"), ""); - List contentList = FastList.newInstance(); + List<GenericValue> contentList = FastList.newInstance(); String contentIdName = "contentId"; if (linkDir != null && linkDir.equalsIgnoreCase("TO")) { contentIdName = contentIdName.concat("To"); @@ -749,9 +756,9 @@ GenericValue assoc = null; GenericValue content = null; String contentTypeId = null; - Iterator assocIt = assocList.iterator(); + Iterator<GenericValue> assocIt = assocList.iterator(); while (assocIt.hasNext()) { - assoc = (GenericValue) assocIt.next(); + assoc = assocIt.next(); String contentId = (String) assoc.get(contentIdName); if (Debug.infoOn()) Debug.logInfo("contentId:" + contentId, ""); content = delegator.findByPrimaryKey("Content", UtilMisc.toMap("contentId", contentId)); @@ -808,7 +815,7 @@ return contentList; } - public static List getAssociations(GenericValue currentContent, String linkDir, List assocTypes, String strFromDate, String strThruDate) throws GenericEntityException { + public static List<GenericValue> getAssociations(GenericValue currentContent, String linkDir, List<GenericValue> assocTypes, String strFromDate, String strThruDate) throws GenericEntityException { GenericDelegator delegator = currentContent.getDelegator(); String origContentId = (String) currentContent.get("contentId"); Timestamp fromDate = null; @@ -819,12 +826,12 @@ if (strThruDate != null) { thruDate = UtilDateTime.toTimestamp(strThruDate); } - List assocs = getContentAssocsWithId(delegator, origContentId, fromDate, thruDate, linkDir, assocTypes); + List<GenericValue> assocs = getContentAssocsWithId(delegator, origContentId, fromDate, thruDate, linkDir, assocTypes); //if (Debug.infoOn()) Debug.logInfo(" origContentId:" + origContentId + " linkDir:" + linkDir + " assocTypes:" + assocTypes, ""); return assocs; } - public static List getContentAssocsWithId(GenericDelegator delegator, String contentId, Timestamp fromDate, Timestamp thruDate, String direction, List assocTypes) throws GenericEntityException { + public static List<GenericValue> getContentAssocsWithId(GenericDelegator delegator, String contentId, Timestamp fromDate, Timestamp thruDate, String direction, List assocTypes) throws GenericEntityException { List exprList = FastList.newInstance(); EntityExpr joinExpr = null; EntityExpr expr = null; @@ -913,9 +920,9 @@ andMap = UtilMisc.toMap(contentIdField, contentId, "contentAssocTypeId", contentAssocTypeId); } try { - List lst = delegator.findByAndCache("ContentAssoc", andMap); + List<GenericValue> lst = delegator.findByAndCache("ContentAssoc", andMap); //if (Debug.infoOn()) Debug.logInfo("getContentAncestry, lst:" + lst, ""); - List lst2 = EntityUtil.filterByDate(lst); + List<GenericValue> lst2 = EntityUtil.filterByDate(lst); //if (Debug.infoOn()) Debug.logInfo("getContentAncestry, lst2:" + lst2, ""); if (lst2.size() > 0) { GenericValue contentAssoc = (GenericValue)lst2.get(0); @@ -942,11 +949,11 @@ if (Debug.infoOn()) Debug.logInfo("getContentAncestry, contentId:" + contentId, ""); Map andMap = UtilMisc.toMap(contentIdField, contentId); try { - List lst = delegator.findByAndCache("ContentAssoc", andMap); + List<GenericValue> lst = delegator.findByAndCache("ContentAssoc", andMap); //if (Debug.infoOn()) Debug.logInfo("getContentAncestry, lst:" + lst, ""); - List lst2 = EntityUtil.filterByDate(lst); + List<GenericValue> lst2 = EntityUtil.filterByDate(lst); //if (Debug.infoOn()) Debug.logInfo("getContentAncestry, lst2:" + lst2, ""); - Iterator iter = lst2.iterator(); + Iterator<GenericValue> iter = lst2.iterator(); while (iter.hasNext()) { GenericValue contentAssoc = (GenericValue)iter.next(); String contentIdOther = contentAssoc.getString(contentIdOtherField); @@ -966,15 +973,15 @@ } } - public static List getContentAncestryNodeTrail(GenericDelegator delegator, String contentId, String contentAssocTypeId, String direction) throws GenericEntityException { + public static List<Map<String, Object>> getContentAncestryNodeTrail(GenericDelegator delegator, String contentId, String contentAssocTypeId, String direction) throws GenericEntityException { List contentAncestorList = FastList.newInstance(); - List nodeTrail = FastList.newInstance(); + List<Map<String, Object>> nodeTrail = FastList.newInstance(); getContentAncestry(delegator, contentId, contentAssocTypeId, direction, contentAncestorList); - Iterator contentAncestorListIter = contentAncestorList.iterator(); + Iterator<GenericValue> contentAncestorListIter = contentAncestorList.iterator(); while (contentAncestorListIter.hasNext()) { - GenericValue value = (GenericValue) contentAncestorListIter.next(); - Map thisNode = ContentWorker.makeNode(value); + GenericValue value = contentAncestorListIter.next(); + Map<String, Object> thisNode = ContentWorker.makeNode(value); nodeTrail.add(thisNode); } return nodeTrail; @@ -1414,7 +1421,7 @@ return val; } - public static Map makeNode(GenericValue thisContent) { + public static Map<String, Object> makeNode(GenericValue thisContent) { Map thisNode = null; if (thisContent == null) return thisNode; |
Free forum by Nabble | Edit this page |