Skip to Content

So, this is it then: My first blog
ever! Never thought I’d have a public diary. But I have a whole bunch of ideas
and tips to share with you. And sharing knowledge is what makes this community
such a big success. So here’s my share of knowledge…

This time I want to write about how to
shorten portal URLs.


The Problem


Simple Portal URLs


You all might already know the portal
URL of the following sort:

http://portal.acme.com/irj/index.html, or

http://portal.acme.com/irj/portal, or even

http://portal.acme.com/irj/servlet/prt/portal/prtroot/com.sap.portal.navigation.portallauncher.default.

In fact the latter one results from an
HTTP redirect from /irj/index.html in SP2
portals – in NW04 portals this will always redirect to the portal alias *
/irj/portal*. And in all cases (SP2 or NW04
portals) the latter one is the URL of the portal component that the request is
dispatched to when a portal alias like /irj/portal
is used – the portal alias will stay in the address bar of the browser, though.</p>

Portal URLs for Accessing Pages Directly


Now imagine an initial call to a
specific portal page (using a bookmark for instance). Accessing a specific
portal page that not happens to be the “first” page (home page, start page,
etc.) of the user’s role combination is done by using the “NavigationTarget”
parameter. Let’s assume the page we want to access directly is
the portal page “Health” (3rd level) in the folder
“Politics” (2nd level) underneath the entry point folder “Topics”, and it
has the following ID (or name): “ROLES://portal_content/acme/roles/com.acme.role.public/com.acme.workset.public/topics/politics/com.acme.page.health”,
where “portal_content/acme/roles/com.acme.role.public“
is the path to the role “Public” within the PCD, and “com.acme.workset.public/topics/politics/com.acme.page.health“
is the path to the page “Health” within that role. What you also notice is that
the page has not been directly put into the role, but instead has been put into
a workset, and that workset in turn has been added to the role. In the Portal
Content Studio this might look like this:


image

This rather long ID is in fact the
slightly modified PCD URL of the page within the PCD (the prefix “pcd:” is
exchanged with “ROLES://”). You find it in the property “PCD Location” of the
page within the role (select the page in the role, click “Properties”, look for
“PCD Location” within the list of properties).

Now, accessing this page directly means
calling one of the following URLs:

http://portal.acme.com/irj/index.html?NavigationTarget=ROLES://portal_content/acme/roles/com.acme.role.public/com.acme.workset.public/topics/politics/com.acme.page.health, or

http://portal.acme.com/irj/portal?NavigationTarget=ROLES://portal_content/acme/roles/com.acme.role.public/com.acme.workset.public/topics/politics/com.acme.page.health, or even

http://portal.acme.com/irj/servlet/prt/portal/prtroot/com.sap.portal.navigation.portallauncher.default?NavigationTarget=ROLES://portal_content/acme/roles/com.acme.role.public/com.acme.workset.public/topics/politics/com.acme.page.health.

And these are the URLs that will stay
like this in the browser’s address bar – with the exception of the first one, of
course. Ain’t that a bit too ugly for users that are used to nice short URLs
that on top tell him something about the navigation hierarchy? Like for
instance http://www.acme.com/topics/politics/health.html?</p>

The Solution


So, what are our possibilities? How do
we get closer to this? Well, we could use some tricks. Here are my thoughts,
step by step:


1) URL to a “PageLauncher” Component with a “Merge Path”


Parameter


This would be a URL like this: http://portal.acme.com/irj/servlet/prt/portal/prtroot/index.htm?page=/topics/politics/health.
And the explanations:


a) “/topics/politics/health”
is the merge path (a word probably coined by me), i.e. the portal page
“Health” (3rd level) in the folder “Politics” (2nd level) underneath the entry
point folder “Topics”, and for each folder/page on the path a mergeID has been
defined, for instance mergeID=”topics” for the entry point “Topics”. By this the
page can be identified stating the “path” of mergeIDs starting from the entry
point.
After resolving the merge path either
the standard portal launcher should be called again with the now known real
“NavigationTarget” (easily done with an HTTP redirect, but the displayed URL
changes to the monstruous one), or the standard portal launcher is to be
included as a portal node to the response (more difficult to implement, since
knowledge of the POM and portal request cycles is needed). In the latter case a
little Javascript is to be included in the response as well. This script only
needs to raise an navigation event via EPCM.doNavigate(realNavigationTarget)
on page load.

But this still doesn’t satisfy me. I
personally don’t like the usage of the parameter “page=” here.


2) URL to a “PageLauncher” Component with an Embedded


Merge Path



The URL could be even further shortened, when we “embed” the merge path to the URL: http://portal.acme.com/irj/servlet/prt/portal/prtroot/page/topics/politics/health.
Explanations:

3) Using a Portal Alias


