Google Map in PhoneGap/Cordova application

I wanted to figure out how to use Google Maps API in the Phone GAP application so i built this sample application that lets you enter address and it displays that address on the Map, you can download the sample application from here This is a screen shot of how my application looks like
This is how the index.html page for my application looks like

<!DOCTYPE html>
<html>
  <head>
    <title>Google Map Example</title>
 <style type="text/css">
      html { height: 100% }
      body { height: 100%; margin: 0; padding: 0 }
      #map_canvas { height: 100% }
    </style>
    <script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
    <script type="text/javascript" charset="utf-8" src="jquery.js"></script>
    <script src="http://maps.google.com/maps/api/js?sensor=true"></script>
    <script type="text/javascript" charset="utf-8">
    function displayCurrentLocation(){
     console.log("Entering displayCurrentLocation()");
  try{
   var currentLocationLatAndLong = new google.maps.LatLng(37.422006,-122.084095);
   var mapOptions ={
    zoom:8,
    center:currentLocationLatAndLong,
    mapTypeId: google.maps.MapTypeId.ROADMAP
   };
   var mapDiv = document.getElementById("map");
   map = new google.maps.Map(mapDiv,mapOptions);
  }catch(e){
   console.log("Error occured in ConsultantLocator.displayMap() " + e);
  }
     console.log("Exiting displayCurrentLocation()");
    }
    function addMarker(latLng,title,contentString){
  console.log("Entering addMarker()");
  var markerOptions = new google.maps.Marker({
    map: map,
    position: latLng,
    title:title,
    clickable:true
   });
  var marker = new google.maps.Marker(markerOptions);
  var infoWindowOptions = {
   content: contentString,
   position: latLng
  };
  var infoWindow = new google.maps.InfoWindow(infoWindowOptions);
  google.maps.event.addListener(marker, "click", function(){
   infoWindow.open(map);
  });
  console.log("Exiting addMarker()");
 }
    function getLatLangFromAddress(address){
     console.log("Entering getLatLangFromAddress()");
     var geocoder = new google.maps.Geocoder();
     geocoder.geocode( { 'address': address}, function(results, status) {

      if (status == google.maps.GeocoderStatus.OK) {
    var returnedValue =results[0].geometry.location;
       console.log("Address found is " + returnedValue);
       addMarker(returnedValue);
      }else{
       alert("Geocode was not successful for the following reason: " + status);
      }
        });
     console.log("Exiting getLatLangFromAddress()");
    }
    function addMarkerForAddress(){

     console.log("Entering addMarkerForAddress()");
     var address = $("#address").val();
     console.log($("#address"));
     var latLangForLocation = getLatLangFromAddress(address);
     console.log("Value returned by getLatLangFromAddress " +latLangForLocation);
     addMarker(latLangForLocation,address,address);
     console.log("Exiting addMarkerForAddress()");
    }
 document.addEventListener("deviceready", displayCurrentLocation, false);
    </script>
  </head>
  <body >
   <p>
    <input type="text" name="address" id="address" />
    <input type="button" id="getLocation" onclick="addMarkerForAddress()" value="Get Location"/>
   </p>
    <div id="map" style="width:100%; height:100%"></div>
  </body>
</html>
This application loads the google map during startup. When user enters address and clicks on get location it takes the address and uses it to find the latitude and longitude for that address and then users addMarker() method to display maker for that location

Using ripple mobile browser emulator for testing PhoneGap

One of the biggest pain point for testing PhoneGap application is deploying it on emulator and testing it, that process takes long time. So i started using the Ripple which is Google Chrome extension and it makes testing PhoneGap application really easy.
  1. Install Ripple extension in Chrome
  2. Start the Google Chrome browser with its access to local file system by executing chrome.exe -–allow-file-access-from-files
  3. Then Right click on the Ripple symbol and say Manage Google Extensions, on the next screen check Allow access to file URLs check box
  4. Now open index.html from the phoneGap application using file URL and enable Ripple for it
  5. Now you can test the geolocation application like this. With Ripple advantage is you can directly open the HTML in browser and then set geolocation directly using Ripple

