Wednesday, December 26, 2007

Mobile Phone Site Detection

I got a Palm Centro mobile device for Christmas and it got me to thinking about the number of potential employers/clients that might use their mobile phones to view my resume or company site online. I've been quite happy with Google's mobile-versioned sites, like www.google.com/calendar/m and decided that I'd better follow the leader.

It wasn't as easy as you might think to find a good mobile-phone detection script online, at least not one that worked for the Centro. After four failed attempts, I found this one on brainhandles.com.




function checkmobile()
{
if(isset($_SERVER["HTTP_X_WAP_PROFILE"]))
return true;

if(preg_match("/iphone/i",$_SERVER["HTTP_USER_AGENT"]))
return false;

if(preg_match("/wap\.|\.wap/i",$_SERVER["HTTP_ACCEPT"]))
return true;

if(isset($_SERVER["HTTP_USER_AGENT"]))
{

$uamatches = array("midp", "j2me", "avant", "docomo", "novarra", "palmos", "palmsource", "240x320", "opwv", "chtml", "pda", "windows\ ce", "mmp\/", "blackberry", "mib\/", "symbian", "wireless", "nokia", "hand", "mobi", "phone", "cdm", "up\.b", "audio", "SIE\-", "SEC\-", "samsung", "HTC", "mot\-", "mitsu", "sagem", "sony", "alcatel", "lg", "eric", "vx", "NEC", "philips", "mmm", "xx", "panasonic", "sharp", "wap", "sch", "rover", "pocket", "benq", "java", "pt", "pg", "vox", "amoi", "bird", "compal", "kg", "voda", "sany", "kdd", "dbt", "sendo", "sgh", "gradi", "jb", "\d\d\di", "moto");

foreach($uamatches as $uastring)
{
if(preg_match("/".$uastring."/i",$_SERVER["HTTP_USER_AGENT"]))
return true;
}
}
return false;
}
#example
if(checkmobile())
{
header('Location: http://www.yoursite.com/m/index.html');
exit;
}

if(!checkmobile())
{
header('Location: http://www.yoursite.com/');
exit;
}
?>



It's a PHP script, of course. For my purposes, I just created an index.php file and set "DirectoryIndex index.php index.html". The first thing the visitor hits is the php file, which either redirects to the main index.html file or the mobile version in the "m" directory.

Tuesday, November 06, 2007

Open Source Project Management

Do's and Don't


Use Open Source Judiciously

We all know the benefits of using open source, but what are the pitfalls? If you have been asked to build a professional, production-ready, mission-critical, customer-facing application, you probably don't want to go bleeding edge.

You need to ask yourself if the project is ready for prime-time. Don't be fooled by a glossy, over-hyped project page. Dig deep. Find out
how many people are really using the project. Look at the mailing list archives. Is it active enough? Is is full of unanswered questions? Check out the source from subversion. Review the check-in history. If time permits, read the code.

For short-timeframe, mission-critical projects, I use what I call The Book Test. Any project for which an O'Reilly book has been written has been vetted and tested to my satisfaction. If three or more books on the subject have been written and are available from your local bookstore, you can bet that the technology is ubiquitous. Tried and true open source projects include Linux, Apache web server, Python, PHP, and Ruby. This litmus test will eliminate a majority of the projects you find on source forge, so this shouldn't be your only test. But it's a good place to start.

Saturday, November 03, 2007

Wireless Problems Upgrading Ubuntu to Gutsy

I ran into trouble this week while installing Ubuntu Gutsy on some older hardware for my five year old son Kadin. He'd been using a prism2 based wireless card with success under older releases of Ubuntu for over a year, so I knew that the card was supported. But after completing the fresh install, network manager couldn't find my home network.

Matt Hartley suggested that the problem was with Network Manager in this post, but I followed his recipe, winstalling wicd as a replacement, and it didn't help my situation. Wicd had the same problems detecting my home network.

ifconfig reported two interfaces for my wireless card---wifi0 and wlan0. Some suggested blacklisting hostap_pci (and others) to remove conflicting drivers which might be confusing network manager. The best suggestion I found early on---which didn't work for me at the time---was to install the linux-wlan-ng package. This was the right thing to do (the needed prism2 driver is installed), but unfortunately, this didn't work either. Perhaps some additional configuration was needed?

Since, according to some, the real problem was with Network Manager, and since it was working well for us under Feisty, I decided to pin back those packages using apt.