Here is the example: http://portal.acme.com/irj/portal?page=/topics/politics/health.
This is a variant of 1), with the addition that the portal alias “/portal”
is used. The portal alias dispatches the request to the standard portal launcher
(com.sap.portal.navigation.portallauncher.default). Alternatively you can
modify the web.xml (see …\irj\root\WEB-INF\web.xml) such that the Gateway
Service dispatches to “com.acme.navigation.pagelauncher.default” instead, where
after resolving the merge path the standard portal launcher is to be called
again.

But the “page=” parameter shows again.
Let’s get rid of it again.


4) URL Composed of Portal Alias and Embedded Merge Path


The example http://portal.acme.com/irj/portal/topics/politics/health
uses a combination of variants 1) through 3). Here the merge path is extracted
in a different way, though. The Gateway Service (or Alias Service) adds an
attribute into the request before dispatching the request. The value in our
case would be “portal/topics/politics/health”
(which allows us again to substract the merge path). For SP2 this attribute is “ORIGINAL_REQUEST_URI”,
i.e.

and for NW04 “PortalAlias”, i.e.


How to Resolve the Merge Path


<p>
Resolving the merge path could be done
like this (draft, not tested):</p>

<textarea cols=”100″ rows=”41″ wrap=”off”>
INavigationService navigationService = (INavigationService)PortalRuntime.getRuntimeResources().getService(INavigationService.KEY);
NavigationEventsHelperService navHelperService = (NavigationEventsHelperService)PortalRuntime.getRuntimeResources().getService(NavigationEventsHelperService.KEY);

java.util.Hashtable environment = navHelperService.getEnvironment(request);
NavigationNodes initialNodes = navigationService.getInitialNodes(environment);
StringTokenizer mergeIDs = new StringTokenizer(mergePath, “/”);
INavigationNode targetNode = null;
INavigationNode pathNode = null;
NavigationNodes nodes = initialNodes;

while(mergeIDs.hasMoreTokens() && nodes != null) // loop through all levels defined by the “length” of the merge path
{
     String mergeID = mergeIDs.nextToken();
     Iterator it = nodes.iterator();
     while(it.hasNext()) // loop through all nodes trying to find a node with the same mergeID
     {
          INavigationNode node = (INavigationNode) it.next();
          if(node.isMergible() && mergeID.equals(node.getMergeID())) // found a node with the same mergeID
          {
               if(mergeIDs.hasMoreTokens()) // there are more levels to check
                    pathNode = node;
               else // found the target node, since we reached the last/deepest level defined by the merge path
                    targetNode = node;
               break; // inner loop, either go on to next level (pathNode) or stop looping (targetNode)
          }
     }
     if(pathNode != null) // we found the node with this level’s mergeID, go on to the next level
     {
          nodes = pathNode.getChildren();
          pathNode = null;
          continue;
     }
     else if(targetNode != null)
     {
          break; // outer loop
     }
}
String realNavigationTarget = null;
if(targetNode != null) // done, got the real navigation target (e.g. “ROLES://portal_content/…”)
     realNavigationTarget = targetNode.getName();
</textarea>

<p>Once the real NavigationTarget has been calculated, the client can call something like this (here written in JSP):</p>

<textarea cols=”100″ rows=”9″>
<%if (realNavigationTarget != null) {%>
<script>
function loadRealNavigationTarget() {
     EPCM.doNavigate(“<%=realNavigationTarget%>”);
}
EPCM.subscribeEvent( “urn:com.sapportals.portal:browser”, “load”, loadRealNavigationTarget);
</script>
<%}%>
</textarea>


How it Fits Together


If the above code is part of a component
that is placed into the Framework Page, then the corresponding content page is
called only after the complete Framework Page including the initial home page
has finished loading. This example is the fastest and easiest way, though.
There of course might exist other, more complicated solutions.


Pitfalls?



The usage of merge paths has some
administrational overhead, of course. If you only want to make a small number
of pages accessible for direct calling via merge paths, then you must define
mergeIDs only for these pages and the folders they are in. If you want to make
all pages accessible via merge paths, then you must define proper
mergeIDs for all folders and pages in all roles. Remember that
the original purpose of a mergeID is to merge certain nodes (folder/page) from
different roles/worksets/folders, so be careful when using them, especially with
different roles that might be assigned to the same user.


The server performance that the resolving of merge paths costs is in my eyes
neglectable. But if you chose to have the “real” NavigationTarget called
after page load (via
EPCM.doNavigate(realNavigationTarget)), then you’d have to deal with at least one further roundtrip.</p>

The Conclusion


Compared to http://www.acme.com/topics/politics/health.html
we reached an acceptable solution with http://portal.acme.com/irj/portal/topics/politics/health.
Don’t you think?


To report this post you need to login first.

10 Comments