Using Geolocation API in Android Emulator

In the Getting address of the current location using GeoLocation API and Google MAP api entry i talked about how to use the GeoLocation API provided as part of HTML 5 to get the current address of the user. Now Phone Gap also provides support for geolocation which means it checks if the browser on the device has support for geolocation if yes it lets it work if not it will call the native API of the underlying browser to get the the location and return it to browser. I wanted to try that so i took the content of geolocation.html and copied it in the index.html file that my phonegap application for android is using, you can download that app from here I followed these steps to build Android PhoneGap application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i copied the content of Geolocation.html in the index.html page
  3. Change the AndroidManifest.xml file to allow application to use the mock location
    
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.webspherenotes.phonegap"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk android:minSdkVersion="8" />
        <uses-permission android:name="android.permission.CAMERA" />
      <uses-permission android:name="android.permission.VIBRATE" />
      
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
      <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
     
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.RECEIVE_SMS" />
      <uses-permission android:name="android.permission.RECORD_AUDIO" />
      <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
      <uses-permission android:name="android.permission.READ_CONTACTS" />
      <uses-permission android:name="android.permission.WRITE_CONTACTS" />
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 
      <uses-permission android:name="android.permission.GET_ACCOUNTS" />
      <uses-permission android:name="android.permission.BROADCAST_STICKY" />
        <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" >
            <activity
                android:name=".HelloPhoneGapActivity"
                android:label="@string/app_name" 
                android:configChanges="orientation|keyboardHidden" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    </manifest>
    
  4. The android emulator does not know how to find out current location of the user, so we have to setup mock location i.e. hard code a location for the user. For that you have two options either use the DDMS perspective in your Eclipse and after your emulator is started select it and enter value of longitude and latitude and click on send like this.
  5. Or open a Telnet session to the emulator by executing telnet localhost 5554 (you can get port number 5554 from the emulator window it would be in title bar) and then in the telnet window use geo fix -121.9754144 37.5669038 command to set the longitude and latitude like this
Now when i run this application in the Android emulator i can see it displaying the address for location that i sent to the emulator like this

Getting address of the current location using GeoLocation API and Google MAP api

The HTML 5 has concept of Geo Location that lets you read current address of the user, So if your using a mobile device it will give you current address using GPS but if your using normal Laptop or Desktop it still gives you current address with less accuracy. In my case it gives address of AT&T office which is my internet provider. I wanted to figure out how GeoLocation API's work so i build this sample application that displays my current address. You can download the Geolocation.html file from here This is how my Geolocation.html page looks like in browser
My Geolocation.html page has one Get Location button when you click on that button it will show you address of the current location. Now the address is not always accurate. Also i tested this code in Firefox 12.0 and Internet Explorer 9, the Geolocation code does not work in Chrome if your accessing the file directly from file system i mean using file:// URL. This is the source code for the Geolocation.html