I added the following to /etc/apt/preferences:

Package: *
Pin: release a=gutsy
Pin-Priority: 700

Package: network-manager
Pin: release a=feisty
Pin-Priority: 800

Package: network-manager-gnome
Pin: release a=feisty
Pin-Priority: 800

Then I duped all of the repository entries in /etc/apt/sources.list and changed each "gutsy" to "feisty" so that apt can find modules in both repositories. Then I ran:

sudo apt-get install network-manager network-manager-gnome

This successfully downgraded and pinned the two Network Manager packages to the versions that are available in Feisty Fawn.

I was really pleased that the downgrade went so smoothly, as I've gotten trapped in dependency hell attempting similar feats in the past. Sadly, though, my problem remained! Perhaps Network Manager was not at fault after all.

linux-wlan-ng seemed to be the right solution for my card, so I decided to build from source from the trunk.

svn co svn://svn.shaftnet.org/linux-wlan-ng/trunk

Then I built and installed it and followed the directions in the README to setup, which included setting values in /etc/wlan/wlan.conf. I added my home network's SSID and changed the following entry:

# for some reason, this value was set to N --- seemed wrong to me
WLAN_SCAN=y

I also added the suggested values to /etc/rc.local (for modprobing the correct driver---prism2_pci in my case).

I remembered seeing wlan0 aliases under /etc/modprobe.d, so I grepped for matches and found this file:
/etc/modprobe.d/linux-wlan-ng

The contents are shown below. The "alias wlan0 prism2_pci" line was commented so I removed the comment.

# Aliases to tell insmod/modprobe which module to use when bringing up the
# wlan0 interface.

# Uncomment the line corresponding to the type of prism2 device you have.
alias wlan0 prism2_pci
#alias wlan0 prism2_usb
#alias wlan0 prism2_cs
#alias wlan0 prism2_plx

# this allows network manager to manipulate the device
options p80211 wlan_wext_write=1

I rebooted and network manager was able to find my home network, as well as a number of my neighbors wireless network. In hindsight, I wonder if, after installing linux-wlan-ng as a package, I had just removed the comment from this file and WLAN_SCAN=Y, my troubles would have washed away? If you are having similar problems, I would try that first before compiling the latest driver from source.

Thursday, November 01, 2007

I posted a question to the Wicket users group a few days ago on how to best deal with frames, in particular how to pass arguments between a tree view in one frame and a list view in another. I didn't get any takers.

I finally determined that there was no good way other than to use plain-old javascript to push values from the tree up to the parent frame, where they are dispersed to the other frames.

I wanted to use LinkTree, but really didn't need most of the features other than the look and feel. I just needed to be able to insert an onclick link that would pass a value to my parent frame.

After a lot of digging I came up with a solution that worked, but seems verbose for the job I'm attempting. My question for the reader is this: Can you propose a more concise solution?


public class CategoryTree extends LinkTree
{
@Override
protected Component newNodeComponent(String id, IModel model)
{
return new LinkIconPanel(id, model, CategoryTree.this)
{
private static final long serialVersionUID = 1L;

protected void onNodeLinkClicked(TreeNode node, BaseTree tree, AjaxRequestTarget target)
{
super.onNodeLinkClicked(node, tree, target);
CategoryTree.this.onNodeLinkClicked(node, tree, target);
}

protected Component newContentComponent(String componentId, BaseTree tree, IModel model)
{
Label l = new Label(componentId, model)
{
private static final long serialVersionUID = 1L;

@Override
protected void onComponentTag(ComponentTag tag)
{
super.onComponentTag(tag);
tag.put("onclick", "parent.notifyViews(this.getAttribute('catalogid'))");

}
};

DefaultMutableTreeNode n = (DefaultMutableTreeNode)model.getObject();

if (n.getUserObject() instanceof EcCategoryTreeNode)
{
EcCategoryTreeNode ec = (EcCategoryTreeNode)n.getUserObject();
l.add(new SimpleAttributeModifier("catalogid", "" + ec.getId()));
}

return l;
};
};
}

public CategoryTree(String s, TreeModel tm)
{
super(s,tm);
this.setLinkType(LinkType.REGULAR);
}

}

Wednesday, October 24, 2007

Wicket and HTML CSS attributes

