BEAWorld, here we come

All of us at bdg are very excited about BEA World 2006. We’ve got our booth all ready to go, we’ve got bdg mousepads and playing cards to give away along with a brand new 4Gb iPod nano (to one lucky winner). Be sure to stop by our booth, located in the Portal Pavilion, just off the corner of BEA’s main exhibit in the center of the exhibition hall.

On top of all that, I was given a short (but sweet) speaking spot at the ALUI Developer User Group on Monday toward the end of the day (not sure exactly what time). I’ll be presenting on ALUI TagLibs, my favorite topic as of late.

Can’t wait for the big event — see you there!

bdg-podcastOn a related note, the bdg Plumtree Podcast, after an almost one-year hiatus, returned last week with Episode 4. Believe it or not, we’re back today in Episode 5 with our first special guest, long-time Plumtree/ALUI supporter and frequent contributor to the dev2dev forums, Eric Whitley.

UUID Object Opener, The Coolest ALI Taglib Yet

Anyone who’s ever done a major Plumtree/ALUI deployment knows of this problem: You create a portlet or community (or any other object) in Dev and then you migrate it to Test and on to Production. The problem is that you’ve also written some code in your navigation portlet or in another portlet that depends on an ObjectID (e.g. you’ve used a pt:standard:opener tag) and now, in each environment, your ObjectID has changed and you’re basically hosed.

Pre-G6, I came up with a solution described (somewhat hastily) in this post, but it requires a lot of leg work and — worse yet — manual configuration in each environment.

Enter G6 and the magic of taglibs. (Am I beginning to sound like a broken record? Yes, I know, you can’t fix every problem with a taglib, just 95% of them, right?) With this new taglib I wrote today, I extend AOpenerLinkTag and simply convert a UUID to an ObjectID and ClassID so that you can use the same taglib invocation in every environment. I don’t want to toot my own horn too much here, but honestly, this is pretty much the most useful taglib I’ve ever encountered, and once again, it took under 30 minutes to write.

Before I dive into the source, let me back up and say that I had to bend the rules a bit. OOTB, there are two subclasses of ATagAttribute: RequiredTagAttribute and OptionalTagAttribute. I added a third: MutableTagAttribute. It looks and smells like a tag attribute, but under the covers it’s not. Instead of grabbing its value out of the tag invocation, it allows you to set/change the value at runtime inside the taglib code. Granted, this is a little weird, but it’s what I needed to do in order to subclass AOpenerLinkTag and keep it happy dappy.

MutableTagAttribute.java:

package com.bdgportal.alui.taglibs;

import com.plumtree.portaluiinfrastructure.tags.metadata.*;

public class MutableTagAttribute extends ATagAttribute {

  private String value;
  
  public MutableTagAttribute(String name, String desc, AttributeType type) {
    super(name, desc, type);
  }
  
  public String GetDefaultValue() {
    return value;
  }

  public void SetDefaultValue(String value) {
    this.value = value;
  }
  
  public boolean GetIsRequired() {
    return false;
  }
}

Now that we have a tag attribute that we can change on-the-fly, writing the taglib was a snap.

UUIDObjectOpener.java:

package com.bdgportal.alui.taglibs;

import com.plumtree.portaluiinfrastructure.tags.*;
import com.plumtree.portaluiinfrastructure.tags.metadata.*;
import com.plumtree.xpshared.htmlelements.*;
import com.plumtree.taglib.standard.basetags.*;
import com.plumtree.server.*;

public class UUIDObjectOpener extends AOpenerLinkTag
{
  public static final RequiredTagAttribute UUID;
  private MutableTagAttribute OBJECT_ID;
  private MutableTagAttribute CLASS_ID;


  public UUIDObjectOpener() {
    OBJECT_ID = new MutableTagAttribute("objectid", "Not used -- do not set a value for this!", AttributeType.INT);
    CLASS_ID = new MutableTagAttribute("classid", "Not used -- do not set a value for this!", AttributeType.INT);
  }

  public ATagAttribute GetObjectIDAttribute()
  {
    return OBJECT_ID;
  }

  public ATagAttribute GetClassIDAttribute()
  {
    return CLASS_ID;
  }

  public static final ITagMetaData TAG;