<!DOCTYPE html>
<html>
  <head>
    <title>GeoLocation</title>
    <script src="http://maps.google.com/maps/api/js?sensor=true">
 </script>
    <script type="text/javascript" charset="utf-8">
   function getLocation(){
      console.log("Entering getLocation()");
      if(navigator.geolocation){
      navigator.geolocation.getCurrentPosition(
      displayCurrentLocation,
      displayError,
      { 
        maximumAge: 3000, 
        timeout: 5000, 
        enableHighAccuracy: true 
      });
    }else{
      console.log("Oops, no geolocation support");
    } 
      console.log("Exiting getLocation()");
    };
    function displayCurrentLocation(position){
      console.log("Entering displayCurrentLocation");
      var latitude = position.coords.latitude;
    var longitude = position.coords.longitude;
    console.log("Latitude " + latitude +" Longitude " + longitude);
    getAddressFromLatLang(latitude,longitude);
      console.log("Exiting displayCurrentLocation");
    }
   function  displayError(error){
    console.log("Entering ConsultantLocator.displayError()");
    var errorType = {
      0: "Unknown error",
      1: "Permission denied by user",
      2: "Position is not available",
      3: "Request time out"
    };
    var errorMessage = errorType[error.code];
    if(error.code == 0  || error.code == 2){
      errorMessage = errorMessage + "  " + error.message;
    }
    alert("Error Message " + errorMessage);
    console.log("Exiting ConsultantLocator.displayError()");
  }
    function getAddressFromLatLang(lat,lng){
      console.log("Entering getAddressFromLatLang()");
      var geocoder = new google.maps.Geocoder();
        var latLng = new google.maps.LatLng(lat, lng);
        geocoder.geocode( { 'latLng': latLng}, function(results, status) {
        console.log("After getting address");
        console.log(results);
        if (status == google.maps.GeocoderStatus.OK) {
          if (results[1]) {
            console.log(results[1]);
            alert(results[1].formatted_address);
          }
        }else{
          alert("Geocode was not successful 
    for the following reason: " + status);
        }
        });
      console.log("Entering getAddressFromLatLang()");
    }
    </script>
  </head>
  <body>
    <h1>Display the map here</h1>
    <input type="button" id="getLocation"
 onclick="getLocation()" value="Get Location"/>
    <div id="map"></div>
  </body>
</html>
When you click on Get Location button the control goes to getLocation() function which first checks if the browser supports GeoLocation API by checking navigator.geolocation object, if that object is not null that means browser supports GeoLocation and we ask browser for current location by calling navigator.geolocation.getCurrentPosition() with displayCurrentLocation() as a call back function if the current location lookup was successful. The browser calls displayCurrentLocation() function with current location using position object, which has longitude and latitude as properties. The latitude and longitude would have values like Latitude 37.5668988 Longitude -121.9753273, so we have to use the Google MAP API to get street address from the longitude and latitude values, for that we call getAddressFromLatLang function. Inside the getAddressFromLatLang() function i am creating object of google.maps.Geocoder and calling its geocode() method with latitude and longitude and it returns array of addresses for that location. Once i have the address i can print it using alert.

Using JQuery Mobile in PhoneGap/Cordova application

JQuery Mobile is one of the most popular Mobile UI frameworks, i wanted to figure out how to use it in Cordova application so i built a sample application that uses JQuery Mobile and also the Cordova device API to display device related properties in JQuery Mobile UI. You can download the sample application from here. I followed these steps to build this application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. I did download the jquery.mobile-1.1.0.zip from JQuery Mobile Download page
  3. I did unzip the jquery.mobile-1.1.0.zip in c:\software folder
  4. Next i copied the css, docs and js folder from C:\software\jquery.mobile-1.1.0\demos into assets/www folder of my web application.
  5. If your just starting with JQuery mobile or playing around then you can even directly point to the CDN URL of JQuery Mobile without actually downloading it in your application, in that case you can skip step 2 and 3. You can simply add these lines in your HTML page
    
    <link rel="stylesheet"
     href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script
     src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
    
  6. Next change your index.html page to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Device Properties Example</title>
    <script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
    <!--  
    <link rel="stylesheet"
     href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script
     src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
    -->
    <link rel="stylesheet"  href="css/themes/default/jquery.mobile-1.1.0.css" />
    <script src="js/jquery.js"></script>
    <script src="js/jquery.mobile-1.1.0.js"></script>
    <script type="text/javascript" charset="utf-8">
     document.addEventListener("deviceready", onDeviceReady, false);
     function onDeviceReady() {
      console.log("Entering index.html.onDeviceReady");
      //var element = document.getElementById('deviceProperties');
      var html = "";
      html = html + "<li>" + 'Device Name: ' + device.name + "</li>";
      html = html + "<li>" + 'Device Cordova: ' + device.cordova + "</li>";
      html = html + "<li>" + 'Device Platform: ' + device.platform + "</li>";
      html = html + "<li>" + 'Device UUID: ' + device.uuid + "</li>";
      console.log(html);
    
      $("#deviceProperties").html(html);
      $("#deviceProperties").listview('refresh');
      console.log("Exiting index.html.onDeviceReady");
     } 
    </script>
    </head>
    <body>
    
     <div data-role="page" id="page1">
      <div data-theme="a" data-role="header">
       <h3>Hello JQuery Mobile</h3>
      </div>
      <div data-role="content">
       Device Properties
       <ul data-role="listview" data-inset="true" 
        id="deviceProperties">
    
       </ul>
      </div>
      <div data-theme="a" data-role="footer">
       <h3>Copyright stuff</h3>
      </div>
     </div>
     
    </body>
    </html>
    
    My application has one JQuery Mobile page whose content is defined using div with id equal to page1. The div with data-role equal to content defines the content of the page. By default it only has empty ul element, i am using onDeviceReady() JavaScript function to read device property and add them to the list.
This is screen shot of application once its loaded

Accessing external website from PhoneGap application

I wanted to create a PhoneGap application, which instead of pointing to a HTML page inside the application should open my blog site http://wpcertification.blogspot.com these are the steps that i followed to build the application, you can download it from here
  • I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  • Then i changed the HelloCordovaActivity like this so that it points to http://wpcertification.blogspot.com
    
    package com.webspherenotes.cordova.sample;
    
    import org.apache.cordova.DroidGap;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    public class HelloCordovaActivity extends DroidGap {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            super.loadUrl("http://wpcertification.blogspot.com");
        }
    }
    
    The onCreate() method is calling super.loadUrl("http://wpcertification.blogspot.com"); so that http://wpcertification.blogspot.com page is opened in the WebView as soon as application starts
  • The default configuration of the PhoneGap application disallows opening of any external URL so i had to change the cordova.xml like this to allow access to http://wpcertification.blogspot.com
    
    <?xml version="1.0" encoding="utf-8"?>
    
    <cordova>
     <!--  
     access elements control the Android whitelist.  
     Domains are assumed blocked unless set otherwise
      -->
    
        <access origin="http://127.0.0.1*"/> <!-- allow local pages -->
     
     
     <access origin="http://wpcertification.blogspot.com" />
     
     
     <!-- <access origin="https://example.com" /> 
     allow any secure requests to example.com -->
     <!-- <access origin="https://example.com" subdomains="true" /> 
     such as above, but including subdomains, such as www -->
     <!--  <access origin=".*"/>   
     Allow all domains, suggested development use only -->
    
        <log level="DEBUG"/>
        <preference name="classicRender" value="true" />
    </cordova>
    
    Take a look at XML comments in this file for syntax on how to allow access to external URLs
