<!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
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
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.
- Install Ripple extension in Chrome
- Start the Google Chrome browser with its access to local file system by executing
chrome.exe -–allow-file-access-from-files
- Then Right click on the Ripple symbol and say Manage Google Extensions, on the next screen check
Allow access to file URLs
check box - Now open index.html from the phoneGap application using file URL and enable Ripple for it
- 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
- 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 copied the content of Geolocation.html in the index.html page
- 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>
- 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.
-
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 usegeo fix -121.9754144 37.5669038
command to set the longitude and latitude 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
- 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
- I did download the
jquery.mobile-1.1.0.zip
from JQuery Mobile Download page - I did unzip the
jquery.mobile-1.1.0.zip
inc:\software
folder - 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. - 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>
- Next change your index.html page to look like this
My application has one JQuery Mobile page whose content is defined using<!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>
div
with id equal topage1
. The div with data-role equal to content defines the content of the page. By default it only has emptyul
element, i am usingonDeviceReady()
JavaScript function to read device property and add them to the list.
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
Thepackage 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"); } }
onCreate()
method is callingsuper.loadUrl("http://wpcertification.blogspot.com");
so thathttp://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
Take a look at XML comments in this file for syntax on how to allow access to external URLs<?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>
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.
- First connect to
http://localhost:10039/wps/mycontenthandler/dav/skinlist
WebDAV URL and you will see list of all the existing skins. - 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 - Rename the
ibm.portal.7002NoSkin
folder towebspherenotes.firstmodularskin
- 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
- Change the content of the theme_en.html to look like this
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<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>
First Modular Skin -
to the portlet title - Then copy the
webspherenotes.firstmodularskin
folder back to thehttp://localhost:10039/wps/mycontenthandler/dav/skinlist
like this - 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.
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.
- Create a folder named
CustomLayout
under/fs-type1/themes/webspherenotes.firstmodular/layout-templates/
folder - 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 - Copy the newly created CustomLayout folder to the
/fs-type1/themes/webspherenotes.firstmodular/layout-templates/
in WebDav storage like this - 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 -
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':'' }
- After make changes copy the layouts.json file to the WebDAV folder
- 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
- 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 - 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
- First i did create HelloModular as Dynamic Web Application(basically a .war file) in RAD
- 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
- After that i had to create plugin.xml that defines the jQuery and jSquishy related modules like this
The jquery.min.js is available at<?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>
http://localhost:10039/HelloModular/jquery.min.js
so i had to define it likeres:/HelloModular/jquery.min.js
while declaring it as a module. - The last step was to create a profile_jquery.json like this
After creating this profile_jquery.json i did upload it to{ "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" ] }
/fs-type1/themes/Portal7.0.0.2/profiles/
folder in WebDAV - Last step was to restart the changes for new module definition to get applied
- 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
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
- First i did connect to
http://localhost:10039/wps/mycontenthandler/dav/themelist
URL using the WebDAV client and there i could seecsa2.theme
folder for PageBuilder2 based theme andibm.portal.7002Theme
folder for the Modular theme, so i did download theibm.portal.7002Theme
folder on my local machine - Then i did change name of that folder to
webspherenotes.firstmodular
- Then i opened the metadata/localized_en.properties file and i did change the title of the theme like this
description= title=FirstModular
I also made changes in the metadata.properties file to change the value of - After that i did upload the
webspherenotes.firstmodular
theme back to the WebDAV store like this - 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
- 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
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
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.
- 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
- Then i did install WebSphere Portal 7.0.0.2 Fix pack
- 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
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 thisConfigEngine.bat deploy-7002-theme -DPortalAdminPwd=password -DWasPassword=password