  static
  {
    TAG = new TagMetaData("uuidobjectopener", "Opens an object based on its UUID.");
    UUID = new RequiredTagAttribute("uuid", "The UUID for the object you want to open.", AttributeType.STRING);
  }

  public HTMLElement DisplayTag()
  {
    Object[] objectAndClassId = ((IPTMigrationManager)(((IPTSession)GetEnvironment().GetUserSession()).OpenGlobalObject(PT_GLOBALOBJECTS.PT_GLOBAL_MIGRATION_MANAGER,
          false))).UUIDToObjectID(GetTagAttributeAsString(UUID));
  
OBJECT_ID.SetDefaultValue(objectAndClassId[PT_MIGRATION_OBJECT_COLS.PT_MOC_OBJECTID].toString());
    CLASS_ID.SetDefaultValue(objectAndClassId[PT_MIGRATION_OBJECT_COLS.PT_MOC_CLASSID].toString());
    return super.DisplayTag();
  }

  public ATag Create()
  {
    return new UUIDObjectOpener();
  }
}

To deploy this code, see the excellent section on edocs about creating custom Adaptive Tags.

To use this code in a portlet, do the following.

myportlet.htm:

<span xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/'>
   <pt:mytaglibns.uuidobjectopener pt:uuid="{00000-0000-0000-000000}" pt:mode="2">Open My
   Object</pt:mytablibns.uuidobjectopener>
</span>

I did actually test this taglib and it worked swimmingly. Of course you need to substitute a real UUID for all those Os.

In closing, here’s a little shameless plug: I’ve been asked by BEA to give a short, 20-minute talk at BEA World on my favorite subject (duh, taglibs) at the ALUI Developer User Group on Monday, September 18th in Moscone Center, San Francisco. It will happen some time between 1 and 5:30 PM. The ALUI User Groups are free for conference attendees. I hope to see you there or at the bdg booth. Please come on up and introduce yourself — I always like to meet members of this great community in person.

Enjoy!

Comments