Now the application is ready and when i launch it i can see my blog being opened like this

Setting up Android PhoneGap 1.7 development environment

Before few days i did blog about Setting up Android PhoneGap development environment in that case i had PhoneGap 1.6 working on Android 2.2. But then PhoneGap 1.7 came along and the Getting started document document talks about how to setup PhoneGap 1.7 which is now called Apache Cordova with Android 4.0.3. I followed the steps in the document and was able to get it working, one strange thing was after i said run as Android application it did not work as expected for first couple of times but after couple of times it started working, i am still trying to figure out what happened. I built this simple application that uses the device API to print device and Cordova related information. This is how my HTML looks like, you can download the source code for the application from here

<!DOCTYPE html>
<html>
  <head>
    <title>Device Properties Example</title>

    <script type="text/javascript" charset="utf-8" 
 src="cordova-1.7.0.js"></script>
    <script type="text/javascript" charset="utf-8">

    // Wait for Cordova to load
    //
    document.addEventListener("deviceready", onDeviceReady, false);

    // Cordova is ready
    //
    function onDeviceReady() {
        var element = document.getElementById('deviceProperties');

        element.innerHTML = 'Device Name: '     + device.name     + '<br />' + 
                            'Device Cordova: '  + device.cordova  + '<br />' + 
                            'Device Platform: ' + device.platform + '<br />' + 
                            'Device UUID: '     + device.uuid     + '<br />' + 
                            'Device Version: '  + device.version  + '<br />';
    }

    </script>
  </head>
  <body>
    <p id="deviceProperties">Loading device properties...</p>
  </body>
