48 Proxy Assets: Integrating Third-Party Content Sources

This chapter describes the integration of external web content into sites delivered by WebCenter Sites using the proxy asset type framework.

This chapter contains the following sections:

48.1 Introduction

Principle

A proxy asset is an asset representing content stored and managed in a remote location. This is illustrated in Figure 48-1.

Figure 48-1 Proxy Asset Architecture

Description of Figure 48-1 follows
Description of "Figure 48-1 Proxy Asset Architecture"

In this architecture:

  • A third-party content repository is assumed, accessible through a public read API from both the editorial and delivery instances.

  • The Contributor interface is customized to access the third-party repository in order to make external content visible in the UI.

  • A proxy asset is created on the fly for every external content rendered in the UI. A proxy asset does not store any metadata, which is assumed to be accessible through a public read API provided by the third-party service.

  • On the live instance, templates written for proxy assets are accessing the same repository and API to render external content on the live site.

Note:

Only those proxy assets that are used are permanently stored in the Sites database. For example, proxy assets created while rendering search results are later purged by a dedicated cleaning event.

Contributor Interface

Once a new proxy asset type is registered in the WebCenter Sites database, developers need to provide the appropriate UI customizations before contributors can start interacting with external content.

The nature of the UI customizations being implemented depend on the contributor's specific requirements. For example, it is expected that the Contributor interface search functionality will be hooked to the external repository's own search service in most cases.

Once this is done, and external content are surfaced in the Contributor interface in the form of proxy assets, they behave mostly like standard assets. That is, contributors are able to:

  • Search the external content repository, using the same asset search tab, showing results in list or thumbnail view, docked or undocked

  • Use drag and drop from search, or tree

  • Associate external content to other assets, whether in form view or web view

  • Preview external content, using WebCenter Sites templates

  • Bookmark, tag, set in workflow, approve and publish

A default inspection screen is provided for all proxy types.

Note:

The following restrictions apply:

  • The external content lifecycle is assumed to be entirely managed in a third-party UI; that is, WebCenter Sites only needs read access to the external repository. Consequently, UI actions such as editing, versioning, and so forth, are disabled by default for all proxy asset types.

  • Proxy assets cannot have associated assets or subtypes.

  • The external content repository must be accessible from both the contribution and delivery WebCenter Sites instances.

48.2 User Interface Customizations

To make the use of WebCenter Sites a better experience, the User Interface can be customized many ways. This section details customizing the portions of the UI to sync with third-party software.

This section contains the following topics:

48.2.1 Customizing the Search Start Menu

In the WebCenter Sites UI, users run searches restricted to a specific asset type by selecting a search start menu, as shown in Figure 48-2.

Figure 48-2 Search Type Start Menu Selection

Description of Figure 48-2 follows
Description of "Figure 48-2 Search Type Start Menu Selection"

In order to customize search for a given asset type, override the controller element UI/Data/Search/Search by creating the following elements:

CustomElements/<AssetType>/UI/Data/Search/SearchAction
CustomElements/<AssetType>/UI/Data/Search/SearchJson

For more details about controller elements, and element overrides, refer to Part III, "Customizing the Contributor Interface."

UI/Data/Search/Search runs the search code and generates the appropriate JSON data for consumption by the grid widget (whether in list or thumbnail view, docked or undocked).

The JSON data must be a valid dojo datastore. Section 48.3.4, "Customizing Search." Section 48.2.2, "Customizing the Content Tree" gives an actual implementation example.

48.2.2 Customizing the Content Tree

The content tree can be customized by defining a custom tree section, contained in a new or existing tree tab: For more details about defining custom tree tab sections, see the WebCenter Sites Administrator's Guide.

A custom tree tab section points to a JSP element generating data based on some rendering logic, eventually consumed by the tree widget. Tree data is generated using the following utility elements:

  • OpenMarket/Gator/UIFramework/BuildTreeNodeId: generates a tree node ID

  • OpenMarket/Gator/UIFramework/BuildTreeNode: generates properly formatted tree node data

The following examples show how to build a tree node, whether it represents an asset or not (that is, an asset node compared to an adhoc node). For more information, see Chapter 34, "Customizing the WebCenter Sites Admin Interface."