JAVA open source web frameworks are everywhere. And the documentation is everywhere, which is not always a good thing. Especially when what you want is to go to a particular somewhere to find all of the documentation. Mailing lists are great, but you may end up the first to post on your problem. As long as you don't need to get it done---you have no problem.

I didn't find a post or documentation that addressed the problem I was facing today.

In this case, the task I had to complete was simple: render an HTML table, alternating the background color for each row. I've implemented this pattern a half dozen times before using Java Servlets, ASP, PHP, OpenLaszlo and even a Struts-like home-grown system when working for a previous employer. In the end, it has always come down to plain old HTML and CSS, programatically assigning one of two CSS classes to a table row. (OpenLaszlo was an exception.)

This time around I'm using Wicket. I must admit that I'm not to crazy about heavy, rule-wielding web frameworks because they often improve on the way you accomplish big tasks, but make it exponentially more difficult to do the simple ones that don't fit nicely into their design. Is this true of Wicket? It's too early for me to say, but I'm certainly interested in finding the answer to that question.

The early verdict: So far, Wicket seems to do a pretty decent job doing what a framework ought to do.

Wicket keeps the presentation concerns separate from the model. In practical term this means that the web stuff (javascript, CSS, HTML) is kept independent from the JAVA source. No mixing-and-matching within a single file. Wicket does a lot more, but I'll leave it to others to sell you on it.

For a non-Wicket web app, the type of logic required to provide alternating rows is normally applied in a for loop, using mod to determine if you are dealing with an even or odd row. In a scripting language, the whole operation takes all of two lines of code.

So I set out to do the same for Wicket. Fifteen minutes of googling and I found How to modify an attribute on a HTML tag. Sounded promising, but upon reading, looked verbose. (Must we really use a separate model and view to create a simple attribute pair?)

Reading this page, it wasn't apparent how the example would lend itself to my case. However, with some experimentation, I was able to retrofit the familiar pattern onto the Wicket frame.



<tr wicket:id="catalog" class="unknown">
<td wicket:id="name"></td>
<td wicket:id="sku"></td>
<td wicket:id="manufacturer"></td>
...
</tr>


ListView listview1 = new ListView("catalog", catalogItems.getProductWindow())
{
public final String style1 = "row_light";
public final String style2 = "row_dark";

protected void populateItem(ListItem item)
{
Map rec = (Map) item.getModelObject();

item.add(new Label("name", (String)rec.get("cttm_display")));
item.add(new Label("sku", (String)rec.get("cttm_partnumber")));
item.add(new Label("manufacturer", (String)rec.get("mfg_name")));
...

if (item.getIndex() % 2 == 0)
item.add(new AttributeModifier("class", new PropertyModel(this, "style1")));
else
item.add(new AttributeModifier("class", new PropertyModel(this, "style2")));

}
}



I used PropertyModel to get around having to create beans to hold the two strings representing the CSS classes. Does the reader see a better solution?

Saturday, May 05, 2007

Indexing and Ranking

It is not easy to get a high Google ranking.

A few weeks back I revived my auction website, www.fleahaus.com. I say revived because I originally developed the site a few years back. It was born, it floundered, it died a slow and agonizing death.

So I got a case of the what-if-I-had-done-it-a-little-differentlies and decided to administer CPR. For example, I initially designed the site show items by region, a feat accomplished by using subdomains. Criagslist uses this very concept currently---based on the geographical data you submit, you see a city based portal. For example---when I visit craigslist, I am directed to http://tulsa.craigslist.com because I live in the Tulsa area. My original site used the same pattern.

This time around I threw out the city based portal idea and introduced a local search button. This ultimately accomplishes the same goal---giving users a way to find items that are geographically near to them---something very important to those who want to sell items too large or heavy to ship by mail. This gave me a change to try out Google's GeoLocate APIs.

After the face-lift and new features were done, it took three weeks before Google indexed my site. This is apparently not too bad.

The delay in indexing during that three weeks was a major concern for me--- especially because I had so much trouble getting the original site indexed. I partially attribute the failure of the original site (www.communitybuy.com for the record) to my inability to get Google to index the darn thing.

I'm still not quite sure why communitybuy.com failed to make it into the index. Perhaps because I didn't put effort into back-linking at that time? Formatting issues? Only the crawler knows for sure.

Google's webmaster tools have been beneficial this go-round. I have been able to check crawl rates, get indexing status, and more.

So one milestone met: www.fleahaus.com is indexed. Next, find a way to move up the rankings.