</html>
On the deviceready event i am reading different device properties and displaying them in the page. This is how the page looks like

Creating custom skin for modular theme

In the Create custom Modular Theme i talked about how to create a custom theme based on modular theme architecture, Now i wanted to create a Custom Skin to go along with that theme so i followed these steps.
  1. First connect to http://localhost:10039/wps/mycontenthandler/dav/skinlist WebDAV URL and you will see list of all the existing skins.
  2. Since i want this theme to be very minimal i started with ibm.portal.7002NoSkin, so i copied that folder to my local machine like this
  3. Rename the ibm.portal.7002NoSkin folder to webspherenotes.firstmodularskin
  4. Open the webspherenotes.firstmodularskin\metadata\localized_en.properties file to rename the theme like this
    
    #
    #Thu May 10 10:41:16 PDT 2012
    description=My first modular skin
    title=First Modular Skin
    
  5. Change the content of the theme_en.html to look like this
    
    <span class="wpthemeAccess">
     <h4><a rel="dynamic-content" href="lm:title"></a></h4>
    </span>
    <div class="wpthemeOverflowAuto"> <!-- lm:control dynamic spot injects markup of layout control -->
     <a rel="dynamic-content" href="lm:control"></a> 
    </div>
    
    Basically i did take out most of the code from the skin except the code required for the portlet title and body and i am adding First Modular Skin - to the portlet title
  6. Then copy the webspherenotes.firstmodularskin folder back to the http://localhost:10039/wps/mycontenthandler/dav/skinlist like this
  7. Now when you login into Portal admin console you should see the Custom Skin in the list of skins so assign it to either Portal 7002 Theme or some other theme of your choice, i am adding it to Portal 7002 theme like this

Deferred module loading in the default Modular theme in WPS 7002

I am playing around with the Modular Theme in WPS 7002 for last few days and one thing that i noticed is the new theme is quite past compared to the older theme. So i was trying to figure out what is going on and it seems IBM used one simple trick(In addition to lot of more complex stuff like combining of resources,.. etc) to speed up the page.
If you load a portal page in the browser and use firebug to look at the requests you will notice that it is loading 24 files. Please note that i am using Disabling combining of resource for debugging modular theme settings so every file gets loaded separately so that i can debug and figure out what is going on. If you have not disabled combining of resources you will see very few requests but little less amount of data will be downloaded from the server
Now if you click on the edit button you will notice that it takes few seconds to load the customization palette and if you look at the firebug you will notice that it downloads 124 resources and close to 1.7MB of data and when you move to options like Change Layout or Change Style you will see few more requests and some more data being downloaded.
The Default theme defers loading of edit page related modules which makes sense because the normal user will view page more frequently compared to editing it, some sites wont even allow to edit the page, so why load stuff that is not required and slow down every page.

Creating a Custom Page Layout in WPS 7002