Comments are listed in date ascending order (oldest first)

  • Will there be any performance issues using this tag as it involves additional operations of getting Object ID and Class ID from the UUID?

    Posted by: psudhir_it on February 6, 2007 at 10:15 PM

  • From what I can tell, the tag makes a single SQL query (something like select objectid, classid from ptmigration where uuid = ?) which should be a pretty darn fast query, especially since there’s probably an index on uuid.

    The portal is making database calls left and right when you’re displaying a portal page, so making one more database call to generate an opener link shouldn’t really be a performance factor. Nonetheless, it’s definitely something to think about and I’m glad you brought it up.

    Posted by: bucchere on February 7, 2007 at 5:53 PM

  • Hi Chris! Am attempting to move this over to .NET; can you tell me which reference I need to add to resolve com.plumtree.taglib.standard and the AOpenerLinkTag? I’m not sure how to convert this Java fragment, which appears to have two seperate definitions of TAG: public static final ITagMetaData TAG; static { TAG = new TagMetaData(“uuidobjectopener”, “… UUID.”); …can you tell me what it means, and any tips on converting to C# ? Should have an opportunity to throw some load at this later on; will post my results here. My customer is already sensitive to performance problems caused by header portlets making DB calls; so I will also be looking into the caching possibilities. Cheers, Rob

    Posted by: rwagner on October 10, 2007 at 11:04 AM

  • Here is another option. The little known server.pt?uuID={XYZ-UUID} syntax. We use this in our public site which is not gatewayed to deep link into portal content without the need for an adaptive tag. We also use this to establish fqdns in apache that redirect to portal pages. For example in apache setup a fqdn of docs.bea.com which points to portal.bea.com/portal/server.pt?uuID={XYZ-UUID}.

    Posted by: ryanyoder on February 11, 2008 at 6:18 AM

  • Wow, very cool! I totally didn’t know that syntax even existed. If it’s supported, it ought to be documented, because it’s quite handy.

    One gotcha is that you need to pass mode=2 if you want to open the object in view mode because the default is edit mode, e.g.: /portal/server.pt?uuID={46514C0F-0187-4340-AA24-84E41C00C60F}&mode=2

    Posted by: bucchere on February 11, 2008 at 6:31 AM

My Love Affair with ALI Taglibs

There’s been some recent activity on this very old thread in the newsgroups regarding displaying the help link in a portlet. Until G6, this could only be done with native code AFAIK. But, if you supress the portlet title bar, there really aren’t many places where you can put native code in a portlet.

Enter G6 and the extensible taglib support, a quiet little feature that (without any fanfare or marketing by BID) has seriously changed my life.

The source speaks for itself. It look 15 minutes to write. (Granted, I already had my ALUI development environment all set up.)

HelpURL.java:

package com.bdgportal.alui.taglibs;

import com.plumtree.openfoundation.util.*;
import com.plumtree.portaluiinfrastructure.tags.*;
import com.plumtree.portaluiinfrastructure.tags.metadata.*;
import com.plumtree.server.*;
import com.plumtree.xpshared.htmlelements.*;

public class HelpURL extends ATag {

public static final ITagMetaData TAG;
public static final RequiredTagAttribute PORTLET_ID;
  public static final RequiredTagAttribute ID;
  public static final OptionalTagAttribute SCOPE;

static
{
 TAG = new TagMetaData("helpurl",
   "Puts the help URL for this portlet into the variable specified by the ID attribute.");

 PORTLET_ID = new RequiredTagAttribute("portletid",
   "The portlet ID.",
   AttributeType.INT);

 ID = new RequiredTagAttribute("id",
   "The name of the variable in which the help link should be stored.",
   AttributeType.STRING);

 SCOPE = new OptionalTagAttribute("scope",
   "The scope used to store the the help link.",
   AttributeType.STRING, Scope.PORTLET_REQUEST.toString());
}

public HTMLElement DisplayTag() {
 ((IXPList)GetState().GetSharedVariable(GetTagAttributeAsString(ID),
  Scope.GetScope(GetTagAttributeAsString(SCOPE)))).Add(
     ((IPTWebService)((IPTSession)GetEnvironment().GetUserSession()).GetWebServices()
  .Open(((IPTGadget)((IPTSession)GetEnvironment().GetUserSession()).GetGadgets()
  .Open(GetTagAttributeAsInt(PORTLET_ID), false)).GetWebServiceID(), false))
  .GetProviderInfo().ReadAsString("PTC_HTTPGADGET_HELPURL"));
 return null;
}

public ATag Create() {
 return new HelpURL();
}
}

To deploy this code, see the excellent section on edocs about creating custom Adaptive Tags.

To use this code in a portlet, do the following.

myportlet.htm:

<span xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/'>
	<pt:mytaglibns.helpurl pt:portletid="234" pt:id="helplink"/>
	<pt:core.html pt:tag="a" href="$helplink">Help</pt:core.html>
</span>

I didn’t test this, so YMMV. Have fun!

Comments

Comments are listed in date ascending order (oldest first)

  • That’s slick, Chris – that’ll be handy for porting between devstageprod where objectids may be different 🙂

    Posted by: ewwhitley on September 13, 2006 at 6:20 AM

  • Hi, This code makes ten database requests just to get the the IPTWebService object for given portlet. Is there any better way to do this?

    Posted by: Piotr Dudkiewicz on May 18, 2007 at 6:48 AM

  • Sorry, but there’s no better way to get the help URL out of the web service. ALUI is optimized to make calls to its database and the UI code does that everywhere — it’s a dynamic web application, so that should be expected.

    Posted by: bucchere on May 29, 2007 at 2:03 PM

  • It seems that ALUI is optimized to do as many database calls as it’s possible;) Thanks.

    Posted by: Piotr Dudkiewicz on June 1, 2007 at 2:46 AM

A Slick Alternative to pt:standard.openerlink

For one reason or another your bosses (we all have more than one, don’t we?) have told you that the look-n-feel of the common object opener in ALUI just doesn’t cut it. Even though it’s powerful, scalable and pretty nice-looking and it includes a myriad of options (e.g search, browse, single vs. multi-select, set previously selected, etc.), they just want something different. Perhaps they don’t want a pop-up window. Perhaps they don’t like how many clicks it takes to get down to an object. Perhaps they’re just being difficult.