Example 1: Build a Tree Node Representing an Asset

This example shows how to generate a tree node representing a single asset node. The node will execute the inspect action when double-clicked (see executeFunction parameter), that is, the asset default inspect view will be rendered in a new tab (or focused if the asset is already opened in a tab).

<%--
- Generates a tree node id.
- The element expects the asset id and type to be passed in parameters
- called respectively "ID" and "AssetType".
- The generated id is stored in a Sites variable called "TreeNodeID"
--%>
<ics:callelement element="OpenMarket/Gator/UIFramework/BuildTreeNodeID">
     <ics:argument name="AssetType" value="Article" />
     <ics:argument name="ID" value="1234567890" />
</ics:callelement>

<%-- 
- Generates a tree node for this asset.
- Note that this element implicitly consumes variables
- currently present in the ICS scope, including "TreeNodeID"
--%>
<ics:callelement element="OpenMarket/Gator/UIFramework/BuildTreeNode">
     <ics:argument name="Label" value="Node Label, for example asset name or other
                   readable string" />
     <ics:argument name="Description" value="Some optional tooltip text" />
     <ics:argument name="executeFunction" value="inspect" />
</ics:callelement>

Example 2: Build an adhoc Tree Node

Tree nodes do not necessarily represent assets, in which case they are called adhoc nodes. The example below shows how to generate an adhoc node representing a parent node, a node which has children and can be expanded and collapsed.

<%--
- Generates a tree node id. 
- For adhoc nodes, the expected parameter is called "AdHoc",
- and can be any arbitrary string, which must be unique across tree nodes
--%>
<ics:callelement element="OpenMarket/Gator/UIFramework/BuildTreeNodeID" >
  <ics:argument name="AdHoc" value="SomeUniqueString" />
</ics:callelement>

<%-- 
- Generates a "LoadURL", that is the URL to be called when a tree node
- is expanded. In this case, we're calling the default utility SiteCatalog entry
- (OpenMarket/Gator/UIFramework/LoadTab) which, in turn, will invoke
- a custom element ("Some/Other/Element" in our example), in charge
- of generating the child nodes, based on some custom logic.
--%>
<satellite:link
       assembler="query" 
       pagename="OpenMarket/Gator/UIFramework/LoadTab"
       outstring="LoadURL">
  <satellite:argument name="populate" value="Some/Other/Element"/>
  <satellite:argument name="op" value="load"/>
</satellite:link>

<%--
- Generates the corresponding tree node data
- Note that this element consumes the "LoadURL" variable previously generated.
- The presence of LoadURL indicates whether a node should be marked as expandable
or not.
--%>
<ics:callelement element="OpenMarket/Gator/UIFramework/BuildTreeNode">
  <ics:argument name="Label" value="Some meaningful node label" />
</ics:callelement>

48.3 Integrating External Content in the WebCenter Sites Contributor Interface

Before contributors can work with external content, a developer has to implement the required UI integration code.

In this section, we'll use a dummy content repository as a case study, and explain the steps to integrate it with:

  • search: to use the repository search service instead of the standard Sites search

  • content tree: to allow contributors to browse the repository content in a custom content tree

This section contains the following topics:

48.3.1 Case Study: The Dummy Repository

Setting Up Sample Data

We're using a set of static JSON files deployed directly in the WebCenter Sites web application to emulate a dummy content repository. The dummy content is assumed to be media content (images).

Those JSON files simulate the following services:

  • Search the repository for a given term (searching on all, "ski" or "surfing" will return actual results): http://localhost:7001/sites/samples/search/<searchterm>.json

  • Get all content categories ("Ski" and "Surfing"): http://localhost:7001/sites/samples/browse/categories.json

  • Get all dummy content for a given category: http://localhost:7001/sites/samples/browse/<category>.json

  • Get metadata for a given content id: http://localhost:7001/sites/samples/content/<id>.json

For example, /samples/search/ski.json returns the following example content:

{
  "items": [
    {
      "id": "1001", 
      "title": "Yellow Skier",
      "foo": "bar1",
      "lastModified": "1354735336444",
      "thumbnail": "samples/images/image7_thumbnail.png"
    },
    {
      "id": "1002", 
      "title": "Female Skier",
      "foo": "bar2",
      "lastModified": "1354735336444",
      "thumbnail": "samples/images/image8_thumbnail.png"
    },
    {
      "id": "1003",
      "title": "Ski Jump",
      "foo": "bar3",
      "lastModified": "1354735336444",
      "thumbnail": "samples/images/image9_thumbnail.png"
    }
  ]
}

Later in this guide an example of JSP element which consumes this type of JSON data is shown.

Note:

Sample code is stored in the folder /misc/Samples/. This folder is located in WebCenter Sites. Sample code specific to proxy assets can be found in /misc/Samples/SampleProxy/proxy_sample.zip.

Retrieving Data from the Dummy Repository

In order to avoid duplication of code, the logic needed to query the external content source is encapsulated in a dedicated element, named Dummy/GetData. In this example, data is returned in JSON format. Therefore, this example uses the jersey (http://jersey.java.net/) and jettison (http://jettison.codehaus.org/) libraries, which are already deployed in the WebCenter Sites web application, in order to retrieve and deserialize incoming JSON data.

The element receives a query, for example, /search/ski.json, in an ICS variable named serviceURL and returns a JSONArray object, stored in the ICS scope using ics.SetObj(String, Object):

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ page import="com.sun.jersey.api.client.*" %>
<%@ page import="com.sun.jersey.api.client.config.*" %>
<%@ page import="org.codehaus.jettison.json.*"%>
<cs:ftcs>
<%
//
// Gets data from dummy repository
// This element expects an ICS variable "serviceURL"
// 
Client client = Client.create();
//
// Host, port and webapp prefix are hardcoded for simplicity, modify as
// appropriate.
// In a real-world scenario, the base service URL would probably be stored in
// a configuration file.
//
WebResource res = client.resource("http://localhost:7001/sites/samples" + 
                  ics.GetVar("serviceURL"));
ClientResponse resp = res.accept("application/json").get(ClientResponse.class);
JSONArray list;
if (resp.getStatus() != 200) {
         list = new JSONArray(); // empty array
}
else {
       JSONObject json = new JSONObject(resp.getEntity(String.class));
       list = json.getJSONArray("items");
}

// store the object in the ICS scope
ics.SetObj("items", list);
%>
</cs:ftcs>

48.3.2 Registering a New Proxy Asset Type

In order to represent our dummy content in the WebCenter Sites repository, we need to define a new proxy asset type.

To create a proxy asset type, complete the following steps:

  1. In the Admin tab, expand Proxy Asset Manager. Double-click Add New.

    The Add New Proxy Asset Type page is displayed.

    Figure 48-3 Add New Proxy Asset Type Page

    Description of Figure 48-3 follows
    Description of "Figure 48-3 Add New Proxy Asset Type Page"

  2. In the Name field, enter a name for the proxy asset type. Similarly, add a description in the Description field, and a plural form of the name in the Plural Form field.

    Note:

    Creating or editing proxy assets through the Contributor interface is not available. Consequently, in this step, only a Search start menu should be enabled.

    In this example, and to use with the examples continuing through the rest of the document, enter Dummy in the Name field, Dummy in the Description field, and Dummies in the Plural Form field.

  3. Click Save.

Note:

Proxy Asset Maker will register the new asset type and create a single table with the same name. A proxy asset table has only a subset of standard asset metadata, and defines only one specific column: externalid. This field is meant to store the identifier of the external content in the external repository.

48.3.3 Implementing UI Integration Code

Assets can appear in many places in the Contributor interface. Some examples of these places include:

  • Asset forms, for fields including asset references

  • Search results

  • Content tree panel

In each case, the actual integration code will vary based on the requirements and available customization hooks. However, in all cases, the following principle must be followed: All external content presented in the Contributor interface must be registered as a proxy asset.

In practice, this means creating (or reusing) a proxy asset which externalid field refers to the content identifier in the external content source. This can be done using the usual Asset API classes and methods. However, for simplicity, the proxy JSP tag library is provided.

It contains utility tags to register a given external content as a proxy asset type:

  • <proxy:register />

It also contains utility tags to generate a JSON datastore for data grid widgets (such as search):

  • <proxy:createstore />: initializes a new store

  • <proxy:addstoreitem />: adds an item to a given store

  • <proxy:tojson />: serializes a store to JSON

More details are provided in the Oracle Fusion Middleware WebCenter Sites Tag Reference, and in the code examples in Section 48.3.4, "Customizing Search."

48.3.4 Customizing Search

As explained in Section 48.2.1, "Customizing the Search Start Menu," an override of the controller element UI/Data/Search/Search for the Dummy asset type by creating the following elements:

CustomElements/Dummy/UI/Data/Search/SearchAction
CustomElements/Dummy/UI/Data/Search/SearchJson

Figure 48-4 Search Drop-Down Showing Dummy Asset

Description of Figure 48-4 follows
Description of "Figure 48-4 Search Drop-Down Showing Dummy Asset"

As illustrated, Dummy appears in the search drop-down. Selecting Dummy (that is, the name of the proxy asset type already created) will run the custom search code, instead of the Lucene-based search.

To implement a full custom search, complete the remaining topics in this section.

This section contains the following topics:

48.3.4.1 Get Search Results Using the Provided Third-Party API

In CustomElements/Dummy/UI/Data/Search/SearchAction, JSON data is retrieved using the element written in Section 48.3.1, "Case Study: The Dummy Repository."

This code example is used in the retrieval:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"%>
<%@ taglib prefix="proxy" uri="futuretense_cs/proxy.tld"%>
<%@ page import="org.codehaus.jettison.json.*"%>
<%@ page import="COM.FutureTense.Interfaces.Utilities"%>
<cs:ftcs>

<%
// in this dummy example, only the empty search string, 'ski' or 'surfing' will 
// return results String searchTerm = ics.GetVar("searchText");
if (!Utilities.goodString(searchTerm)) searchTerm = "all";
%>
<ics:setvar name="serviceURL" value='<%="/search/" + searchTerm + ".json" %>' />
<ics:callelement element="Dummy/GetData" />
<%
JSONArray list = (JSONArray)ics.GetObj("items");

//
// ...to be continued in the next section
//
%>

</cs:ftcs>

At this point, list contains the JSON data received from the external content source.

48.3.4.2 Turn Search Results into Proxy Assets, Filter Incoming Search Results, Register External Content, and Gather Data for Search Grid Widget

This step builds the code through a series of steps.

First, a new data store is built:

<proxy:createstore store="<storeName>" />

where <storeName> is an arbitrary string designating the data store.

Then, for each incoming external content asset, the current asset is registered as a proxy asset:

<proxy:register
       externalid="<external_content_identifier>"
       type="<proxy_asset_type>"
       name="<proxy_asset_name>"
       varname="<variable_name>" />

where:

  • <external_content_identifier> is the identifier of the current external content item in the third-party repository. In the case of this example dummy repository, this identifier is returned in the id field of incoming JSON data.

  • <proxy_asset_type> is the proxy asset type name. In this example, "Dummy."

  • <proxy_asset_name> is the readable string to be used as name throughout the Contributor interface. In this example, the title field returned in the incoming JSON data.

  • <variable_name> is the name of the WebCenter Sites variables which will be contain the proxy asset id corresponding to the current external content item.

Then, for each incoming registered proxy asset, add the asset to the data store:

<proxy:addstoreitem 
       store="<storeName>"
       id="<proxy_asset_id>"
       type="<proxy_asset_type>" />

where:

  • <storeName> is the data store, as defined by <proxy:createstore />.

  • <id> is the proxy asset identifier.

  • <type> is the proxy asset type.

The full code for the Dummy proxy asset type is then:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"%>
<%@ taglib prefix="proxy" uri="futuretense_cs/proxy.tld"%>
<%@ page import="org.codehaus.jettison.json.*"%>
<%@ page import="COM.FutureTense.Interfaces.Utilities"%>
<cs:ftcs>

<%
// in this dummy example, only the empty search string, 'ski' or 'surfing' will
// return results
String searchTerm = ics.GetVar("searchText");
if (!Utilities.goodString(searchTerm)) searchTerm = "all";
%>
<ics:setvar name="serviceURL" value='<%="/search/" + searchTerm + ".json" %>' />
<ics:callelement element="Dummy/GetData" />
<%
JSONArray list = (JSONArray)ics.GetObj("items");
%>

<%-- create a new data store --%>
<proxy:createstore store="assets" />
<%
// go through each incoming item
for (int i = 0; i < list.length(); i++) {
     JSONObject item = (JSONObject)list.get(i);%>

     <%-- Register the current external content item as a proxy asset --%>
     <proxy:register externalid='<%=item.getString("id") %>'
     type="Dummy"
     name='<%=item.getString("title") %>'
     varname="internalid" />

<%-- Add the proxy asset to the datastore --%>
<proxy:addstoreitem store="assets"
                    id='<%=ics.GetVar("internalid") %>'
                    type="Dummy" />
<%
}
// put store name in request scope
request.setAttribute("store", "assets");
request.setAttribute("total", Integer.valueOf(list.length()));
%>
</cs:ftcs>

48.3.4.3 Build a Data Store for the Grid Widget

This step consists in rendering the JSON to be sent back to the UI widget. This step is done inside CustomElements/Dummy/UI/Data/Search/SearchJson.

The proxy:tojson utility tag does this, as shown in the following example:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="proxy" uri="futuretense_cs/proxy.tld"%>
<cs:ftcs>
<proxy:tojson store="${store}" total="${total}" />
</cs:ftcs>

48.3.4.4 Testing Custom Search

Once the JSON is scripted, test the search function to ensure everything works properly. To test the search, complete the following steps:

  1. In the Contributor interface, from the search box select Dummy as the search type. Click the search icon.

    Figure 48-5 Search Drop-Down Showing Dummy Asset

    Description of Figure 48-5 follows
    Description of "Figure 48-5 Search Drop-Down Showing Dummy Asset"

  2. The Search tab will open displaying the associated data.

    Figure 48-6 Search Results

    Description of Figure 48-6 follows
    Description of "Figure 48-6 Search Results"

  3. Switching to thumbnail view shows the following:

    Figure 48-7 Search Results in Thumbnail View

    Description of Figure 48-7 follows
    Description of "Figure 48-7 Search Results in Thumbnail View"

    The search tab is also functional in docked mode. Note that the tooltip is filled in with default data:

    Figure 48-8 Search Results in Docked Panel

    Description of Figure 48-8 follows
    Description of "Figure 48-8 Search Results in Docked Panel"

48.3.4.5 Additional Customizations

This section deals with customizations that can be implemented beyond what has already been covered. It is not necessary to use any of these customizations to have implemented a custom search.

Rendering a Thumbnail

In the example, the Dummy repository returns a URL to a thumbnail image for each content item.

We will modify it to retrieve the thumbnail URL (sent by our Dummy repository) in order to render it. For that to happen, we must customize the grid in thumbnail view (whether docked and undocked), that is, overriding the configuration elements for:

UI/Layout/CenterPane/Search/View/ThumbnailView
UI/Layout/CenterPane/Search/View/DockedThumbnailView

Configuration files for each must be created:

CustomElements/Dummy/UI/Layout/CenterPane/Search/View/ThumbnailViewConfig
CustomElements/Dummy/UI/Layout/CenterPane/Search/View/DockedThumbnailViewConfig

The ThumbnailViewConfig configuration file will have the following XML configuration:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld" %>
<%@ taglib prefix="xlat" uri="futuretense_cs/xlat.tld" %>
<cs:ftcs>
  <thumbnailviewconfig>
    <formatter>fw.ui.GridFormatter.simpleThumbnailFormatter</formatter>
  </thumbnailviewconfig>
</cs:ftcs>

You can see that this overrides the formatter setting for the Dummy thumbnail view (any other setting contained in the global configuration element is maintained). This formatter renders a default view for each search result showing a thumbnail image, and the name field ("inspect" link).

Similarly, docked thumbnail view settings should be overwritten by creating DockedThumbnailViewConfig in CustomElements/Dummy/UI/Layout/CenterPane/Search/View/ with the following XML:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"%>
<cs:ftcs>
<ics:callelement element=
"[CustomElements/Dummy/UI/Layout/CenterPane/Search/View/ThumbnailViewConfig]" />
</cs:ftcs>

Note:

For more details about search configuration, refer to Part III, "Customizing the Contributor Interface."

The brackets around the element name indicates that the ics:callelement tag should not search for a customized version of the given element name.

Configuring the Grid Context Menu

Not all asset operations apply to proxy assets. For this reason, we need to override the default grid context menu setup. Override the element UI/Config/GlobalHtml by creating a new element named MyConfig in UI/Config. For more information on creating MyConfig, see Section 65.5.3, "Customizing Context Menus."

This ensures that none of the global settings will be merged with the overridden settings.

Sort Fields

Depending on the external content source API capabilities, sorting might not be available for all fields. In this case, the sort drop-down menu can be customized by overriding the default context menu configuration file. This is handled by creating the configuration file:

CustomElements/Dummy/UI/Layout/CenterPane/Search/View/SearchTopBarConfig

and use the following XML code (in this case, assuming that only sorting by name is available):

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="xlat" uri="futuretense_cs/xlat.tld"%>
<cs:ftcs>
<sortconfig>
  <sortfields>
    <sortfield id="name_asc">
      <fieldname>name</fieldname>
      <displayname>
          <xlat:stream key="UI/UC1/Layout/NameSort1" escape="true"/> 
      </displayname>
      <sortorder>ascending</sortorder>
    </sortfield>
    <sortfield id="name_desc">
      <fieldname>name</fieldname>
      <displayname>
          <xlat:stream key="UI/UC1/Layout/NameSort2" escape="true"/> 
      </displayname>
      <sortorder>descending</sortorder>
    </sortfield>
  </sortfields>
</sortconfig>
</cs:ftcs>

Be aware that both SearchTopBarConfig and ContextMenuConfig elements must have merge=false set in the element resdetails1 (or resdetails2) field, as shown in Figure 48-9.

Figure 48-9 Configuration File Element Details Value Field

Description of Figure 48-9 follows
Description of "Figure 48-9 Configuration File Element Details Value Field"

48.3.5 Implementing a Custom Tree

A custom tree is useful to allow users to browse external content by category, as shown in Figure 48-10.

Two elements are created to render the tree:

  • Dummy/Tree/Root: renders the tree root nodes, that is, the content categories.

  • Dummy/Tree/Load: renders all content under a given category.

To implement the custom tree, you must first register the custom tree tab, and then implement the custom tree code.

This section contains the following topics:

48.3.5.1 Registering the Custom Tree Tab

The custom tree tab is defined in the Add New Tree Tab page. With the tab created in this example, a new content tree called Proxy Assets will be created, containing a single custom section (Dummy) pointing at Dummy/Tree/Root.

To register the custom tree tab for this example, complete the following:

  1. In the Admin interface, select the Admin tab, then double-click Tree.

    The Tree Tabs page opens.

  2. At the bottom of the Tree Tabs page, click Add New Tree Tab.

    The Add New Tree Tab page opens (Figure 48-11).

    Figure 48-11 Add New Tree Tab Page

    Description of Figure 48-11 follows
    Description of "Figure 48-11 Add New Tree Tab Page"

  3. Fill in the fields as needed. For this specific example, fill in the fields in this way:

    • Title: Enter Proxy Assets for the title of the tab.

    • Sites: Select Proxy from the list.

    • Required Roles: Select Any from the list.

    • Tab Contents: Select Dummy and click Add Selected Items to move it to the Selected column. Dummy should be the only item in the selected column.

    • Section Name: Enter Dummy in the field.

    • Element Name: Enter Dummy/Tree/Root in the field.

  4. Once the fields have been properly entered, click Save to save the asset.

48.3.5.2 Implementing the Tree Code

In Dummy/Tree/Root, the external repository is queried to get the tree root nodes., In this case, the list of categories. Tree node data is then generated for each category:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="satellite" uri="futuretense_cs/satellite.tld"%>
<%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"%>
<%@ page import="org.codehaus.jettison.json.*"%>
<cs:ftcs>

<%-- Retrieve all categories from dummy repository --%>
<ics:setvar name="serviceURL" value="/browse/categories.json" />
<ics:callelement element="Dummy/GetData" />

<%
JSONArray list = (JSONArray)ics.GetObj("items");
for (int i = 0; i < list.length(); i++) {
     String category = (String)list.get(i);
     // 1) build a tree node id - needs to be unique
     // Note: we need to make sure that AssetType is not present in the scope
     // (BuildTreeNodeID is otherwise assuming the tree node to be an asset node)
     %>
     <ics:removevar name="AssetType" />
     <ics:callelement element="OpenMarket/Gator/UIFramework/BuildTreeNodeID" >
          <ics:argument name="AdHoc" value='<%=category %>' />
     </ics:callelement>
     <%
     // 2) build the 'LoadURL' i.e. the URL to call to load this node's children
     // OpenMarket/Gator/UIFramework/LoadTab is a standard element. 
     // Required input is:
     // - populate: the element rendering tree nodes to execute
     // - op: must be set to 'load'
     //
     // We're also passing 'category' which is required by our custom logic in 
     // Dummy/Tree/Load
     //
     %>
     <satellite:link assembler="query"
                       pagename="OpenMarket/Gator/UIFramework/LoadTab"
                       outstring="LoadURL">
     <satellite:argument name="populate" value="Dummy/Tree/Load"/>
     <satellite:argument name="op" value="load"/> 
     <satellite:argument name="category" value='<%=category %>' />
     </satellite:link>

     <ics:callelement element="OpenMarket/Gator/UIFramework/BuildTreeNode">
          <ics:argument name="Label" value='<%=category %>' />
     </ics:callelement>
<%}%>
</cs:ftcs>

This code should generate the following tree:

Figure 48-12 Generated Custom Tree

Description of Figure 48-12 follows
Description of "Figure 48-12 Generated Custom Tree"

Note:

The elements OpenMarket/Gator/UIFramework/BuildTreeNodeID and OpenMarket/Gator/UIFramework/BuildTreeNode both consume variables in the ICS scope. Since the <ics:callelement /> tag has a global scope, it is, in some cases, necessary to explicitly remove (permanently or temporarily) certain variables from the ICS scope, using <ics:removevar />.

In Dummy/Tree/Load, we query the external service to retrieve all content in a given category, then render a node for each content item:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"%>
<%@ taglib prefix="proxy" uri="futuretense_cs/proxy.tld"%>
<%@ page import="org.codehaus.jettison.json.*"%>
<cs:ftcs>

<%-- Retrieve all content for a given category --%>
<ics:setvar name="serviceURL" value='<%="/browse/" + ics.GetVar("category") +
   ".json"%>' />
<ics:callelement element="Dummy/GetData" />

<%
JSONArray list = (JSONArray)ics.GetObj("items");

for (int i = 0; i < list.length(); i++) {
     JSONObject item = (JSONObject)list.get(i);%>

     <proxy:register externalid='<%=item.getString("id") %>'
                     type="Dummy"
                     name='<%=item.getString("title") %>'
                     varname="internalid" />

     <ics:callelement element="OpenMarket/Gator/UIFramework/BuildTreeNodeID">
        <ics:argument name="AssetType" value="Dummy" />
        <ics:argument name="ID" value='<%=ics.GetVar("internalid") %>' />
     </ics:callelement>

     <ics:callelement element="OpenMarket/Gator/UIFramework/BuildTreeNode">
        <ics:argument name="Label" value='<%=item.getString("title") %>' />
        <ics:argument name="Description" value='<%=item.getString("title") %>' />
        <ics:argument name="executeFunction" value="inspect" />
     </ics:callelement>
     <%
}
%>
</cs:ftcs>

You should then be able to browse each category. You should then be able to browse each category, as shown in Figure 48-13.

Figure 48-13 Browsing Custom Tree Objects

Description of Figure 48-13 follows
Description of "Figure 48-13 Browsing Custom Tree Objects"

Double-clicking a node will open the default proxy asset inspection page for that node.

48.4 Embedding Proxy Assets in Web Pages

This section contains the following topics:

48.4.1 Writing Template for Proxy Assets

Templates can be defined for proxy assets, just as templates can be defined for any other asset type. A Dummy asset, as has been used in the preceding examples, can have a template defined in the manner described in Figure 48-14.

Figure 48-14 Defined Summary Template

Description of Figure 48-14 follows
Description of "Figure 48-14 Defined Summary Template"

For all Templates, the usage should be listed as Element is used as Layout. If Element is used within an HTML Page is selected, the Contributor UI will not display the created templates while creating a Page asset.

Writing templates for proxy assets usually involves two distinct actions. First, given an (internal) asset ID, an external ID is retrieved. Second, the third-party repository API is used to retrieve content metadata.

For the specific example of the Dummy asset, the following code would be implemented:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="asset" uri="futuretense_cs/asset.tld"%>
<%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"%>
<%@ taglib prefix="render" uri="futuretense_cs/render.tld"%>
<%@ page import="com.sun.jersey.api.client.*"%>
<%@ page import="com.sun.jersey.api.client.config.*"%>
<%@ page import="org.codehaus.jettison.json.*"%>
<cs:ftcs>
<render:logdep cid='<%=ics.GetVar("tid")%>' c="Template"/>

<%-- first, retrieve the external id --%>
<asset:load name="asset" type='<%=ics.GetVar("c") %>'
       objectid='<%=ics.GetVar("cid") %>' />
<asset:get name="asset" field="name" />
<asset:get name="asset" field="externalid" />

<%
// given the external content id, invoke the  third-party repository API
// to retrieve content metadata
Client client = Client.create();
WebResource res = client.resource("http://localhost:7001/sites/samples/content/" +
     ics.GetVar("externalid") + ".json");
ClientResponse resp = res.accept("application/json").get(ClientResponse.class);
JSONObject data = new JSONObject(resp.getEntity(String.class));
%>
<%-- finally render content metadata --%>
<h4><%=data.getString("title") %></h4>
<p><%=data.getString("foo") %></p>
<img src="<%=data.getString("image") %>" alt="<%=data.getString("title") %>" />
</cs:ftcs>

48.4.2 Using Proxy Assets in Slots

Proxy assets can be embedded inside pages using the exact same tags and principles which apply to standard assets. This is illustrated in Figure 48-15:

Figure 48-15 Example of an Embedded Proxy Asset

Description of Figure 48-15 follows
Description of "Figure 48-15 Example of an Embedded Proxy Asset"

In the code sample below, we assume that a Page asset has a page attribute called dummyAttribute, accepting Dummy assets:

<%@ taglib prefix="cs" uri="futuretense_cs/ftcs1_0.tld"%>
<%@ taglib prefix="ics" uri="futuretense_cs/ics.tld"%>
<%@ taglib prefix="render" uri="futuretense_cs/render.tld"%>
<%@ taglib prefix="insite" uri="futuretense_cs/insite.tld"%>
<%@ taglib prefix="assetset" uri="futuretense_cs/assetset.tld"%>
<cs:ftcs><render:logdep cid='<%=ics.GetVar("tid")%>' c="Template"/>
<html>
<head>
     <title>Proxy test page</title>
</head>
<body>
     <assetset:setasset name="page" id='<%=ics.GetVar("cid") %>'
                        type='<%=ics.GetVar("c") %>' />
     <assetset:getattributevalues attribute="dummyAttribute" listvarname="dummy"
                        name="page" typename="PageAttribute" />
     <ics:listget listname="dummy" fieldname="value" output="id" />
     <h3>Proxy Test Page</h3>
     <insite:calltemplate field="dummyAttribute" c='Dummy'
                        cid='<%=ics.GetVar("id") %>' tname="Summary" />
</body>
</html>
</cs:ftcs>

48.4.3 Caching Proxy Assets

Because the rendered content is stored and managed in a separate repository, WebCenter Sites has no way to know when any content is modified, or even if content is modified.

Because of this, it is recommended to set cache expiration to a limited period of time on templates rendering proxy assets (such as the Summary template defined in Section 48.4.1, "Writing Template for Proxy Assets."