The ATag class is the base class used to write custom tags. To implement a custom tag, follow the steps below:
Implement the DisplayTag abstract method. Use this method to create and display HTML. To display any HTML and tags defined within the tag, call ProcessTagBody and return the resulting HTML.
Implement the Create abstract method to return a new instance of the tag.
Create exactly one public
static final ITagMetaData member
variable that provides the name and description of the tag. For example,
the code below is for the custom tag <pt:thistag/>:
public
static final ITagMetaData = new TagMetaData("thistag", "This
tag displays some HTML ...");
Create one public static
final RequiredTagAttribute or
OptionalTagAttribute member variable
for every attribute that the tag supports. For example, the code below
continues the example above, if pt:thistag has two attributes (e.g., <pt:thistag
pt:firstattribute="foo" pt:secondattribute="bar"/>).
You can also use standard HTML and XML attributes; see Accessing
Attributes below.
public
static final RequiredTagAttribute = new RequiredTagAttribute( "firstattribute",
"This attribute is used to ...");
public
static final OptionalTagAttribute = new OptionalTagAttribute( "secondattribute",
"This optional attribute is used to ...", AttributeType.STRING,
"default value");
The ATag class allows you to include a wide range of functionality in custom tags. For a full list of interfaces and methods, see the Adaptive Tag Framework API Documentation. For information on how the portal processes tags, see Adaptive Tag Control Flow.
The ITagMetaData, RequiredTagAttribute, and OptionalTagAttribute objects pre-process tag attributes (presence, correct type, and default values). If the required attributes are not correct, an error is logged and the tag and its children are skipped. An HTML comment describing the tag and error is displayed instead. All basic data types are supported as attributes (defined in the AttributeType class), including boolean, char, double, int, long and string.
You can access any attributes used in a custom tag. The "pt:" attributes specify the logic for the tag, while any non-pt attributes specify the behavior of the resulting HTML tag. Non-pt attributes are only applicable in tags that output a simple HTML tag.
To access pt attributes, use the appropriate GetTagAttributeAs* method using the attribute name. A method is provided for each supported attribute type, i.e., GetTagAttributeAsLong. The GetTagAttribute method is provided for backwards compatibility and should not be used.
First, define the attribute:
MODE
= new OptionalTagAttribute("mode", "Turns debug mode on
and off.", AttributeType.BOOLEAN, "true");
Then, access the attribute in the DisplayTag method:
boolean
bNewDebugMode = GetTagAttributeAsBoolean(MODE);
To access non-pt (XML/HTML) attributes, use the GetXMLTagAttribute method using the attribute name, or GetXMLTagAttributesAsString to retrieve all non-pt attributes.
result.AddInnerHTMLElement(new
HTMLGenericElement("<a href=\"" + GetHREF() + "\"
" + GetXMLTagAttributesAsString() + ">"));
The IEnvironment class provides access to information about the current request and user, including the following:
HTTP Request and Response: Retrieve the Request or Response objects, or the Request URL.
User information: Retrieve the user's session, or key information including language, locale, time zone, and access style (standard, 508, or low bandwidth).
VarPacks: Retrieve any VarPacks associated with the application in which the tag is executed.
For example:
String
strTZ = GetEnvironment().GetTimeZone();
IXPRequest
request = GetEnvironment().GetCurrentHTTPRequest();
Tags can store custom data as member variables using the SetStateVariable or SetStateSharedVariable methods, and retrieve it using GetStateVariable or GetStateSharedVariable.
Standard variables (stored with SetStateVariable) can only be accessed by tags in the same library. Shared variables (stored with SetStateSharedVariable) can be accessed by tags from any library. To prevent tags from other libraries from editing a shared variable, set bOwnerEditOnly to true when the shared variable is stored (tags in other libraries will still be able to read the variable).
The Scope parameter determines who can see the data and how long it stays in memory. The following options are defined in the Scope class:
Application Scope: The data is visible to all tags and all users, and is only removed when the application is restarted. Therefore, care should be used when storing data on the application to make sure it does not become cluttered with large amounts of data.
HTTP Request Scope: The data will be visible to all tags in the same HTTP Request as the current tag, and is removed from memory when the HTTP Request is finished.
Session Scope: The data is visible to all tags for the current user, and is cleared from memory when a user logs out and logs in again.
Persistent Session Scope: The data is visible to all tags in the same HTTP session, and is only removed from memory when the browser is closed or the browser session times out. Note: Data is not cleared on user logout, so do not cache anything on this scope that could be considered a security risk if it was leaked to another user. Most tags should use Session Scope for HTTP Session data storage (as described above).
Portlet Request Scope: The data is visible to all tags in the same portlet as the current tag, and is removed from memory when the portlet is finished displaying. Tags in other portlets on the same page will not be able to see the data.
Tag
Scope: The data can only be seen by children of the current tag
and is removed from memory when the tag is finished. (For example, in
the following tags: <pt:atag><pt:btag/></pt:atag><pt:ctag/>,
data stored in Tag Scope by "atag" would be visible to "btag"
but not to "ctag.")
If data is stored directly in the tag in member variables (not recommended), override the ReleaseTag method to release the data stored on the tag.
/**
* @see com.plumtree.portaluiinfrastructure.tags.ATag#ReleaseTag()
*/
public void ReleaseTag()
{
// Release all member variables.
m_strPreviousRequestURL = null;
}
Note: Displaying an HTMLElement in a tag and caching it so another tag can add more HTML is not supported. HTMLElement trees can be generated and stored for later use as long as they are self-contained trees and used in a read-only way. It is safest to clone a cached HTMLElement tree before trying to display it again to make sure there are no threading problems.
It is a best practice not to use static fields for data storage in tags. Each tag instance is guaranteed to be accessed by only a single thread at a time, but there may be multiple threads accessing different instances of the same tag class at the same time, either from the same user or a different user. This means that any static fields will need to be accessed using synchronized methods. Since there can be multiple instances of the same tag running at the same time, state variables set in shared Scopes (Session, Persistent Session and Application) may change values during the execution of a single tag.
To include JavaScript in a tag, use the AddJavaScript method inside your DisplayTag method.
HTMLScriptCollection
scriptCollection = new HTMLScriptCollection();
HTMLScript script = new HTMLScript("text/javascript");
scriptCollection.AddInnerHTMLElement(script);
script.AddInnerHTMLString("function myTest() { alert('test'); }");
AddJavascript(scriptCollection);
To include common JavaScript that can be shared between multiple instances of a tag (i.e. JavaScript that is displayed once per page, regardless of how many tags of a certain type there are), override the DisplaySharedJavascript method. DisplaySharedJavaScript is called automatically by the framework.
/**
* Adds the PTIncluder object to the client. This
object is used for
* retrieving JSComponent client classes from a page.
*/
public HTMLScriptCollection DisplaySharedJavascript()
{
HTMLScriptCollection result = new HTMLScriptCollection();
HTMLScript script = new HTMLScript("text/javascript");
result.AddInnerHTMLElement(script); script.SetSrc("/myjsfile.js");
return result;
}
Note: JavaScript is not displayed in 508 mode for either method, since section 508 compliant browsers do not support JavaScript.
If there are errors in the tag and the JavaScript cannot be displayed properly, the tag should throw an XPException with an error message, and the tag framework will log the error and add the message and stack trace to the HTML as an HTML comment. The message contents will be HTML encoded before being added to the comment.
Tags can be used within other tags. The outer tag is referred to as the "parent" tag. Any tags within a parent tag are referred to as "child" tags of that tag.
If the tag is only intended for use within a particular parent tag, create a public static final RequiredParentTag member variable. If there are multiple RequiredParentTag members, at least one of the parent tags must be present for the child tag to function.
If the tag must include a particular child tag to function, create a public static final RequiredChildTag member variable for each tag that is required inside the parent tag. If the child tag is not required for the parent tag to function, but is still related to that tag, create a public static final RelatedChildTag member variable instead.
public
static final RequiredChildTag DATA_OBJECT;
static
{
... DATA_OBJECT = new RequiredChildTag(DataObjectTag.TAG);
}
Note: If required parent or child tags are missing when a tag is displayed, the tag framework will not process the incorrect tag and will add an error message to the HTML as an HTML comment.
To display the tag in non-standard access styles (508 or low bandwidth), override the SupportsAccessStyle method. The default implementation of the SupportsAccessStyle method will cause the tag to be skipped in 508 and low-bandwidth mode. Make sure that tags that support 508 mode can function without JavaScript, since JavaScript will not be displayed in 508 mode.
Most tags display HTML, display the body, then display more HTML. If the tag displays the tag body more than once (i.e., looping tag), override the GetTagType() method and return TagType.LOOPING. If the tag never displays the tag body (i.e. a singleton tag), override GetTagType() and return TagType.NO_BODY. The GetTagType() method can be used to optimize tag transformation.
Follow the instructions below to deploy your custom tags.
Navigate to PORTAL_HOME\settings\portal and open CustomTags.xml in a text editor (you might need to make the file writeable).
Find the <AppLibFiles> tag and add a new entry using the name of the .jar/.dll file used to define the custom tag library (e.g., mytags).
|
|
Add the custom implementation (.jar/.dll) to the portal hierarchy:
Java:
Copy the custom .jar file to PORTAL_HOME\lib\java and add it to the portal.war file in PORTAL_HOME\webapp. (You must stop the portal while modifying portal.war because it will be locked while the portal is running.) If you installed the UICI, you can complete this step by running a clean build:
Open a command prompt and change the directory to the \ptwebui directory where you installed the UICI.
Run a clean build
using the following Ant script: ant build
Generate a new WAR
file for the application server using the following Ant script: ant
install
Note: This target deletes and rebuilds all jar files associated
with all the UI source projects (as well as the custom projects in the
ptwebui folder). See Creating
a Custom Project (Java) for details on adding your projects to the
build script.
C#:
Copy the custom .dll file to PORTAL_HOME\webapp\portal\bin. In most cases, you can complete this step by running a clean build:
Build the project in Visual Studio.
Visual Studio should
copy the sampletags.dll file from SOURCE_HOME\sampletags\dotnet\prod\bin
to PORTAL_HOME\webapp\portal\bin for you. If there are problems with Dynamic
Discovery on startup, you might need to do this step manually. This is
necessary to allow Dynamic Discovery to find the new library.
Note: See Creating
a Custom Project (.NET) for details on adding your projects to the
build script
Once you have deployed your code, create a portlet that contains the tag and add it to a portal page as described below.
Add the following HTML to your portlet. Custom Adaptive Tags must either include the correct XML namespace or be contained within another tag that does. The simplest way is to put the HTML inside a span as shown below. (Custom Adaptive Tags must use the pt:libraryname.tagname and pt:attributename format. For details on tag syntax, see Adaptive Tags.)
<span
xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/'>
<pt:mytags.mytag/>
</span>
Add the portlet to a portal page, then view the page to see the tag execute. (For details on registering portlets in the portal, see Portlet Configuration.)