Regardless, you’ve been asked to come up with a clean, fast, in-place object selector that still shows a hierarchical view. (For the purposes of this discussion, I’m going to use the example of communities from here on out instead of just talking about “objects.”) So naturally, as a portlet developer, you turn to the IDK. Unfortunately, if you want to get portal metadata, you have to use the PRC/SOAP server. There goes fast. So maybe you can write it in native code or using database calls. There goes clean.

Your best bet here — and really the only good way to accomplish this — is to develop a custom taglib. Custom taglibs are quickly becoming my favorite new feature in G6. (BTW, if you aren’t on G6, upgrade ASAP — it’s worth the effort.) So, for your benefit, I decided to try my hand at writing a taglib to present a nice hierarchy of communities. Here’s what I discovered.

First off, let’s talk about the HTML I want to display in my custom tag for a moment. Whoever came up with the concept of select boxes and optgroup elements was a complete goofball. Why develop something that’s naturally suited for a hierarchy and then limit the hierarchy to a depth of one?

Here’s an example:

So I had to throw my initial idea of using nested option elements out the window simply because you can’t nest an optgroup within an option. Bummer.

So here’s the display I settled on:

There’s still a hierarchy here, it’s just flattened and there’s essentially a “breadcrumb” for each community. In the example I have, bdg is a top level community and services is a subcommunity of bdg. Consulting, development, integration and training are all subcommunities of services.

Alrighty then, so how to you construct this nice select box? And BTW, make it easy, clean and fast. Here you go:

package com.bdgportal.alui.taglib

import com.plumtree.openlog.OpenLogService;
import com.plumtree.openlog.OpenLogger;
import com.plumtree.portaluiinfrastructure.tags.*;
import com.plumtree.portaluiinfrastructure.tags.metadata.*;
import com.plumtree.xpshared.htmlelements.*;
import com.plumtree.server.*;

public class CommunitySelector extends ATag
{
   private static OpenLogger log = OpenLogService.GetLogger(
    OpenLogService.GetComponent("UI_Infrastructure"),
    "com.bdgportal.alui.taglib.CommunitySelector");

 public static final ITagMetaData TAG;
 public static final RequiredTagAttribute SELECT_ID;
 public static final RequiredTagAttribute SELECT_NAME;
 public static final OptionalTagAttribute SELECT_CLASS;
 public static final RequiredTagAttribute ROOT_FOLDER_ID;

 static
 {
  TAG = new TagMetaData("communityselector",
    "Displays a community selector.");

  SELECT_ID = new RequiredTagAttribute("id",
    "The id of the select box.",
    AttributeType.STRING);

  SELECT_NAME = new RequiredTagAttribute("name",
    "The name of the select box.",
    AttributeType.STRING);
 
  ROOT_FOLDER_ID = new RequiredTagAttribute("rootfolderid",
    "The root folder. All communities in this folder and below " +
    "will be displayed.",
    AttributeType.INT);

  SELECT_CLASS = new OptionalTagAttribute("class",
    "The CSS class of the select box.",
    AttributeType.STRING, "objectText");
 }

 public HTMLElement DisplayTag()
 {
  HTMLSelect comms = new HTMLSelect(
    GetTagAttributeAsString(SELECT_NAME),
    GetTagAttributeAsString(SELECT_ID));  

  comms.SetStyleClass(GetTagAttributeAsString(SELECT_CLASS));
  recursiveAddComms(((IPTSession)GetEnvironment().GetUserSession())
   .GetCommunities(), ((IPTSession)GetEnvironment()
   .GetUserSession()).GetAdminCatalog(), comms,
   GetTagAttributeAsInt(ROOT_FOLDER_ID), "");
 
  return comms;
 }

 public ATag Create()
 {
  return new CommunitySelector();
 }

 public TagType GetTagType() {
  return TagType.NO_BODY;
 }
 