I followed these steps to add a custom page layout in the WPS 7002.
  1. Create a folder named CustomLayout under /fs-type1/themes/webspherenotes.firstmodular/layout-templates/ folder
  2. Copy the layout.html and icon.gif from say 1Column folder and copy it to CustomLayout folder after the copy it should look like this
  3. Copy the newly created CustomLayout folder to the /fs-type1/themes/webspherenotes.firstmodular/layout-templates/ in WebDav storage like this
  4. Change the markup of layout.html in the CustomLayout folder to look like this <!-- Entering CustomLayout.layout.html --> <div class="component-container wpthemeCol wpthemeCol-1 ibmDndColumn wpthemeLeft" name="ibmMainContainer"></div> <!-- Entering CustomLayout.layout.html --> I made changes to take out the section that displays hidden widgets on the page and i also did add the HTML comments to show beginning and end of the layout
  5. Then open the /fs-type1/themes/Portal7.0.0.2/system/layouts.json which holds list of layouts that appear in the change layout panel. change it to add one row for newly create CustomLayout like this { 'label':'change_layout_CustomLayout', 'url':ibmCfg.themeConfig.themeWebDAVBaseURI+'layout-templates/CustomLayout/', 'id':'CustomLayout', 'thumbnail':ibmCfg.themeConfig.themeRootURI+'/layout-templates/CustomLayout/icon.gif', 'help':'' }
  6. After make changes copy the layouts.json file to the WebDAV folder
  7. Now try opening the change layout UI and you might not see the newly added CustomLayout in the option, or you might if you do you will see option to switch to CustomLayout like this. If you dont see CustomLayout option go to next step
  8. In my case i was not able to see the option for CustomLayout so i looked in the firebug what is going on and it seems that when i go to Change Layout tab browser makes a request to http://localhost:10039/wps/mycontenthandler/!ut/p/digest!EWsjUKfQQnHqfLpBEXvbog/dav/fs-type1/themes/Portal7.0.0.2/system/layouts.json and the browser was using the cached copy for this file and as a result the changes in the layouts.json were not getting reflected. So i did disable the browser caching using firebug like this
  9. After that when i did refresh the page and i could see the new layouts.json being returned by the server and as a result i could see the change custom layout button

Responsive Web Design

Recently i read this very nice article about Responsive web design, which talks about how to build a responsive web application. Which means how to build a web interface that works well on every device from mobile to normal desktop to big screens. The author of the article talks about using CSS Media queries to find out the size of the screen and react accordingly by say using relative size in all the content and hiding and rearranging some of the content. Its quite powerful stuff. I also started learning about these couple of nice frameworks which helps us in building responsive web application

Mobile browser simulator in Rational Application Developer

The Rational Application Developer 8.* (I tried this feature in 8.0.4) has a concept of Mobile browser simulator. The way it works is if you have a web application running on say WebSphere application or WebSphere Portal server you can right click and say Run on Mobile Browser Simulator
The first time you do that it will install a Mobile Browser Simulator web application on the application server and open a web page like this, which would have mobile browser simulator with the mobile simulator pointing the page that your testing.

Adding support for JQuery as module in modular theme

