A while back i was trying to implement programmatic portal navigation from this blog. While its a good proof of concept, it didnt work for my requirement. After some hit and trial, i saw the default globeTemplate which gave me a very simple idea on how to execute a declarative portal navigation.
This only works for commandLink but an equivalent structure can be constructed for goLinks as well. Here is how it goes:

For any commandLink to do a portal navigation, it needs 3 things:
1. actionlisnener should call #{navigationContext.processAction}
2. action should point to "pprnav"
3. Link should have a f:attribute with "node" as the name and a reference to a SiteStructure object in the value field.

           <af:commandLink id="pt_cl1" text="#{node.title}"  
                   action="pprnav"  
                   actionListener="#{navigationContext.processAction}">  
            <f:attribute name="node" value="#{node}"/>  
           </af:commandLink>  

                           

You can refer to a specific node in a navigation model with this syntax:
#{navigationContext.navigationModel['/oracle/webcenter/portalapp/navigations/application_navigation_model.xml'].listModel['startNode=/, includeStartNode=false, depth=1'][1]}



where application_navigation_model is the navigation model where the desired node is present as the second node. The highlighted index is the index of the nodes with a starting index of 0.

This commandLink when clicked will cause a portal navigation to the "Docs n Downloads" node.

Special case : Navigation from within taskflows:
Often, most of the links in a portal are in adf regions in specific taskflow. In this case, things change a bit. The settings for f:attribute and actionListener remains the same. However, the action parameter changes. Remember, to execute a portal navigation, a "pprnav" action has to be invoked on the main page. Now since we are inside a region, the action has to be propagated to the main page.

For this use a parent action with parentOutcome=pprnav in your taskflow. Have your links action to this parent action inside the taskflow.

Hope this helps!





0

