package er.extensions.foundation; import com.webobjects.appserver.WOActionResults; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOAssociation; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WOElement; import com.webobjects.appserver.WORequest; import com.webobjects.appserver.WOResponse; import com.webobjects.appserver._private.WOCGIFormValues; import com.webobjects.appserver._private.WODynamicElementCreationException; import com.webobjects.appserver._private.WOHTMLAttribute; import com.webobjects.appserver._private.WOHTMLDynamicElement; import com.webobjects.appserver._private.WONoContentElement; import com.webobjects.appserver._private.WOStaticURLUtilities; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation._NSDictionaryUtilities; /** * ERXHyperlinkResource is very similar to WOHyperlink action Binding are the some, * but there is one big difference. There is a "linkResource" Binding. * * Like the ERXStaticResource this Object creates a URL just in Time for * your URL with a new Syntax: * * href * http://{url} * https://{url} * * Direct Action * da://{actionClass}:{directActionName} * da://{directActionName} * wa://{actionClass}:{directActionName} * wa://{directActionName} * * REST * rest://{restAction} * ra://{restAction} * * Page * page://{pageName} * * SnoWOman * cms://{cmsUrl} * * WOResource * static://{frameworkName}:{fileName} * static://{fileName} * * * Reason 1 : Bindings * normal way * <wo:hyperlink actionClass="{actionClass}" directActionName="{directActionName}" ... /> * * HyperlinkResource * <wo:ERXHyperlinkResource linkResource="da://{actionClass}:{directActionName}" ... /> * * Reason 2 : CMS * HyperlinkResource Object makes it easy to create Objects in CMS Systems. * It will also heavily used in SnoWOman and other coming Frameworks. * It is easy to write a URL into the Database and retrieve the Link * via this Object. */ public class ERXHyperlinkResource extends WOHTMLDynamicElement { //******************************************************************** // WOAssociation //******************************************************************** protected WOAssociation _action; protected WOAssociation _string; protected WOAssociation _linkResource; protected WOAssociation _disabled; protected WOAssociation _fragmentIdentifier; protected WOAssociation _escapeHTML; private final WOAssociation _queryDictionary; private NSDictionary<String, WOAssociation> _otherQueryAssociations; //******************************************************************** // Constructor //******************************************************************** @SuppressWarnings("unchecked") public ERXHyperlinkResource(String aName, NSDictionary<String, WOAssociation> someAssociations, WOElement template) { super("a", someAssociations, template); _otherQueryAssociations = _NSDictionaryUtilities.extractObjectsForKeysWithPrefix(_associations, "?", true); _otherQueryAssociations = ((_otherQueryAssociations != null) && (_otherQueryAssociations.count() > 0)) ? _otherQueryAssociations : null; _disabled = _associations.removeObjectForKey(WOHTMLAttribute.Disabled); _queryDictionary = _associations.removeObjectForKey(WOHTMLAttribute.QueryDictionary); _escapeHTML = _associations.removeObjectForKey(WOHTMLAttribute.EscapeHTML); _string = _associations.removeObjectForKey(WOHTMLAttribute.String); _fragmentIdentifier = _associations.removeObjectForKey(WOHTMLAttribute.FragmentIdentifier); _action = _associations.removeObjectForKey(WOHTMLAttribute.Action); _linkResource = _associations.removeObjectForKey("linkResource"); if (_linkResource == null && _action == null) { throw new WODynamicElementCreationException("<" + getClass().getName() + "> Missing required attribute: 'linkResource' or 'action' "); } } @Override public String toString() { return "<" + getClass().getName() + " string: " + _string + " linkResource: " + _linkResource + " queryDictionary: " + _queryDictionary + " otherQueryAssociations: " + _otherQueryAssociations + " fragmentIdentifier: " + _fragmentIdentifier + " disabled: " + _disabled + " secure: " + _secure + ">"; } //******************************************************************** // RR-Methods //******************************************************************** @Override public WOActionResults invokeAction(WORequest aRequest, WOContext aContext) { String nextPageName = null; WOActionResults invokedElement = null; final WOComponent component = aContext.component(); if (aContext.elementID().equals(aContext.senderID())) { if (!((_disabled != null) && _disabled.booleanValueInComponent(component))) { if (_linkResource != null) { Object nextPageValue = _linkResource.valueInComponent(component); if (nextPageValue != null) { nextPageName = nextPageValue.toString(); if(nextPageName.startsWith("page://")) { int i = nextPageName.indexOf("://") + 3; nextPageName = nextPageName.substring(i); if (nextPageName != null) { invokedElement = WOApplication.application().pageWithName(nextPageName, aContext); } } } } if(_action != null) { invokedElement = (WOActionResults) _action.valueInComponent(component); } } else { invokedElement = new WONoContentElement(); } if (invokedElement == null) { invokedElement = aContext.page(); } } return invokedElement; } //******************************************************************** // Methods //******************************************************************** @Override protected void _appendOpenTagToResponse(WOResponse aResponse, WOContext aContext) { // スーパークラスのメソッドをオーバライドする必要があります:disable 機能の追加 if (!isDisabledInContext(aContext)) { super._appendOpenTagToResponse(aResponse, aContext); } } @Override protected void _appendCloseTagToResponse(WOResponse aResponse, WOContext aContext) { // スーパークラスのメソッドをオーバライドする必要があります:disable 機能の追加 if (!isDisabledInContext(aContext)) { super._appendCloseTagToResponse(aResponse, aContext); } } protected void _appendQueryStringToResponse(WOResponse aResponse, WOContext aContext, String aRequestHandlerPath, boolean htmlEscapeURL, boolean defaultIncludeSessionID) { NSDictionary<String, Object> aQueryDict = computeQueryDictionaryInContext(aRequestHandlerPath != null ? aRequestHandlerPath : "", _queryDictionary, _otherQueryAssociations, defaultIncludeSessionID, aContext); if (aQueryDict.count() > 0) { String aQueryString = WOCGIFormValues.getInstance().encodeAsCGIFormValues(aQueryDict, htmlEscapeURL); if (aQueryString.length() > 0) { int questionMarkIndex = (aRequestHandlerPath != null ? aRequestHandlerPath.indexOf("?") : -1); if (questionMarkIndex > 0) { aResponse.appendContentString(htmlEscapeURL ? "&" : "&"); } else { aResponse.appendContentCharacter('?'); } aResponse.appendContentString(aQueryString); } } } protected void _appendFragmentToResponse(WOResponse aResponse, WOContext aContext) { String fragmentIdentifier = fragmentIdentifierInContext(aContext); if (fragmentIdentifier.length() > 0) { aResponse.appendContentCharacter('#'); aResponse.appendContentString(fragmentIdentifier); } } protected void _appendCGIActionURLToResponse(WOResponse aResponse, WOContext aContext, boolean htmlEscapeURL, String actionPath) { NSDictionary<String, Object> aQueryDict = computeQueryDictionaryInContext(actionPath, _queryDictionary, _otherQueryAssociations, true, aContext); aResponse.appendContentString(aContext._directActionURL(actionPath, aQueryDict, secureInContext(aContext), 0, htmlEscapeURL)); _appendFragmentToResponse(aResponse, aContext); } protected void _appendComponentActionURLToResponse(WOResponse response, WOContext context, boolean escapeHTML) { String actionURL = context.componentActionURL(WOApplication.application().componentRequestHandlerKey(), secureInContext(context)); response.appendContentString(actionURL); _appendQueryStringToResponse(response, context, actionURL, escapeHTML, true); _appendFragmentToResponse(response, context); } /** * <span class="ja"> * static URL バインディング処理 * * @param response - リスポンス * @param context - コンテキスト * @param escapeHTML - HTML 回避するかどうか * @param staticURL * </span> */ protected void _appendStaticURLToResponse(WOResponse response, WOContext context, boolean escapeHTML, String staticURL) { if (WOStaticURLUtilities.isRelativeURL(staticURL) && !WOStaticURLUtilities.isFragmentURL(staticURL)) { String resourceURL = context._urlForResourceNamed(staticURL, null, false); if (resourceURL != null) { response.appendContentString(resourceURL); staticURL = resourceURL; } else { response.appendContentString(context.component().baseURL()); response.appendContentCharacter('/'); response.appendContentString(staticURL); } } else { // This is a non relative url already. response.appendContentString(staticURL); } _appendQueryStringToResponse(response, context, staticURL, escapeHTML, false); _appendFragmentToResponse(response, context); } protected void _appendOpeningHrefToResponse(WOResponse response, WOContext context) { response.appendContentCharacter(' '); response.appendContentString(WOHTMLAttribute.Href); response.appendContentCharacter('='); response.appendContentCharacter('"'); String prefix = prefixInContext(context); if (prefix.length() > 0) response.appendContentString(prefix); } protected void _appendClosingHrefToResponse(WOResponse response, WOContext context) { String suffix = suffixInContext(context); if (suffix.length() > 0) response.appendContentString(suffix); response.appendContentCharacter('"'); } public void appendContentStringToResponse(WOResponse aResponse, WOContext aContext) { if (_string != null) { WOComponent aComponent = aContext.component(); Object val = _string.valueInComponent(aComponent); if (val != null) { String valueToAppend = val.toString(); boolean shouldEscapeHTML = true; if (_escapeHTML != null) { shouldEscapeHTML = _escapeHTML.booleanValueInComponent(aComponent); } if (shouldEscapeHTML) { aResponse.appendContentHTMLString(valueToAppend); } else { aResponse.appendContentString(valueToAppend); } } } } @Override public void appendChildrenToResponse(WOResponse aResponse, WOContext aContext) { super.appendChildrenToResponse(aResponse, aContext); appendContentStringToResponse(aResponse, aContext); } protected void _appendDataAjaxFalseToResponse(WOResponse response, WOContext context) { response.appendContentString(" data-ajax=\"false\""); } protected String fragmentIdentifierInContext(WOContext context) { Object value = (_fragmentIdentifier != null ? _fragmentIdentifier.valueInComponent(context.component()) : null); return (value != null ? value.toString() : ""); } protected boolean isDisabledInContext(WOContext context) { return (((_disabled != null) && _disabled.booleanValueInComponent(context.component())) || !isRenderedInContext(context)); } protected String linkResourceUri(WOResponse aResponse, WOContext aContext) { String uri = null; if(_linkResource != null) { WOComponent aComponent = aContext.component(); Object val = _linkResource.valueInComponent(aComponent); if (val != null) { uri = val.toString(); } } return uri; } @Override public void appendAttributesToResponse(WOResponse aResponse, WOContext aContext) { super.appendAttributesToResponse(aResponse, aContext); _appendOpeningHrefToResponse(aResponse, aContext); if (_action != null) { _appendComponentActionURLToResponse(aResponse, aContext, true); _appendClosingHrefToResponse(aResponse, aContext); return; } if(_linkResource != null) { String uri = linkResourceUri(aResponse, aContext); if(!ERXStringUtilities.stringIsNullOrEmpty(uri)) { if(uri.startsWith("http://") || uri.startsWith("https://") || uri.startsWith("static://")) { _appendStaticURLToResponse(aResponse, aContext, true, urlForHyperlinkResource(aContext, uri)); _appendClosingHrefToResponse(aResponse, aContext); _appendDataAjaxFalseToResponse(aResponse, aContext); return; } if(uri.startsWith("page://")) { _appendStaticURLToResponse(aResponse, aContext, true, urlForHyperlinkResource(aContext, uri)); _appendClosingHrefToResponse(aResponse, aContext); return; } if(uri.startsWith("da://") || uri.startsWith("wa://")) { int i = uri.indexOf("://") + 3; String s = uri.substring(i); String actionClass = null; String directActionName = null; i = s.indexOf(":"); if(i > 0) { actionClass = s.substring(0, i); directActionName = s.substring(i + 1); } else { directActionName = s; } if ((actionClass != null) || (directActionName != null)) { String actionPath = computeActionStringInContext(actionClass, directActionName, aContext); _appendCGIActionURLToResponse(aResponse, aContext, true, actionPath); } _appendClosingHrefToResponse(aResponse, aContext); _appendDataAjaxFalseToResponse(aResponse, aContext); return; } if(uri.startsWith("rest://") || uri.startsWith("ra://")) { _appendStaticURLToResponse(aResponse, aContext, true, urlForHyperlinkResource(aContext, uri)); _appendClosingHrefToResponse(aResponse, aContext); _appendDataAjaxFalseToResponse(aResponse, aContext); return; } if(uri.startsWith("cms://")) { _appendStaticURLToResponse(aResponse, aContext, true, urlForHyperlinkResource(aContext, uri)); _appendClosingHrefToResponse(aResponse, aContext); _appendDataAjaxFalseToResponse(aResponse, aContext); return; } } } _appendClosingHrefToResponse(aResponse, aContext); } //******************************************************************** // Helper Static Methods //******************************************************************** /** * <span class="en"> * Binding "actionClass" and "directActionName" calculation * * @param actionClass - Action Class * @param directActionName - Direct Action Name * @param aContext - Context * * @return Result * </span> * * <span class="ja"> * バインディング "actionClass" と "directActionName" を処理します。 * * @param actionClass - アクション・クラス * @param directActionName - ダイレクトアクション名 * @param aContext - コンテキスト * * @return 結果文字列 * </span> */ @SuppressWarnings("javadoc") protected static String computeActionStringInContext(String actionClass, String directActionName, WOContext aContext) { String anActionString = null; if ((actionClass != null) && (directActionName != null)) { if (actionClass.equals("DirectAction")) anActionString = directActionName; else anActionString = actionClass + "/" + directActionName; } else if (actionClass != null) anActionString = actionClass; else if (directActionName != null) anActionString = directActionName; else { throw new IllegalStateException("<" + "ERXHyperlinkResource" + "> Both 'actionClass' and 'directActionName' are either absent or evaluated to null. Cannot generate dynamic url without an actionClass or directActionName."); } return anActionString; } public static String urlForHyperlinkResource(WOContext context, String uri) { if(!ERXStringUtilities.stringIsNullOrEmpty(uri)) { if((uri.startsWith("http://")) || (uri.startsWith("https://"))) { return uri; } else if(uri.startsWith("static://")) { return ERXStaticResource.urlForResourceNamed(context, uri); } else if((uri.startsWith("da://")) || (uri.startsWith("wa://"))) { int i = uri.indexOf("://") + 3; String s = uri.substring(i); String actionClass = null; String directActionName = null; i = s.indexOf(":"); if(i > 0) { actionClass = s.substring(0, i); directActionName = s.substring(i + 1); } else { directActionName = s; } if ((actionClass != null) || (directActionName != null)) { String actionPath = computeActionStringInContext(actionClass, directActionName, context); if(!ERXStringUtilities.stringIsNullOrEmpty(actionPath)) { return context._directActionURL(actionPath, null, false, 0, false); } } } else if(uri.startsWith("cms://")) { // TODO SnoWOman coming in the Next Version } else if((uri.startsWith("rest://")) || (uri.startsWith("ra://"))) { // TODO ERRest Future Version } else if(uri.startsWith("page://")) { int i = uri.indexOf("://") + 3; String pageName = uri.substring(i); if(!ERXStringUtilities.stringIsNullOrEmpty(pageName)) { return context.componentActionURL(WOApplication.application().componentRequestHandlerKey(), context.secureMode()); } } } return null; } }