 private void recursiveAddComms(IPTObjectManager commObjMgr,
  IPTAdminCatalog adminCatalog, HTMLSelect comms,
  int folderId, String prefix) {
 
  //CAB: add the communities at this level, if any
  IPTQueryResult commsToAdd = commObjMgr.SimpleQuery(folderId,
   PT_PROPIDS.PT_PROPID_NAME);
  for (int i = 0; i < commsToAdd.RowCount(); ++i) {
   comms.AddOption(new HTMLOption(
    Integer.toString(commsToAdd.ItemAsInt(i, PT_PROPIDS.PT_PROPID_OBJECTID)),
    prefix.substring(0, prefix.length() - 3)));
  }
 
  IPTAdminFolder adminFolder = adminCatalog.OpenAdminFolder(folderId, false);
  if (0 == adminFolder.QuerySubfoldersCount()) {
   return; //CAB: base case
  } else {
   IPTQueryResult subFolders = adminFolder.QuerySubfolders(
    PT_PROPIDS.PT_PROPID_OBJECTID + PT_PROPIDS.PT_PROPID_NAME
    + PT_PROPIDS.PT_PROPID_FOLDER_FOLDERTYPE,
    0,
    PT_PROPIDS.PT_PROPID_NAME,
    0,
    -1,
    null);
 
   //CAB: recurse into each subfolder
   for (int i = 0; i < subFolders.RowCount(); ++i) {
    recursiveAddComms(commObjMgr, adminCatalog, comms,
     subFolders.ItemAsInt(i, PT_PROPIDS.PT_PROPID_OBJECTID),
     prefix + subFolders.ItemAsString(i, PT_PROPIDS.PT_PROPID_NAME)
     + " : ");
   }
  }
 }
}

I think the code pretty much speaks for itself, but if you want further explanation, let me know by posting a comment.

Comments

Comments are listed in date ascending order (oldest first)

  • Iiiinteresting. This is very cool, Chris. I might be forced to highjack this and turn it into a pt:data tag 🙂 You have any thoughts on how / where you might approach caching with this? Have you seen the EOD sample tag?

    Posted by: ewwhitley on August 31, 2006 at 9:41 AM

  • Hmmm . . . caching. It’s so fast OOTB that I didn’t think about caching it. 🙂 Plus, I used it on a project where we have fewer than 100 communities, so I didn’t have any problems. I mean, we’re not using the PRC, right? I guess if you wanted to cache it you could doink around with the shared variables in the tag library (session scope), but you’ve got to worry about clearing the cache after a fixed interval of time.

    Posted by: bucchere on August 31, 2006 at 10:31 AM

  • Awesome! Thanks Chris. I just spent about 2 hours last night trying to do almost the exact same thing. This is great!

    Posted by: jturmelle on May 17, 2007 at 7:48 AM

Hello World + Portlet vs. Native Navigation

Hello world! This is my first post on my shiny new dev2dev blog and needless to say, I’m very pleased to be blogging here and to be given the opportunity to contribute to this prestigious and active community.

That being said, I hope you’ll find my contributions worthwhile. I plan to address mostly technical topics pertaining to AquaLogic User Interaction or ALUI (pronounced ah-LOO-eee by those in the know) with the occasional foray into the business side of ALUI and portals in general. I’ve been working with the ALUI (formerly Plumtree) products now for almost 10 years, but I also know a bit about Weblogic and I want to learn more about the other products in the AquaLogic family. So watch this space for news about all your favorite BEA products!

Okay, on to the topic at hand — navigation. Once upon a time (pre Plumtree 5.0), UI customization meant hacking through layers of ASP or JSP includes to find the right file and then changing this and that and trying not to break anything. And then when upgrade time came, you somehow had to merge all your changes out of the old UI code and into the new. It was a black art at best, a total goat rodeo at worst.

Enter Plumtree 5.0, which introduced the concept of header, footer and content canvas portlets along with “pluggable navigation.” For the first time, you could code your navigation against a supported and documented API and bundle it into a separate DLL or JAR file. When upgrades came along, you simply installed the new code and everything in your old DLL or JAR just worked. The only problem was that writing a pluggable navigation was really hard.

So, in ALUI G6 (the current version), you can easily make a navigation using a remote portlet and few lines of HTML, CSS along with a few handy XML tags. However, the old pluggable navigation model is still in the product, which begs the question: how do I know what form of navigation to use (native or remote)?

A lot of times you’ll hear me say, “it depends” i.e. it depends on your goals or it depends on your architecture. But, in this case, with 100% certainty, I can say that you want to go with remote navigation. Why? Because it’s about 1000 times easier to code, maintain and deploy, you can change it on the fly and in a breeze, you can include Javascript and CSS just like you would in a normal web page, you don’t have to work with confusing HTMLElement classes, you don’t have to know Java or C# . . . need I say more?