I wanted to try adding a custom module in the Modular theme so i started reading the Using jQuery in a theme wiki page and i made few changes in the steps to get it working in my local. It seems that the source code for all the modules that are part of Portal 7002 modular theme is stored in the .war file in WebSphere\PortalServer\theme\wp.theme.modules\webapp\installedApps\ThemeModules.ear\ThemeModules.war\modules
Now we are not supposed to add any code in the WebSphere\PortalServer directory because when you install new fix or fix pack your changes could get overwritten, so i did create a new HelloModular.war file that has all the static resources as well as plugin.xml file that defines the static resources in my theme. You can download the HelloModular.war file with all the source code from here, i followed these steps
  1. First i did create HelloModular as Dynamic Web Application(basically a .war file) in RAD
  2. Then i did copy the jquery.min.js(I got the latest version from JQuery Web Site), jSquishy.css,jSquishy.js,jSquishy.jsp(I got the jSquishy related files from Portal Wiki) in the WebContent folder so that they will be available at the root of the HelloModular web application. This is how my project structure looks like
  3. After that i had to create plugin.xml that defines the jQuery and jSquishy related modules like this
    
    <?xml version="1.0" encoding="UTF-8"?>
    <?eclipse version="3.0"?>
     <plugin
       id="com.ibm.portal.jQuery.samples"
       name="jQuery module contributions"
       version="1.0.0"
       provider-name="IBM">
            <!-- jQuery js -->
     <extension point="com.ibm.portal.resourceaggregator.module" 
     id="jQuery_main_head">
      <module id="jQuery">
       <prereq id="wp_portal"/>
       <contribution type="head">
        <sub-contribution type="js">
      <uri value="res:/HelloModular/jquery.min.js" />
        </sub-contribution>
       </contribution>
      </module>
     </extension>
     <extension point="com.ibm.portal.resourceaggregator.module" 
     id="jSquishy_main_head">
      <module id="jSquishy">
       <prereq id="jQuery"/>
       <contribution type="head">
        <sub-contribution type="css">
      <uri value="res:/HelloModular/jSquishy.css" />
        </sub-contribution>
        <sub-contribution type="js">
      <uri value="res:/HelloModular/jSquishy.js" />
        </sub-contribution>
       </contribution>
       <contribution type="config">
        <sub-contribution type="markup">
      <uri value="res:/HelloModular/jSquishy.jsp"/>
        </sub-contribution>
       </contribution>
      </module>
     </extension>
    </plugin>
    
    The jquery.min.js is available at http://localhost:10039/HelloModular/jquery.min.js so i had to define it like res:/HelloModular/jquery.min.js while declaring it as a module.
  4. The last step was to create a profile_jquery.json like this
    
    {
     "moduleIDs" : [
      "wp_portal",
      "wp_theme_portal_7002",
      "wp_portlet_css",
      "wp_legacy_layouts",
      "wp_layout_windowstates",
      "wp_client_main",
      "wp_client_ext",
      "wp_theme_menus",
      "wp_one_ui_21",
      "jQuery",
      "jSquishy" 
     ]
    }
    
    After creating this profile_jquery.json i did upload it to /fs-type1/themes/Portal7.0.0.2/profiles/ folder in WebDAV
  5. Last step was to restart the changes for new module definition to get applied
  6. I did change the profile applied to one of my test page to profile_jquery.json by following steps defined in Changing the profile for a page
Now this is how my portal page looks like with squishy in both header and footer
When i looked at the request flow in the Firebug i can see the jQuery related resources being included. Follow the steps in Disabling combining of resource for debugging modular theme so that you can see individual request for resource.

Disabling combining of resource for debugging modular theme

If you look at the Net panel for all the resource requests generated by portal page in order to display a page you will notice that it generates very few requests. For example portal will figure out all the JavaScripts files required by the page combine them and return them in single request. Same thing for CSS files it will combine all of them and return a single response in compressed version like this
But when your debugging the theme you would want to disable this combining of requests to see what all files are getting loaded on a page as well as get uncompressed version of the files in order to do that you can set following trace string com.ibm.wps.resourceaggregator.CombinerDataSource.RemoteDebug=all
After setting the trace string when you refresh the same portal page you will see separate requests for resources on the page also you will notice portal is returning uncompressed version like this

Changing the profile for a page

The default profile for the Portal7002 theme is profiles/profile_deferred.json but you can override it at page level by setting the value of resourceaggregation.profile to different profile for example i can set it to profiles/profile_lightweight.json like this
Once you set that and access the page you will see that it does not have page action button any more.

Create custom Modular Theme