Add a comment

  1. A while back i was trying to implement programmatic portal navigation from this blog. While its a good proof of concept, it didnt work for my requirement. After some hit and trial, i saw the default globeTemplate which gave me a very simple idea on how to execute a declarative portal navigation.
    This only works for commandLink but an equivalent structure can be constructed for goLinks as well. Here is how it goes:

    For any commandLink to do a portal navigation, it needs 3 things:
    1. actionlisnener should call #{navigationContext.processAction}
    2. action should point to "pprnav"
    3. Link should have a f:attribute with "node" as the name and a reference to a SiteStructure object in the value field.

               <af:commandLink id="pt_cl1" text="#{node.title}"  
                       action="pprnav"  
                       actionListener="#{navigationContext.processAction}">  
                <f:attribute name="node" value="#{node}"/>  
               </af:commandLink>  
    

                               

    You can refer to a specific node in a navigation model with this syntax:
    #{navigationContext.navigationModel['/oracle/webcenter/portalapp/navigations/application_navigation_model.xml'].listModel['startNode=/, includeStartNode=false, depth=1'][1]}



    where application_navigation_model is the navigation model where the desired node is present as the second node. The highlighted index is the index of the nodes with a starting index of 0.

    This commandLink when clicked will cause a portal navigation to the "Docs n Downloads" node.

    Special case : Navigation from within taskflows:
    Often, most of the links in a portal are in adf regions in specific taskflow. In this case, things change a bit. The settings for f:attribute and actionListener remains the same. However, the action parameter changes. Remember, to execute a portal navigation, a "pprnav" action has to be invoked on the main page. Now since we are inside a region, the action has to be propagated to the main page.

    For this use a parent action with parentOutcome=pprnav in your taskflow. Have your links action to this parent action inside the taskflow.

    Hope this helps!





    0

    Add a comment

  2. CMIS(Content Management Interoperability Services) is an open standard for Content Queries used by different Content Integration systems to communicate over Internet.

    Typically, you can query content based on the metadata fields they have.
    Oracle UCM supports CMIS queries based on profiles. By default, the global profile is used.

    Look at this preliminary blog by Yannick for the basics:http://yonaweb.be/using_cmis_query_content_query_navigation_model

    Suppose you have a Single Valued Option List field called xCity. You can write a query to fetch all the documents which have xCity=Denver with this:



    SELECT * FROM ora:t:IDC:GlobalProfile WHERE ora:p:xCity = 'Denver'

    Now lets say, you had to configure your metadata field as a multi valued Options List. So now, one document can have more than one city values attached to them. For example:
    Document1: Denver, Dallas, Chicago
    Document2: Chicago
    Document3: Denver

    Now, if you want to bring in all documents which are tagged with Denver your previous query wont work.
    This is the query you should use in your content presenter taskflow:

    SELECT * FROM ora:t:IDC:GlobalProfile WHERE ANY ora:p:xCity IN (\'Denver\') order by ora:p:dInDate desc

    Dont forget to escape the single quotes else your page will throw an uncompiled query exception when you save your changes.


    0

    Add a comment



  3. Recently, i was designing a dashboard for a client and i stumbled upon this error. There isnt much help on the forums for this so i had to dig on this myself. I found the solution with some help from a colleague.
    When we design dashboards with 3 column layouts we may encounter this error. This blog post talks about correct design pattern for a 3 column layout dashboard involving oracle composer components.
    Imaging you have a dashboard to design with different movable widgets:

     



    You may end up implementing your page somewhat like this:
     

    We often use ADF rich layouts to achieve our page layout. However, with Oracle composer components, the drag and drop might not work when some specific combinations of Composer and ADF components are used. Notice that I have used a panelGroupLayout to group my 3 panelCustomizables in a horizontal layout above.
    However, by doing this, you will end up with 3 different independent containers which cannot share their widgets. If you try to move a widget(showDetailFrame) from one column to another you will see a warning in the log:
    A different component with same id was found in the destination container. Move operation aborted
    The correct design to do a dashboard would be:



     
    Notice how the panelGroupLayout is replaced by a panelCustomizable instead. This ensures that the boxes move around freely. Its important to note that you need to set allowAction=”none” on the top panelCustomizable container so that you don’t end up moving boxes to this panelCustomizable. 




    Hope this helps!
    0

    Add a comment



  4. Webcenter Spaces manages content in UCM using the WebcenterConfigure Component which comes with UCM 11g. Whenever a new Space is created a couple of things happen.
    • First, a new ScopeId is generated in the application which is used to identify the Space. The ScopeId typically looks like this: "s870843cf_3ab1_42eb_8b31_d14df6c3fe20".
    • For every ScopeId, the Spaces app seeds at least 3 application Roles in the policy Store which corresponds to the default user types in a Space. ie, Moderator, Participant and Viewer: 


    • Right after a space is created, the Spaces application creates a folder right under the root spaces folder in UCM. The ScopeId is used to construct a UCM account for this new folder.
     

    • Now, Space administrators can go and assign Moderator/Participant/Viewer roles to different users from the Identity Store. Imagine you have the following members:


    • If you go back to UCM and login with all the 3 users, you will see that they have roughly the same accounts and roles.
    •  In UCM, you can see a document only if the document's Account and Role matches with yours. Your actual access(RWDA) is determined by the intersection of your Role and Account's Permissions.
    • So next question arises: How is Brian restricted to create a document in UCM under this Space folder when he and Bob has the same roles and accounts. The answer is "UCM Extended User Attributes". The implementation of this is hidden and Spaces manages all the finer level access control using extendeduserattributes. You can read more about it here.
    • You can verify  the extended user attributes by querying the USEREXTENDEDATTRIBUTES table in UCM. Or you can invoke the QUERY_EXTENDED_USER_ATTRIBUTES service (details here)
    • Typically rows in the UCM table will look like this: The  DATTRIBUTEPRIVILEGE column will contain integer values signifying the permissions. They follow the unix permission syntax for RWD access.
    • Below is a java program to read the extended user attributes using the UCM Service:
     public static void main(String[] args)throws Exception{  
         IdcClientManager manager = new IdcClientManager();  
         IdcClient idcClient;  
           idcClient = manager.createClient("idc://localhost:4444");  
           IdcContext userContext = new IdcContext("sysadmin", "welcome1");  
           ServiceResponse response;  
               String user = "smishra1";  
               DataBinder dataBinder = idcClient.createBinder();  
               dataBinder.getLocalData();  
               dataBinder.putLocal("IdcService", "QUERY_EXTENDED_USER_ATTRIBUTES");  
               dataBinder.putLocal("dName", user);  
               response = idcClient.sendRequest(userContext, dataBinder);  
               DataBinder serverBinder = response.getResponseAsBinder();  
               DataResultSet userAttribInfo =  
                 serverBinder.getResultSet("ExtUserAttribInfo");   
               System.out.println("----------------Printing extended User Attributes for "+user);  
               for(DataObject obj : userAttribInfo.getRows()){  
                 System.out.println("       Application: "+obj.get("dApplication"));  
                 System.out.println("       Attributes : "+obj.get("AttributeInfo"));  
               }  
       }  
    


    Hope this clears the hidden implementation of Spaces UCM integration.




    0

    Add a comment

  5. After much trial and error , i finally managed to configure the CPS portlets in Webcenter Spaces :) CPS portlets gives you an alternate approach to interact with UCM. Most importantly, it gives you the Workflow Portlet where approvers can review and approve/reject content standing in their queue for approval.
    Below are the steps which i followed in my 11.1.1.5 installation:


    Steps to install CPS Portlet in 11.1.1.5 :
    1Step 1.      Extracted the StellentPortlets.ear:
    Get the ContentPortletSuite_WSRP_Webcenter.zip from MW_HOME/Oracle_ECM1/ucm/Distribution/CPS folder
    2Step 2.       Unzip it in a folder. Make sure that you get a StellentPortlets.ear inside a portlets folder.
    3Step 3.       Open weblogic console and deploy this ear as an application. The doc recommends to deploy the ear on a separate managed server. I am using the WC_Spaces as all the targeted library and datasources are already targeted to this server.
    4Step 4.       Note : if you deploy it to WC_Spaces, you will have to target some libraries to the spaces managed server:
    i.                     oracle.portlet-producer.wsrp
    ii.                   wc-producer-web-lib
    iii.                  wlp-producer-full-web-lib
    iv.                 wlp-light-core-web-lib
    v.                   wlp-framework-common-web-lib
    vi.                 wlp-wsrp-producer-core-web-lib
    5Step 5.       verify that you can access the following url: http://serverurl:8888/cps/portlets/wsrp2?WSDL
    Note that on 11.1.1.5 onwards, the wsrp2 portlets needs to be used instead of the wsrp1.
    6Step 6.       Now we need to register the producer : invoke the wlst from MW_HOME/Oracle_WC1/common/bin
    Execute the 2 commands as it is:
    i.                     Register the portlet on the spaces application (i.e, webcenter)
    Wlst> registerWSRPProducer('webcenter','StellentWSRP','http://train.orion.com:8888/cps/portlets/wsrp2?WSDL',timeout=500)

    ii.                   Now register the JCR connection for the CPS application (i.e StellentPortlets)


    createJCRContentServerConnection('StellentPortlets', 'CSConnection', socketType='socket', serverHost='train.orion.com', serverPort='4444')
    7Step 7.       Restart the managed server (in our case Spaces server)


    8Step 8.       Verify in the Spaces/Portal project that the portlets are added. Go to resource catalog and verify it under the Portlets node. If you see StellentWSRP node, the portlets are added.

    9Step 9.       Add the portlet on a page to verify that it works.

    Note it can take a while when you first add any of the CPS Portlets on to your page.
    If there are no errors in the logs, you should see the portlet in about a minute!
    0

    Add a comment

  6. Configuring mail in webcenter can be cumbersome. Also, the default mail taskflow doesn't expose too many functionality like Custom Folders. One can actually go ahead and configure an External app to the Microsoft Outlook Web Access. Most of the MS exchange mail servers expose this web application.

    You just need to configure the mail Ext app connection with the following details and you are done.


    Just drag and drop the new connection created in your page as an inline Frame pointing to the external app. For example, if your external App name is "Acme OWA", you can access the external app by pointing your inlineFrame to this URI:

    /adfextapplogin?extappid=Acme%20OWA
    0

    Add a comment

  7. In one of our customer's webcenter portal, we had this situation where we needed to access thumbnails from UCM. Now usually, UCM gives a way to stream the generated thumbnails with a URL format like this:
    If the content can be accessed with a url like: http://localhost/idc/groups/public/documents/adacct/000012.pdf

    The thumbnail url will be :
    where 1 is the revision number of the content. 

    Problem: Our client didnt have SSO between /webcenter and /cs. So, our access to thumbnails using this approach was limited only to public content. We were not able to access protected content's thumbnails with this approach. All urls pointing to protected content ids through /cs were challenged for authentication and were not shown on the UI.
    Solution:  there is an alternate URI format which can be used to get to the thumbnails through /webcenter application itself. This takes care of the loggedin users credentials and hence takes care of the protected content scenario too.
    http://localhost:8888/webcenter/content/conn/UCM_TEST/uuid/dDocName:1000035?rendition=P

    where UCM_TEST is the UCM connection name from webcenter to UCM and 1000035 is the contentId of the content.
    Note that you can also make the connection name dynamic  by replacing UCM_TEST with this expression:
    #{documentsService.defaultConnectionName}
    5

    View comments

  8. Sometimes your classes might contain map instance variables which gets exposed as datacontrols. This is pretty common if you are using EJB datacontrols where you might want to expose a transient Map variable. Here is the way to access the exposed dataControl's map's data in your pages.
    Take for example a class which has a map variable:
    public class Test {
    private Long testId;
    Map dataMap;
    public Test() {
    dataMap = new HashMap();
    dataMap.put("one","ONE");
    dataMap.put("two","TWO");
    }

    public void setDataMap(Map dataMap) {
    this.dataMap = dataMap;
    }

    public Map getDataMap() {
    return dataMap;
    }

    public void setTestId(Long testId) {
    this.testId = testId;
    }

    public Long getTestId() {
    return testId;
    }
    }



    when you generate a dataControl from this class. Your datacontrol palette would look something like this:

    when you drag and drop the map datacontrol onto your page, it creates a table and adds a treeBinding to your page's pagedef.


    You can now access the data from this map using the key values with this expression:

    "#{bindings.dataMap.collectionModel.firstRow.inputValue['one']}"


    Note that you can also use generic Maps in this case. The only limitation is against each key there should be an object otherwise the EL throws an error if it cannot find a value in the map for a given Key.
    4

    View comments

  9. There was a request from a friend about a typical page layout this morning. In a page, there is the usual header, center area and the footer. The header and footer are of fixed height and the center is of variable height.

    The requirement is: if the center area contain too many components, the footer should slide down. But if the centre area doesn't contain anything, the footer shouldnot squeeze into the header, it should stay at the bottom of the page.

    For example, lets say the browser height is of 700px and both the header and footer are of 100px each. So now lets say the center area is of 800px, then a scrollbar appears on the page. But if the center is of only 300px,(making the whole content to be of 100+100+300=500px), then the footer should not come up towards the middle of the page, it should stay at the end of the page and the center area should show all its contents with the required space towards the end.

    Solution: After wasting some time on a panelStretchLayout, i decided to go with the panelBorderLayout and some javascript. I am using a trick here to insert a spacer on the "start" facet of the panelBorderLayout. This spacer height is set using javascript on load of the page to get the required height of the center area when there are fewer components.
    Below is the jspx source:
    <?xml version='1.0' encoding='UTF-8'?>
    <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <f:view>
    <af:document id="d1">
    <af:clientListener method="onDocLoad" type="load"/>
    <af:form id="f1">
    <af:panelGroupLayout id="pgl1" halign="left" valign="top"
    layout="scroll">
    <af:panelBorderLayout id="pbl1">
    <f:facet name="start">
    <af:spacer width="10" height="10" id="dch1"
    clientComponent="true"/>
    </f:facet>
    <f:facet name="bottom">
    <af:panelGroupLayout id="panelGroupLayout2" halign="left"
    valign="top" layout="vertical"
    inlineStyle="border-color:Black; border-width:2px; border-style:solid; height:100.0px;"/>
    </f:facet>
    <f:facet name="end"/>
    <f:facet name="top">
    <af:panelGroupLayout id="panelGroupLayout1" halign="left"
    valign="top" layout="vertical"
    inlineStyle="border-color:Black; border-width:2px; border-style:solid; height:100.0px;"/>
    </f:facet>
    <af:panelGroupLayout id="pgl2" layout="vertical" halign="left"
    valign="top">
    <af:commandButton text="commandButton 1" id="cb1"/>
    </af:panelGroupLayout>
    </af:panelBorderLayout>
    </af:panelGroupLayout>
    </af:form>
    <af:resource type="javascript">
    function onDocLoad(){
    var defScrWidthComp = document.getElementById('dch1');
    var h = getWindowHeight();
    h = parseInt(parseInt(h) - parseInt(200));
    defScrWidthComp.style.height = h+'px';
    }

    function getWindowHeight() {
    var myHeight = 0;
    if( typeof( window.innerWidth ) == 'number' ) {
    //Non-IE
    myHeight = window.innerHeight;
    } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    //IE 6+ in 'standards compliant mode'
    myHeight = document.documentElement.clientHeight;
    } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    //IE 4 compatible
    myHeight = document.body.clientHeight;
    }
    return myHeight;
    }

    </af:resource>
    </af:document>
    </f:view>
    </jsp:root>


    Try to put some more components on the panelGrouplayout inside the panelBorderLayout to see what happens when the center area exeeds the browser height.
    5

    View comments

  10. People who are working on taskflows and adf regions often need to interact between regions.
    One of the new scopes which comes with ADF is the pageFlowScope. This scope is taskflow private and cann't be accessed from outside of the taskflow.
    If you don't have a too complex design (For example involving dynamic taskflows) and your page contains multiple regions within another bounded taskflow in the background, you can access the pageFlowScope of any region from any other region or from anywhere within the page with the following methods:
    //Main Method:
    public static PageFlowScope getPageFlowScopeOfTaskFlowId(String taskFlowId){
    Map tfMap = new HashMap();

    Object o1 = JSFUtils.resolveExpression("#{controllerContext.currentViewPort}");
    ChildViewPortContextImpl viewPort = (ChildViewPortContextImpl)o1;
    Set clientIds = (Set)viewPort.getChildViewPortClientIds();
    for(Object id : clientIds){
    ViewPortContextFwk fwk = viewPort.getChildViewPortByClientId(id.toString());
    String tfId = fwk.getTaskFlowContext().getTaskFlowId().getFullyQualifiedName();
    tfMap.put(tfId,id.toString());
    }
    viewPort = getViewPortByClientId(tfMap.get(taskFlowId));

    return viewPort.getPageFlowScopeMap();
    }

    // Helper Method:
    private static ChildViewPortContextImpl getViewPortByClientId(String viewPortClientId){
    Object o1 = JSFUtils.resolveExpression("#{controllerContext}");
    ControllerContextImpl ctx = (ControllerContextImpl)o1;
    ChildViewPortContextImpl view = (ChildViewPortContextImpl)ctx.getViewPortByClientId(viewPortClientId);
    return view;
    }


    A simple call can be done from the managed bean of any taskflow like:
    PageFlowScope pfs = getPageFlowScopeOfTaskFlowId("/WEB-INF/com/apps/../../flow/MyTaskFlow.xml#MyTaskFlow");


    Caution: As you can see the methods rely heavily on internal ADF controller design hence, i would just advice to use the code for understanding purpose of how each region is treated by the controller.
    Disclaimer:I havn't tested this code on any complex design pattern but for most of the simple flows it works just fine:)
    4

    View comments

Loading