If you’re not sold yet on the concept of remote, XML tag based navigation over native pluggable navigation, consider the following example. Say I want to get all the mandatory with tab communities and display them in an unordered list.

Here’s the native code for that:

CListURLMediator mediator = new CListURLMediator(m_asOwner,
m_model.GetCategoryLinks(NavCategoryType.MANDATORYTABS,
false));
HTMLList ul = new HTMLList(1);
while (mediator.Next()) {
  HTMLListItem li = new HTMLListItem();
  li.AddInnerHTMLElement((HTMLAnchor)mediator.GetEntry());
  ul.AddInnerHTMLElement(li);
}
return ul;

And here’s the remote code:

<ul xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/'>
  <pt:ptdata.mandtabcommsdata pt:id='mandtabcomms'/>
  <pt:logic.foreach pt:data='mandtabcomms' pt:var='c'>
    <li><pt:core.html pt:tag='a' href='$c.url'><pt:logic.value 
  pt:value='$c.title'/></pt:core.html></li>
  </pt:logic.foreach>
</ul>

The code speaks for itself. (If you never have to learn what a CListURLMediator is, consider yourself lucky.) Not to mention that you have to worry about the NavType and deployment issues and that you have to restart the portal any time you make a change to your navigation code.

So, is remote, tag-based portlet navigation the solution to all your navigation needs? Not exactly. With pluggable navigation, you can control six navigation areas: the top bar, above the header, below the header, the left side, the right side and above the footer. It’s easy enough to suppress the top bar and replace the first three areas with your header portlet. Same goes for the footer. But what about the left and right sides? You can’t really replace those with portlets unless you use the two narrow columns, which leaves you with only a single portlet column for content.

So you’re in a pickle — you read this post and you’re now the world’s biggest remote navigation fan — but you can’t use them in left and right navigation. There is a solution. If you read this post in the newsgroups, you’ll see that it’s possible to write some code that will load a remote portlet into the left and/or right columns. (The only thing I would change about this code is make sure you put the portlet ID in a VarPack instead of hardcoding it.) Using this fairly simple approach, you can have your cake and eat it too.

Comments

Comments from BEA dev2dev are listed in date ascending order (oldest first)

  • were doing UI customization to modify the top nav section, but would like to move to remote portlet with tags instead for reasons you mention.
    If i understand correctly, we have to do this in header portlet. How do we enforce that that the same top nav is used across the portal if the community managers are able to change the tag code in the header portlet?

thanks
steph
[email protected]

Posted by: sviau on July 9, 2006 at 7:04 AM

  • That’s a great question and I’m glad you asked it. First let me clear something up: community managers cannot and should not change the tag code. However, they can change which header portlets are being used for the communities they manage (and thus choose a whole new set of tags or no tags at all).

    Fortunately, there’s a easy way to prevent this from happening using community templates. There’s a check box on the “Header and Footer” page of the community template that says “Force Community to use Header and Footer from Experience Definition.” You’ll want to check this box. Then, as the portal administrator, you can mandate which community templates your community managers can use. Set it up so that your community managers are forced to use the community template with this box checked and you should be good to go.

    Posted by: bucchere on July 10, 2006 at 2:38 AM

  • Hopefully this is applicable here…I am trying to brand (or customize) the title bar of a portlet(s). I have determined how to change the text on the title bar (PortletResponse.setTitle) but cannot find nothing on changing colors or adding colors to the title bar. Can this even be done? Is there a CSS class somewhere or can it be done programmatically? Thanks for your assistance.

    Posted by: jayparker on January 2, 2007 at 12:28 PM

  • This should do the trick:
    .platportletHeaderBg {
     background-color: red;
    }

    Posted by: bucchere on January 10, 2007 at 7:13 PM

  • is the remote code supported in 6.1 mp1? I attempted to run it and nothing is generated. Has mandtabcommsdata gone away? I notice it also is absent from the current edocs http://edocs.bea.com/alui/devdoc/docs60/Portlets/Adaptive_Portlets/Using_Adaptive_Tags/PlumtreeDevDoc_Portlets_Adaptive_Navigation.htm

    Posted by: geoffgarcia on October 2, 2007 at 11:26 AM