iShare :) Baby in 6 hours!!!

iShare

iShare

Before starting the success story one must always tell others the difficulties he faced. So let me start from that. December 22nd 2008, I got my api key and secret key from SlideShare. I was playing a little bit with them but I did not have any idea what tool to create using them. Then I was browsing the developers page of SlideShare and found few apps for SlideShare. I knew they have an app for Linkedin and a tool for PowerPoint but I never knew they have one for Facebook. Damn!!! I made up my mind to create an app for Orkut then. But what app to make??? These came in my mind “I have used iLike which is to share music and videos, why shouldn’t I create an app to share slideshows.” I named it right away as “iShare” just because the idea was from iLike :) No offence. Then I went through Opensocial documentations I loved it immediately because it’s completely using JavaScript :) Now I have to take an important decision, which programming language should I use? Java, PHP or Rails? Rails I don’t have depth knowledge in it so now PHP or Java? Java I can say I will act like a pro if I have javadoc in my hands but I must accept it is really slow. What about PHP? Even in PHP I don’t have much of experience and knowledge but PHP is damn fast. So I decided to choose PHP for server side scripting and JavaScript for client side scripting.

Then I downloaded SlideShare api for PHP. It is known as SSUtil (I guess SlideShare Utility). I must say the only file I like very much in the api is readme file. In the first glance on readme file I got what are all stuffs my app can do in Orkut. So I made final decision on my app. My app must let user to search for slideshows from SlideShare using tags or user ids and user must also be able to add slideshows also using urls (Example: user finds some slideshow in SlideShare and likes it and also wants to add in his Orkut profile. He need not come and search for his slideshow in Orkut rather he can copy paste the url and add). And it is going to have one more feature which is still underdevelopment due to some issues with OpenSocial api will likely to be implemented before beta release :) And thats a surprise ;) Now I made decisions about everything. I started with setting up my localhost and created the skeleton with the features in 2 hours in my laptop. But this code was dropped later you can find the reason when you read more here. Now it is time for writing code using OpenSocial api to make a container for my app. And got done with it in less than 10 minutes. Damn!!! Even though I have a website (which I used for blogging) I don’t have a hosting service :( That’s really sad. Three things made me sadder 1) I don’t have a credit card to buy hosting space 2) I can’t afford so much money right now for hosting space 3) Reason for (2) is I don’t have any money on me and I am bankrupted :( Alright it is time to find a free hosting service provider. I searched I searched I searched and found only few good one with no free ad banners in my site. I tried with each one of them but my code was not working and did not show any errors. I was confused a lot. Fortunately one of the service provider has a channel in irc. I went there, posted my question and asked for reason. And I got the answer “allow_url_fopen = off” by all free hosting service providers :( Since I need to get data from SlideShare.net using its api I needed that feature badly. Then I decided to buy hosting space for minimum period with affordable price. So started searching for service providers again and asked opinion from my friends too. Different opinions but I was not satisfied either by the price or the features. If the price is affordable then features are bad and vice versa. Finally I found Yahoo Small Business with a reasonable price and even GoDaddy. I decided to stick with Yahoo. What is the use of taking this decision? Even then I did not have any money or even now I don’t :( I decided to borrow from someone but I was embarrassed to ask. I decided to ask from very close friends. So went to Sidhartha but unfortunately he had no credit card or money on him and his dad was out of station. I was even more embarrassed and finally I asked Kannan and he was ok for paying $26+.

December 24th 2008, Morning Kannan called me up and gave his credit card details and I successfully registered an account in Yahoo for 3 months period. And finished the setups and configurations. Uploaded my code and started running. Unfortunately I got errors once again. I was really confused and did not know what to do. I posted my errors in #php channel in irc and got response saying “it might be because of PHP 4″. I was really shocked to hear this. I coded completely in PHP 5 and the error was not from my code unfortunately from SSUtil.php. Only good thing I made was registering with Yahoo with 30 days money back guarantee on dissatisfaction. I wondered how come a big organization like Yahoo still has PHP 4 and with no option to migrate to PHP 5. All other service providers have the feature of migrating from PHP 4 to PHP 5 and vice versa but WHY YAHOO DON’T HAVE???? I immediately mailed Yahoo customer care to cancel my account and asked for cash back. I was helpless once again. I didn’t wanna go and ask anyone for money again :( And I don’t have money to pay Kannan back :( Atleast I got 3 months time to pay him back but right now I don’t have money to buy hosting space. I almost dropped my idea of creating app. I went to GoDaddy where I registered my domain name http://sprasanna.com to edit the configuration back to point my tumblr blog. And suddenly I saw something when I logged into my account saying “Free hosting space.” I thought not again but it said it comes with my domain name because of the promo coupon code I used when I bought this domain name. I didn’t want to waste this and wanted to give it a try. Successfully I finished configuring and saw the php.ini file and found “allow_url_fopen = on”. I must say thats one of my best moments in my life. I was so happy. I uploaded my files and started running. Everything was fine except for the free ads banners :( Except that everything was fine. It infact worked well inside Orkut also. But that free ads banners were so annoying. I can’t say people whoever uses my app that the ads are because of free hosting service. So I decided to drop this code and restart from the scratch once again. But anyways I need hosting and I don’t wanna lose this free space I got but I didn’t want ad banners in my app. Since this code was completely written using PHP and only few lines of JavaScript I decided to use complete AJAX and restart once again from the scratch. It was already 4 AM 25th December 2008. Christmas eve. I needed some sleep since I have been working a lot with less sleep. I went to bed saying “Merry Christmas” to myself.

I got up at 1 PM 25th December 2008. Really bad and such a waste of time I spent in my bed. I finished my daily routines and other works started coding right from the scratch once again. But this time completely using AJAX stuffs and much more usage of OpenSocial api. I handled the most possible way to avoid ad banners since I have all my PHP files hosted from GoDaddy so every response from the server comes with the ad banners attached. So I added ‘<message>’ to the both ends of responses. So I can easily get my responses using JavaScript like this response.split(‘<message>’)[1] Wow and my ad banners are gone now :) So finally found a way to avoid ad banners from free hosting services. I started speeding up my work and completed the coding part. Here I faced a big problem Orkut gets everything via proxy, say JS file, CSS file, images, videos, audios, etc etc and slideshows do come under this list. When everything else was loading well, slideshows alone were not loaded. And I found the reason was proxy and the transaction was not intialized by static.slideshare.net server. So I posted a question to SlideShare developers list asking is there any fix this but yet no reply from them :( So I had to find another way. And I found it. If the url is posted without this “http://3.sandbox.gmodules.com/gadgets/proxy?refresh=86400&url=&#8221; the embedded slideshow is playing properly. So I decided to change it dynamically and it works well :) So now the app is ready for testing. Infact I even decided to release the beta version and I was going through the Orkut developer guidelines and it reminded me I was not done with one more feature. Yeah I totally forgot about the “Updates” feature in Orkut (whenever you do something with your apps it can be posted to your friends as your updates, it will just increase the apps’ reputation exponentially and makes it popular). So I started working on it. But fortunately it kept failing again and again. And after few struggles I found I was posting the title part alone and I forgot about body. Even after body it kept failing because I had the slideshows image in it. So decided to drop the image and tried, Voila!!! it started working :) Now everything works fine and the app is ready for beta release :) But still I have one more feature which I would love to do often if it is added and I am on it now :)

26th December 2008, Now I am giving out small demo to my friends in the web to test the stability and look out for bugs. Every now and then GoDaddy server goes down I have mailed them regarding this. So the baby is ready now to walk in the web world :) I must say this whole coding part took six hours for me to complete. But there is no artistic magical designing works in my app. It is simple and easy to use. Even a novice can come and use this without any trouble and that is all I want. Lets see whether this app makes any magic :)

Overriding the default addressLayout.jsp

This week I had to override the existing addressLayout.jsp. At first I was very confused in how to do it so I discussed it with Daniel, Brian and I sent an mail to the dev list. Finally Ben gave me an wonderful example and using that I successfully overriden the default addressLayout.jsp. Actually I tried that before Ben sent me the example but by mistake I extended SimpleFormController for portlet instead of the servlet one. Finally Daniel noticed it and corrected it and I have successfully overridden it. I had to create my own controller since the controller which the portlets usually extends doesnt support formView.  But I think I am wrong in the moduleApplicationContext.xml part which is given below. Must show that to Daniel and cross check it. Or if you find something wrong in that please let me so that I can correct it.

Here’s a screenshot:

So this is how it overrides.

This is how i modified my moduleApplicationContext.xml

<bean id=”addresshierarchyUrlMapping” class=”org.springframework.web.servlet.handler.SimpleUrlHandlerMapping”>
<property name=”mappings”>
<props>
<prop key=”**/addressLayout.portlet”>addressLayoutPortletController</prop>
</props>
</property>
</bean>

<bean id=”addressLayoutPortletController” class=”org.openmrs.module.addresshierarchy.web.controller.AddressLayoutPortletController” >
<property name=”commandName”><value>addressLayout</value></property>
<property name=”commandClass”><value>org.openmrs.module.addresshierarchy.web.controller.AddressLayoutPortletController</value></property>
<property name=”formView”><value>/module/@MODULE_ID@/portlets/addressLayout</value></property>
<property name=”successView”><value>/module/@MODULE_ID@/portlets/addressLayout</value></property>
</bean>

Still I am not done with the backend. I have few doubts hope I will finish it within this week.

But I do face a problem with this. Whenever newPatient form reloads with some validation errors my addressLayout.jsp is missing there.

If someone has solution or suggestion please feel free to drop it here.

Codes for review

Tree builder JS

var TreeBuilder = {
buildTreeNodes:function (dataObjs, treeParentNode){
var arr=new Array("Start","Country","State","Sub Location1","Sub Location2","Sub Location3","Sub Location4","Sub Location5","Sub Location6","Postal Code","Longitude","Latitude");

for(var i=0; i<dataObjs.length;i++){
var typ = dataObjs[i].typeId;
var dumm = parseInt(typ);
var titl = dataObjs[i].title+”( “+arr[dumm]+” )”;
var node = dojo.widget.createWidget(“TreeNode”,{
title:titl ,locationName:dataObjs[i].title, locationId:dataObjs[i].locationId , typeId:dataObjs[i].typeId , parentId:dataObjs[i].parentId
});
treeParentNode.addChild(node);
treeParentNode.registerChild(node,i);
if(dataObjs[i].children){
this.buildTreeNodes(dataObjs[i].children, node);
}
}
},
buildTree:function (treeDat){
myTreeWidget = dojo.widget.createWidget(“Tree”,{
widgetId:”myNewTreeWidget”
});

this.buildTreeNodes(treeDat.treeNodes,myTreeWidget);
var treeContainer = document.getElementById(“myWidgetContainer”);
var placeHolder = document.getElementById(“treePlaceHolder”);
treeContainer.replaceChild(myTreeWidget.domNode,placeHolder);
DemoTreeManager.init();

}

};

Context menus and its actions

var DemoTreeManager = {
djWdgt: null,
myTreeWidget: null,
addTreeContextMenu: function(){

var ctxMenu = this.djWdgt.createWidget(“TreeContextMenu”,{});
ctxMenu.addChild(this.djWdgt.createWidget(
“TreeMenuItem”,{caption:”Add Location Component”,
widgetId:”ctxAdd”}));
ctxMenu.addChild(this.djWdgt.createWidget(
“TreeMenuItem”,{caption:”Edit Location Component”,
widgetId:”ctxEdit”}));
ctxMenu.addChild(this.djWdgt.createWidget(
“TreeMenuItem”,{caption:”Delete Location Component”,
widgetId:”ctxDelete”}));
document.body.appendChild(ctxMenu.domNode);
/* Bind the context menu to the tree */
ctxMenu.listenTree(this.myTreeWidget);
},

addController: function(){
this.djWdgt.createWidget(
“TreeBasicController”,
{widgetId:”myTreeController”,DNDController:”create”}
);
},
bindEvents: function(){
/* Bind the functions in the TreeActions object to the
context menu entries */
dojo.event.topic.subscribe(“ctxAdd/engage”,
function (menuItem) {
TreeActions.addNewNode(menuItem.getTreeNode(), “myTreeController”); }
);
dojo.event.topic.subscribe(“ctxDelete/engage”,
function (menuItem) { TreeActions.removeNode(menuItem.getTreeNode(),
“myTreeController”); }
);
dojo.event.topic.subscribe(“ctxEdit/engage”,
function (menuItem) { TreeActions.editNode(menuItem.getTreeNode(),
“myTreeController”); }
);
},
init: function(){
/* Initialize this object */
this.djWdgt = dojo.widget;
this.myTreeWidget = this.djWdgt.manager.
getWidgetById(“myNewTreeWidget”);
this.addTreeContextMenu();
this.addController();
this.bindEvents();
}
};

Its actions

var TreeActions = {
addNewNode: function(parent,controllerId){
this.controller = dojo.widget.manager.getWidgetById(controllerId);
if (!parent.isFolder) {
parent.setFolder();
}
var arr=new Array("Country - 1","State - 2","Sub Location1 - 3","Sub Location2 - 4","Sub Location3 - 5","Sub Location4 - 6","Sub Location5 - 7","Sub Location6 - 8","Postal Code - 9","Longitude - 10","Latitude - 11");
var typeid = parent.typeId;
var str="Enter corresponding code for the location type \n";
for(var i=typeid;itypeid){
if(dummy<12){
var titl = prompt("Enter the location name","");
if(titl==""){
alert("Enter valid name");
}
else{
var rad = this.controller;
AddressHierarchy.createLocation(titl,parseInt(dummy)-1,parent.locationId,function(data){
var arr=new Array("Start","Country","State","Sub Location1","Sub Location2","Sub Location3","Sub Location4","Sub Location5","Sub Location6","Postal Code","Longitude","Latitude");
var dumm = parseInt(data[2]);
var titl = data[0]+"( "+arr[dumm]+" )";
var res = rad.createChild(parent, 0, { title: titl,locationName:data[0], locationId : data[1],typeId : data[2], parentId : data[3] });
})

}}
else{
alert(“Invalid location type”);
}
}
else{
alert(“Invalid location type”);
}

},
removeNode: function(node,controllerId){
if(node.title!=”Start”){
if (!node) {
alert(“Nothing selected to delete”);
return false;
}
else{
var name = node.locationName;
var parid = node.parentId;
this.controller = dojo.widget.manager.getWidgetById(controllerId);
var rad = this.controller;
if(!confirm(“Are you sure you want to delete “+name)){
return false;
}
AddressHierarchy.deleteLocation(parid,name,function() {
var res = rad.removeNode(node, dojo.lang.hitch(this));
})

}
}
else{
alert(“Cannot remove”);
}
},
editNode: function(node,controllerId){
if(node.title!=”Start”){
if (!node) {
alert(“Nothing selected to edit”);
return false;
}
else{
var oldname = node.locationName;
var parid = node.parentId;
this.controller = dojo.widget.manager.getWidgetById(controllerId);
var rad = this.controller;
var newname = prompt(“Enter the location name”,oldname);

if(newname!=null){
AddressHierarchy.editLocation(parid,oldname,newname, function(){
var arr=new Array(“Start”,”Country”,”State”,”Sub Location1″,”Sub Location2″,”Sub Location3″,”Sub Location4″,”Sub Location5″,”Sub Location6″,”Postal Code”,”Longitude”,”Latitude”);
var dumm = parseInt(node.typeId);
var titl = newname+”( “+arr[dumm]+” )”;
node.edit({title:titl , locationName:newname});
})}
}
}
else{
alert(“Cannot Edit”);
}
}};

JSON input from servlet

function loadData(){
$.getJSON("${pageContext.request.contextPath}/moduleServlet/addresshierarchy/addressTree",
function(data){
TreeBuilder.buildTree(data);
});
}

HTML code before the tree loads

<div  id=”myWidgetContainer”>
<span id=”treePlaceHolder”
style=”background-color:#F00; color:#FFF;”>
Loading…
</span>
</div>

Screenshots for review

This is how it looks in the admin page now.

This is how the tree looks when there is no location components.

On right click on the tree these options are given out. Brian has asked me to do something like even the options are given out according to the user previleges.

When user selects “Add Location Component” a pop up with list of components and its code is given. Its enough to enter the code by user.

The location component is created and it displayed with the location type in the braces.

Prompt box user gets when he selects “Edit Location Component”.

Confirmation box user gets when he selects “Delete Location Component”.

Sixth week of coding

This week I had to work bit harder because i had to take off for four days from the project since I had to attend my cousin’s marriage.  At the start of the week Brian suggested a new concept for easy user interface. A javascript tree to maintain the address hierarchy even Daniel thought that would be good and pretty useful.

As they wished I designed the javascript tree with a small css bug (an extra line could be easily removed on a small research). Here are the screen shots

This is default view. You cannot delete or edit that “Start” link.

This is the tree view.

Three options you get when you right click on a link.

Popup you get when select “Add Location”.

Popup you get when you select “Edit Location”.

Confirmation dialog you get when you select “Delete Location”.

I use a Java string parsed into Json array to build this array. But I feel loading the tree the with a large number of locations will take pretty long time but once it is loaded this is the best way for an user to create a location rather than the old way plus maintaining the locations is also very easy.

I hope my mentor and backup mentor like this tree. I am doing a small research for the second part of my project. It will take 2 or 3 days and on 10th I have my project review. After that I will start coding for the second part.

P.S : I welcome any alternative suggestion for the Json array. I am not sure how to build a js tree without an array.

Fifth Week of Coding

I have almost completed my first half project to be submitted for mid term evaluation.

I have put few screenshots here and explanation for it.

This is the create hierarchy page. It has auto completion text boxes which helps admin to create locations easily. There’s also a location counter in this page showing the number of locations in the table.

This is the manage hierarchy page. When it loads it looks like this. You can start editing or deleting by selecting a country.

The hierarchy is changed as you choose a location. If a hierarchy skips a location type then that particular drop down menu will be disabled.

As you can see in screenshots the hierarchies are changed as the locations are changed.

Even manage hierarchy page has a location counter.

On choosing edit the submit button is renamed to “Edit”. And the user will be provided radio buttons and corresponding disabled textboxes. On selecting a radio button the corresponding textbox will be loaded with the selected value in the drop down menu. If the drop down menu is disabled then the textbox will not be enabled on selecting the radiobox. User can edit the location in the text box and click Edit button.

Delete also similar to Edit but this will not give out textboxes. User can click the radio button and click the Delete button.

Yesterday when Brian tried to work with my module he faced problems in manage hierarchy page. Still I could not figure out the reason. I tried the module in few other systems it worked fine there. Even in Internet Explorer 6 it worked fine. I have done this autocomplete thing today havent updated my code yet in the repository since there’s some problem in the repository.