I am learning about the new Modular theme that ships as part of the WPS 7002 and i wanted to create a new modular theme so that i could play around with it. So these are the steps that i followed
  1. First i did connect to http://localhost:10039/wps/mycontenthandler/dav/themelist URL using the WebDAV client and there i could see csa2.theme folder for PageBuilder2 based theme and ibm.portal.7002Theme folder for the Modular theme, so i did download the ibm.portal.7002Theme folder on my local machine
  2. Then i did change name of that folder to webspherenotes.firstmodular
  3. Then i opened the metadata/localized_en.properties file and i did change the title of the theme like this
    
    description=
    title=FirstModular
    
  4. I also made changes in the metadata.properties file to change the value of com.ibm.portal.layout.template.href to point to a 2ColumnEqual layout that is inside the firstmodular theme like this. I also change the value of com.ibm.portal.layout.template.href
    
    com.ibm.portal.layout.template.href=dav\:fs-type1/themes/firstmodular/layout-templates/2ColumnEqual/
    com.ibm.portal.themetype=CSA2
    resourceaggregation.profile=profiles/profile_full.json
    
  5. After that i did upload the webspherenotes.firstmodular theme back to the WebDAV store like this
  6. Now when i logged into WebSphere Portal Admin console and went to Theme and Skin management i could see FirstModular theme in the list like this
  7. The last step was to assign the Portal 7.0.0.2 based skins to the FirstModular theme and then assign that theme to the page. When i tried accessing a FirstModular based page without assigning the Portal 7.0.0.2 based skin it looked like this
    But assigning correct skin solved the problem

Where is the code for modular theme

I started looking into how the WebSphere 7002 Modular theme looks like and first step was to figure out where is the source code for the theme. So it seems that the Modular theme is basically a Page Builder theme. The page builder theme code is normally divided into 2 parts one is static resource such as static html markup files, JavaScript and CSS files that code is stored in the WebDAV at http://localhost:10039/wps/mycontenthandler/dav/fs-type1/themes folder
If you dig dipper into the static resources you will notice that it has the same theme.html file as the entry point for the static markup and same layout and skin related folders
The difference in the theme.html file is couple of additional lines that are responsible for adding necessary JavaScript and CSS files. For example in the <head> you will see this line <link rel="dynamic-content" href="co:head"> this line is responsible for including necessary CSS that is required for the page and it also contains bare essential JavaScript. The following line is added at the end just above the closing <body> element, it is responsible for including necessary JavaScript in the page. <a rel="dynamic-content" href="co:config"></a> The basic idea is to include all the CSS at the top so that when the browser starts rendering markup it has all the classes and include all the JavaScript at the bottom so that the page starts rendering soon without waiting for downloading all the JavaScript The dynamic part of the resources are included using .jsp pages which are bundled in ThemeModules.ear which is available at WebSphere\PortalServer\theme\wp.theme.modules\webapp\installedApps

Installing new WPS 7.0.0.2 Modular theme

The WebSphere Portal 7.0.0.2 fixpack introduced a concept of Modular Theme which allows you to create themes using different module extensions to contribute to different areas of the page in order to provide flexibility, enhance the user experience, and maximize performance. I wanted to learn more about the Modular Theme so i followed these steps to install WPS 7.0.0.2 and install Modular theme, on my existing WPS 7.0 installation.
  1. When you install WPS on your machine by default it install WebSphere Application Server 7.0.11 which is not sufficient to install WPS 7.0.0.2, so first you must update your WAS, i did update mine by going to the latest version which is 7.0.21. My development environment is standalone so i had to install
    • 64-bit x86 AMD/Intel AppServer: WAS fix pack
    • 64-bit x86 AMD/Intel Java SDK: Fix pack for the JDK used by WAS
  2. Then i did install WebSphere Portal 7.0.0.2 Fix pack
  3. The Modular theme is not installed as part of WPS 7.0.0.2 fix pack installation process instead you will have to run following configuration task
    
    ConfigEngine.bat deploy-7002-theme -DPortalAdminPwd=password -DWasPassword=password
    
    This task took few minutes to complete. But once the task was completed i could see Portal 7.0.0.2 name in the theme list like this
Then i did change the theme to apply it to a page and i could see the new look and feel like this