You must be Logged on to comment or reply to a post.

  1. Anonymous
    Have you ever looked at tinyurl.com? Not sure if it will work across firewalls though
    (0) 
    1. Sven Kannengiesser Post author
      Yeah, looks like a nice and nifty tool.  But it depends on a Internet connection.  And that’s what we cannot guarantee when dealing with (internal) portals at a customer, not counting that you’d depend on a foreign third-party service. Besides it simply redirects to the ‘long’ URL once tinyurl.com has resolved the ‘short’ URL, and the address bar stays with the long URL.  That’s what I wanted to avoid.
      (0) 
  2. Anonymous

    Sven, good so see/hear/read/ something from you again. You found some really cute solution to these clumsy URLs.

    (0) 
  3. Ingo Tegeder
    Hi Sven!
    What do you think about the following idea
    (also communicated in /message/1552749#1552749 [original link is broken])

    Within the WebAS you can define URLS.
    Take for example portal.sap.com/useradmin

    If it would be possible to define a URL like
    portal.sap.com/short by yourself within WAS and simply put a redirect to portal.sap.com/irj/portal/short then it should work, shouldn´t it?

    Looking forward to hear your thoughts on it
    Cheers
    Ingo

    (0) 
    1. Sven Kannengiesser Post author
      Hi Ingo!
      I know of the possibility to use HTTP Aliases (or even Application Aliases). With an HTTP Alias you could really shorten your URL further, even leaving out “/irj/portal”. But you could use them only for selected and well known navigation targets, since you’d have to manually create an HTTP Alias + index.html for each navigation node you want to reach via this mechanism. So this mechanism is feasible for quicklinks, that redirect you to the desired navigation nodes (deep down the navigation) – i.e. you can use them only initially when entering the portal, not when navigating within the portal.
      Example: Define an HTTP Alias “topics_public_health”, match it to some *static* directory “/topics/politics/health”, create an index.html wherein you redirect the request to “http://portal.acme.com/irj/portal?navigationtarget=roles://portal_content/acme/roles/com.acme.role.public/com.acme.workset.public/topics/politics/com.acme.page.health“. So, now you could type in the URL “http://portal.acme.com/topics_public_health” and you will indeed see the corresponding portal page, but the URL wouldn’t stay like that and the further navigation couldn’t use other HTTP Aliases, since the NavigationService or your navigation iView doesn’t have knowledge of the other HTTP Aliases.
      Best regards
      Sven
      (0) 
      1. Ingo Tegeder
        Hi Sven,
        Yes, you are absolutely right.
        I tested my solution now and it is working really nice.
        I was just thinking about the nice usability that I know from the Quicklinks in Service Marketplace
        (like service.sap.com/notes). Having a nice and easy “EntryPoint”.
        And I think this is what I have with my solution.

        I know that you are working in the Look&Feel area
        at SAP and thought, this solution “feels” really
        nice. Customer is telling me the same.

        Cheers
        Ingo

        (0) 
  4. Jimmy Roberts
    Hi Sven,

    Have you ever createdan URL for an iView in the MSS Business Package? For example, a discrete URL that navigates directly to an Employee’s data in MSS General Data, say Org Assignment? The problem would seem to be calling the OADP, passing a value for the employee and then the NavigationTarget, all from one URL.

    Thanks & Regards,
    Jimmy.

    (0) 
  5. pradeep bondla
    Hi,
    I need to use this for only 5-10 navigation node in my entire portal, still I need to wirte the code ( in option 4 of the blog)? or can I use Merge ID property of the page without the code?

    If code is compulsury, there where to write it?

    (0) 
    1. Sven Kannengiesser Post author

      Hello!<br/><br/>Nowadays you can use quick links or quick link paths. On certain nodes (pages/iViews/folders) within a role you define the property ‘Quick Link’. Say you have a page called “Health” within a role, and you set the property Quick Link to “health”. Then you can reach that particular page at run time via the URL “/irj/portal/health”.<br/><br/>Now, if you furthermore define quick links on each parent node of this page (role folders, workset folders) and the page itself, then you can alternatively use quick link paths that are composed of the single quick links in the path from entry point to the respective node/page. Say in our example we have the first level folder (and thus entry point) “Topics” with Quick Link=”topics”, the second level folder “Politics” with Quick Link=”politics”, and finally our page from above (“Health” with Quick Link=”health”). Then the (unique) access URL is “/irj/portal/topics/politics/health” (or as already stated above “/irj/portal/health”, but which is not identifying because there might be other pages with the same quick link -> first come first serve).<br/><br/>These quick links and quick link paths can be used to send them via mail or spell out the link to someone via phone. But, unfortunately, the portal framework does not use such URLs for rendering its navigation links. But you can always have some custom coding in this case: a new navigation component/iView which uses the Navigation Tag Library to render a top/detail navigation. Here you  render navigation URLs for anchor tags either with a quick link (path) – if available for the respective node – or with the NavigationTarget parameter and the node’s (hashed) name.<br/><br/>In NetWeaver Portal 7.0 EHP 2 and NetWeaver Portal 7.20 there will be an enhanced version of the <nav:navNodeTarget> JSP tag, which allows you to use a new navigation method (byQuickLink, next to the existing two byEPCM and byURL).<br/><br/>Best regards,<br/>Sven

      (0) 

Leave a Reply