Contact Us

If you are interested in our services leave your contact details below and our sales representatives will contact you.

The organization which you represent
Email address we will use to contact you
Longer contact form…
 
  • About

    mFabrik Blog is about mobile and web software development, open source and Linux. We tell exciting tales where business, technology, web and mobile convergence.

    Creative Commons License
    This work is licensed under a Creative Commons Attribution 3.0 Unported License.

Cross-platform mobile application development and payments



We have been piloting multi-platform mobile application development and payments in few client projects. Target platforms usually include iPhone, Android, Blackberry and Nokia Series 60. Also there are two notable usual cases which need to be specially handled

  • Image uploads
  • Payments for subscribed content

Sounds easy, right? Well it isn’t… Below are some notes for our due diligence work which you fellow developers might find interesting.

SDKs

Mobile phone vendors are jealously and don’t want to co-operate with each other. Building application which works in all handsets is major headache.

We found some reasonable candidates for cross-platform mobile development doing HTML and Javascript. HTML and Javascript pages are converted to native application using a wrapper technology (a.k.a. appaccelerator). Doing Flash Lite or Java ME can be pretty much forgotten nowadays as they won’t run on the most hyped platform, iPhone. Flash Lite has poor support for anything except content authoring due to primitive and limited APIs. Java ME provides horrible user experience.

(X)HTML is the only common language spoken by mobile phones. Thus, there has been a rise of “appaccelerators”, technologies which allow to create mobile applications with HTML(5) and Javascript.

  • Phonegap: iPhone, Android, Blackberry and possibly S60 in the future. Pluses: BSD license, very active community. Minuses: bad documentation, difficult deployment process.
  • Titanium: iPhone, Android. Pluses: Professional, Apache license. Minuses: Too tightly coupled with Appacclerator Inc. company.
  • Rhomobile: iPhone, Android, Blackberry, S60, Windows Mobile. Pluses: Professional, tries to build open source community, the widest platform support. Minuses: Dual licensing and tightly coupled with Rhomobile Inc.
  • Nokia Web-runtime: Nokia S60 and some other Symbian based phones. Pluses: Professional, good documentation. Minuses: Not open source, impossible to extend, Nokia has little interest to make this cross-platform, Nokia doesn’t like updating old models and web-runtime is useable only in the latest S60 5th edition models.
  • Palm Pre supports web applications natively. However Palm Pre application business is still taking a shape.

All these wrap the browser component (WebKit) and provide some extra Javascript APIs when your web pages as executed under the application mode.

  • Locationing
  • Contacts
  • SMS
  • Client-side database
  • and so on…

Rhomobile has little different use cases  from the rest of the bunch as it provides client-side programming using Ruby and less focuses on Javascript/web applications.

Payments and in-application purchases

There are four major way to do mobile payments “inside” the application for bought content and subscriptions. The price tag on the application itself is left out on this discussion as the application stores themselves take care of it.

  • Credit card
  • SMS
  • App Store payment (thus far Apple only)
  • Direct operator payments – you have a service provider (Bango) which can directly charge items to the operator phone bill based on handset identification.

App Store payment is the most attractive as it provides the best end user experience.  It allows you to use App Store payment mechanism inside the application. It is safe and no need to hassle with external payment providers. However, App Store payment can be used only for content consumed directly inside the application. You cannot use it e.g. for ordering a pizza. I think this might be related to recent EU legislation forbidding SMS payments for services not consumed in the phone itself.

SMS payment is ok for little payments. Operators take big cut of the revenue, generally 30% – 70% depending on the country. Short code fees usually start from 500€ set-up fee + 500€ / month. SMS cannot be often send as a background, but the user is presented the normal SMS editor which reduces the user experience somehow.

For credit card payments there exists several providers. Credit card has the cheapest entry fees, but the downside is that the user needs to have the credit card. This excludes teenager audience.

Direct operator payments are not very well supported yet globally. Most western operators support them. The operator also takes a big share and the fixed fee is pretty high.

My favorite payment provider thus far is Bango which provides credit card payment starting 9€ / mo. and scales up to worldwide SMS payments which cost few grannies per month.

In most cases, the payment experience will not be smooth. You need to open the phone main browser on the payment provider page to do the payment. This usually will close your own application. Rarely you can do the payment inside the application and support multiple platforms. After doing the payment most platforms allow you to close the browse and reopen your application using a special URL handler.

Wikipedia mobile payments page is also useful.

Image upload

<input type=”file”> won’t work on iPhone and some other platforms as those don’t have user browsable file system. Also the file dialog usually doens’t have image preview making it useless.

Phonegap has a branch which supports images picking using iPhone’s own gallery browser.

In any case, there is not yet cross-platform solution for this.

Future prospects

In some time-frame we will get rid of the need to wrap HTML applications natively as the web browser applications will support all HTML5 features without extensions and probably have some proprietary extensions for mobile specific features like SMS. We already have had some taste for this:

Scripting Google analytics for multidomain site



We are running few Plone sites which use top level domain (TLD) to identify the site language.

Like many other CMSs out there,  Plone has only one box to enter Google Analytics script snippet.  It is often desirable to use different tracker for different domain and different language combinations, but Google itself doesn’t provide any fancy generator to create complex page tracking code.

Page tracker code, though looks little difficult when spit out by Google Analytics, is just normal Javascript.  You can make the condition to choose the appropriate page tracker id in Javascript itself using document.location property and this way you don’t need to mess with your page templates to create separate tracking Javascript snippet slots.

Here is an example what you can toss in to Plone site setup -> site -> JavaScript for web statistics support:

<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>

<script type="text/javascript">
try {
// Choose page tracker id according to domain
  var domains = document.location.hostname.split(".");
  var tld = domains[domains.length-1];
if(tld == "fi") {
  // .fi
  pageTracker = _gat._getTracker("UA-8819100-1");
} else {
  // .com
  pageTracker = _gat._getTracker("UA-8819100-4");
}
pageTracker._trackPageview();
} catch(err) {
}

</script>

This is used on www.twinapex.com and www.twinapex.fi sites.

Use console.log(err) to output possible Javascript in catch {} errors using Firebug.

Python-like urlparser module for Javascript



I had to deconstruct and reconstruct URLs from pieces when doing advanced Javascripting for Plone.

I found this nice library from Denis Laprise. However, it had a bug with fragment extractor and lacked reconstruction possibilies. So I decided to make a new version.

Download urlparse.js version 0.2. thank you :)

Couple of examples:

var p = new Poly9.URLParser('http://user:password@poly9.com/pathname?arguments=1#fragment');
p.getHost() == 'poly9.com';
p.getProtocol() == 'http';
p.getPathname() == '/pathname';
p.getQuerystring() == 'arguments=1';
p.getFragment() == 'fragment';
p.getUsername() == 'user';
p.getPassword() == 'password';

var p = new Poly9.URLParser("http://localhost:8080/path);

p.setQuerystring("foo=bar");
p.setFragment("anchor");
p.setPort(7070);

var url = p.getURL() // http://localhost:7070/path?foo=bar#anchor

MooTools setOptions() nullifies object references



I bumped up into a problem where the object references where resolved as object copies when I passed them to class instances. That might sound easy to resolve, but unfortunately I was already deep in code and it was difficult to see this. Therefore, here’s a little explanation for those who are facing the same frustrating issue.

Say, you have variable ‘a’ and you want to pass it to a MooTools class B instance during creation. In the easiest case you’d use new B({ myReference: a}) and trust on MooTools’ Class.setOptions() to minify the need of code lines. This is what you should do… well at least that’s what I did and in this case it was a mistake.

It turns out that Class.setOptions() merges it’s arguments to this.options and then takes copy of them via $merge(). That means that any variable references you pass to setOptions() will get copied to this.options and.. well, that’s it. See lines 1170-1173 in uncompressed version of MooTools 1.2:

var Options = new Class({
    setOptions: function(){
        this.options = $merge.run([this.options].extend(arguments));

That effectively nullifies the benefits of Class.setOptions() if you want to pass in variable references..

Here’s a longer example to clarify (use Firebug):

  // The most basic MooTools class that implements options
  // ref is a variable meant for pointing at given object
  // (won't do that, however)
  var B = new Class({
    Implements: Options,
    options: {
      ref: null
    },
    initialize: function(options) {
      this.setOptions(options);
    }
  });

  // Ok let's create an instance that we can pass to B
  // It's similar with all sorts of variables
  var A = new Class({
    initialize: function() {
      this.somevar = 'untouched';
    }
  });
  var a = new A();

  // Create an instance of B and give it somevar as reference
  var b = new B({ ref: a });

  // prints out "untouched" as should
  console.log(b.options.ref.somevar);

  // Let's change the variable (direct access, bad)
  a.somevar = "changed";

  // b's reference should still point to a, right?
  // In that case the following should print "changed",
  // but because our reference object was copied instead
  // of retaining reference to it, we just get "untouched"
  console.log(b.options.ref.somevar);

I don’t know why MooTools wants to make a copy of arguments in setOptions() – propably for performance reasons.

Catching silent Javascript exceptions with a function decorator



The one utterly annoying thing with the otherwise excellent Firefox/Firebug combo is that some exceptions are let silently through without being end up to be visible in the Firebug console. This makes debugging very difficult, unless you are aware of the phenomenon. I am not sure whether this is caused by some internal Firefox logic flow, since IE + Visual Web Developer doesn’t seem to be affected by this.

Since this problem pops up constantly, I decided to create an easy way to deal with the situation. I decorate all functions which made have silent exceptions (e.g. one called from document load event) with a custom logger function which will first log the exception and then rethrow it.

Thus instead of writing (JQuery example)

myfunction() {
    // crash here
    var i = foobar; // missing variable foobar
}

$(document).ready(myfunction);

write

myfunction() {
    // crash here
    var i = foobar; // missing variable foobar
}

$document.ready(logExceptions(myFunction));

or

// myFunction can be bind to many events and exceptions are logged always
myfunction = logExceptions(function() {
  // crash here
    var i = foobar; // missing variable foobar
});

$document.ready(logExceptions(myFunction));

The Javascript code for the decorator:

/**
 * Enhancd Javascript logging and exception handler.
 *
 * Copyright 2008 Red Innovation Ltd.
 *
 * @author Mikko Ohtamaa
 * @license 3-clause BSD
 *
 */

// Browser specific logging output initialization
// Supports Firefox/Firebug. Other (Opera) can be hooked in here.
if(!console) {
	// Install dummy functions, so that logging does not break the code if Firebug is not present
    var console = {};
    console.log = function(msg) {};
    console.info = function(msg) {};
    console.warn = function(msg) {};
} else {
    // console.log provided by Firefox + Firebug
}

/**
 * Try print human digestable exception stack trace to Firebug console.
 *
 * http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Error
 *
 * @param e: Error
 */
function printStackTrace(e) {

	var msg = e.name + ":" + e.message;

	if (e.fileName) {
		msg += " at " + e.fileName + ":" + e.lineNumber;
	}
	console.log(msg);

	if (e.stack) {
		// Extract Firefox stack information. This tells how you ended up
		// to the exception in the first place. I didn't find
		// instructions how to parse this stuff.
		console.log(e.stack);
	}
}

/**
 * Decorate function so that exceptions falling through are printed always.
 *
 * Returns a decorated function which will be used instead of the normal function.
 * The decorated function has preplaced try ... catch block which will not let
 * through any exceptions silently or without logging. Even though there is an
 * exception it is normally throw upwards in the stack after logging.
 *
 * @param func: Javascript function reference
 */
function logExceptions(func) {

	var orignal = func;

	decorated = function() {
		try {
			orignal.apply(this, arguments);
		} catch(exception) {
			printStackTrace(exception);
			throw exception;
		}
	}

	return decorated;
}

Updated: Eclipse web developer plug-in memo



Below are my personal notes what plug-ins are needed to get “perfect” Eclipse web development set-up. Basically they are just my own notes so that I don’t need to Google everything all over again every time I reinstall. I hope the readers can find new pearls here or suggest improvements.

This post is update to previous Eclipse web developer plug-in memo post. New versions are available and some plug-ins have become deprecated. This blog post reflects those changes.

These instructions are good for:

  • Python developer
  • PHP developer
  • Java developer

Choosing Eclipse distribution

  • On Window, use EasyEclipse
  • On Linux, use Eclipse provided by the distribution – Eclipse links against the embedded Mozilla browser and this is distribution specific – EasyEclipse has some issues here. For Ubuntu users:
sudo apt-get install sun-java6 eclipse

EasyEclipse bundles some of the stuff listed here with it – when using EasyEclipse you don’t need to have separate PyDev and Subclipse downloads.

Eclipse for 64-bit Linux has various problems. You might want to run 32-bit Eclipse (another relevant blog post). When you use Linux distribution specific Eclipse install, all your personal Eclipse files go to .eclipse folder under your home folder.

Installing plug-ins

Eclipse has internal updater/web installer. All plug-ins are downloaded as ZIP files and extracted to Eclipse folder or installed through the internal updater. Paste Eclipse update site URLs to menu Help -> Software updates -> Find and Install, New Remote Location.

Python

PyDev is a plug-in for Python and Jython development. It has enhanced commercial extensions for professional developers with more intelligent autocomplete and debugger.

Site URL: http://pydev.sourceforge.net

PyDev Eclipse update URL: http://pydev.sourceforge.net/updates/

PyDev extensions Eclipse update URL (this commercial, but worth of every penny): http://www.fabioz.com/pydev/updates

PDT

PDT download provides Eclipse, HTML editor, PHP editor and CSS editor.

Site URL: http://www.eclipse.org

Eclipse update site URL: http://download.eclipse.org/tools/pdt/updates/

Java

If you need to do J2EE development use IBM’s Web Tools Platform. If you don’t need Java capabilities don’t install these, since they just bloat Eclipse and make the start up time worse.

Subclipse

Subclipse provides Subversion version control integration to Eclipse.

Eclipse update site URL: http://subclipse.tigris.org/update_1.4.x/

In the installer, uncheck the integration modules checkbox or the installer will complain about missing modules.

Aptana Studio

Aptana Studio is state-of-the-art Web 2.0 development suite for Eclipse. It has Javascript, CSS and HTML editors. It supports various Javascript libraries out of the box and has support for Firefox and IE in-browser Javascript debugging.

Eclipse update site URL: http://update.aptana.com/update/studio/3.2/site.xml

ShellEd

Syntax coloring for Unix shell scripts

Project site: http://sourceforge.net/projects/shelled

SQL Explorer

SQL terminal and SQL editor with some GUI capabilities.

Eclipse update site URL http://eclipsesql.sourceforge.net/

SQL Explorer needs MySQL JDBC driver. Download from here. Install MySQL connector by extracting the file and adding it from SQL Explorer preferences.

Plone, KSS, Javascript, field validation and the cup of WTF



Knowledge does hurt. Today I had my sweet cup of WTF. We are developing a medical database based on Plone 3.1. It uses very advanced AJAX framework called KSS – basically you can avoid the pain of writing pure Javascript by crafting special CSS like stylesheets which bind server-side Python code to any Javascript event. This makes AJAX programming a joy. You can easily combine server-side logic with user interface events, like field validation.

Well… then there was an error. KSS validation was not working for the text fields on a certain pages…. or it did sometimes. We were not sure. This is so called Heisenbug. I armed myself with sleepy eyes, Firebug and a lot of energy drinks.

I saw a KSS error in the Firebug log window and failed HTTP POST in the server logs.

Invalid request.

The parameter, value, was omitted from the request.

Looks like the field value was not properly posted for the field validation.

The first thing was locate the error and get function traceback for the messy situation. Unfortunately Firefox Javascript engine or Firebug cannot show tracebacks properly… the grass is so much greener on the other side of the fence. So I had to manually search through the codebase by manually plotting console.log() calls here and there.

Finally I thought I pinpointed the cause of the failure. By shaking finger (excitement, tireness and all that extra caffeinen from energy drinks), I opened the Javascript file just to realize why Javascript is utterly utterly shitty and why no sane person wants to do low level Javascript development. If ECMA standard committee had been clever and had been able to enforce anything long time ago, the following piece could be replaced with one function call.

fo.getValueOfFormElement = function(element) {
    // Returns the value of the form element / or null
    // First: update the field in case an editor is lurking
    // in the background
    this.fieldUpdateRegistry.doUpdate(element);
    if (element.disabled) {
        return null;
    }
    // Collect the data
    if (element.selectedIndex != undefined) {
        // handle single selects first
        if(!element.multiple) {
                if (element.selectedIndex < 0) {
                    value="";
                } else {
                    var option = element.options[element.selectedIndex];
                    // on FF and safari, option.value has the value
                    // on IE, option.text needs to be used
                    value = option.value || option.text;
                }
        // Now process selects with the multiple option set
        } else {
            var value = [];
            for(i=0; i<element.options.length; i++) {
                var option = element.options[i];
                if(option.selected) {
                    // on FF and safari, option.value has the value
                    // on IE, option.text needs to be used
                    value.push(option.value || option.text);
                }
            }
        }
    // Safari 3.0.3 no longer has "item", instead it works
    // with direct array access []. Although other browsers
    // seem to support this as well, we provide checking
    // in both ways. (No idea if item is still needed.)
    } else if (typeof element.length != 'undefined' &&
        ((typeof element[0] != 'undefined' &&
        element[0].type == "radio") ||
        (typeof element.item(0) != 'undefined' &&
        element.item(0).type == "radio"))) {
        // element really contains a list of input nodes,
        // in this case.
        var radioList = element;
        value = null;
        for (var i=0; i < radioList.length; i++) {
            var radio = radioList[i] || radioList.item(i);
            if (radio.checked) {
                value = radio.value;
            }
        }
    } else if (element.type == "radio" || element.type == "checkbox") {
        if (element.checked) {
           value = element.value;
        } else {
            value = null;
        }
    } else if ((element.tagName.toLowerCase() == 'textarea')
               || (element.tagName.toLowerCase() == 'input' &&
                    element.type != 'submit' && element.type != 'reset')
              ) {
        value = element.value;
    } else {
        value = null;
    }
    return value;
};

It turned out that the element in this case was an empty list of radio buttons. When you are tab keying through a radio button group without any value selected, like in the case a content object is just created, KSS validation is triggered even though there is no value in any of the radio buttons. This makes KSS think the value is null and it does not properly handle the situation. This does not cause any user visible effects unless you have Javascript debugging on (Firebug + debugging mode in Plone’s Javascript registry).

But this was not the bug I was looking for. It was just masking the original bug, because I had an empty radio button group next to the text field whose validation was not correctly done. More server side debugging…

I inserted some funky debug prints to Archetypes.Field.validate_validators():

validate_validators()
Calling validators:(('isEmptyNoError', V_SUFFICIENT), ('validDecRange03', V_REQUIRED))

We can see that not triggered validator, validDecRange03, is still with us. Then I add more debug prints to see where things go wrong, this time to to Products.validation.chain.__call__.

Calling validators:(('isEmptyNoError', V_SUFFICIENT), ('validDecRange03', V_REQUIRED))
Name:isEmptyNoError value:234234234234234234234234234232342342342344534232342344234534554 result:True

Ok – we have a case here. isEmptyNoError validator is executed before our custom validator. Since this validator is flagged as “sufficient” other validators are not evaluated. I think this has not been the case before and our validator have worked properly… maybe there was API change in Plone 3.1 which broke the things?

After digging and digging and digging I found this 4 years old bug. Let’s open the famous isEmptyNoError source code in Products.validation.validators.EmptyValidator:

class EmptyValidator:
    __implements__ = IValidator

    def __init__(self, name, title='', description='', showError=True):
        self.name = name
        self.title = title or name
        self.description = description
        self.showError = showError

    def __call__(self, value, *args, **kwargs):
        isEmpty  = kwargs.get('isEmpty', False)
        instance = kwargs.get('instance', None)
        field    = kwargs.get('field', None)

        # XXX: This is a temporary fix. Need to be fixed right for AT 2.0
        #      content_edit / BaseObject.processForm() calls
        #      widget.process_form a second time!
        if instance and field:
            widget  = field.widget
            request = getattr(instance, 'REQUEST', None)
            if request and request.form:
                form   = request.form
                result = widget.process_form(instance, field, form,
                                             empty_marker=_marker,
                                             emptyReturnsMarker=True)
                if result is _marker or result is None:
                    isEmpty = True

        if isEmpty:
            return True
        elif value == '' or value is None:
            return True
        else:
            if getattr(self, 'showError', False):
                return ("Validation failed(%(name)s): '%(value)s' is not empty." %
                       { 'name' : self.name, 'value': value})
            else:
                return False

There is my WTF. Or XXX – thanks for the kisses. My guess is that because KSS validation is executed in special context, the magical REQUEST might not be there. The “is sufficient” validator fails because of the some sort of god forgotten magic and thus all custom validators fail in KSS when the field is not required.

The workaround: I add my own greetings to the code:

        # XXX: This is a temporary fix. Need to be fixed right for AT 2.0
        #      content_edit / BaseObject.processForm() calls
        #      widget.process_form a second time!
        if instance and field:
            widget  = field.widget
            request = getattr(instance, 'REQUEST', None)

            # XXX: Whatever this all does, it does not work for KSS validation
            # requests and we should ignore this
            if "fieldname" in request:
              return False

            if request and request.form:
                form   = request.form
                result = widget.process_form(instance, field, form,
                                             empty_marker=_marker,
                                             emptyReturnsMarker=True)
                if result is _marker or result is None:
                    isEmpty = True

If Zope 3 drives you smoking Plone 3 drives me drinking. No wonder newbies steer away from Plone – if you hadn’t been armed with 8 years of web development experience you would never have figured out what’s going on with such a simple thing as adding a custom validator. A comment added to the bug tracker.

Verifying Javascript files with JSLint from Eclipse IDE



With Web 2.0, Javascript has gained a great foothold in the web development. Javascript, which was originally indented to be few lines of interactivity here and there, is now used for full-blown UI frameworks like Ext JS and Dojo. Because its toy background, Javascript is not the easiest language to debug. Also, the lovely phenomenon known as browser wars has ensured that professional Javascript development is PITA due to browser incompatibilities – It seconds the Symbian C++ embedded programming if you know what I mean.

Javascript is executed run-time. Thus, any errors cannot be cached until the code is run. JSLint is a verifier tool which checks Javascript syntax offline. Of course, it cannot detect application logic errors, but it makes sure that you don’t have cross-browser compability problems in your Javascript syntax. Maybe the most famous of these problems is the extra comma before the list termination which is ok for Firefox, but kills the page on Internet Explorer.

Some argue that this does not really catch bugs and instead a comprehensive unit-test suite should be used. This might be a requirement for a platform level Javascript library code, but it is often an overkill for your little site with some flashy dialog windows.

Installing JSLint

JSLint itself is written in Javascript. So you need to offline Javascript interpreter to run it. On Linux there exists Rhino. On Ubuntu, Rhino package is broken. and you need to download the orignal Rhino JAR from here and copy it to /usr/share/java.

sudo apt-get install rhino # Note https://bugs.launchpad.net/ubuntu/+source/rhino/+bug/93885

Then get JSLint offline version from here.

Running JSLint straight from the Eclipse

We use JSEclipse plug-in in Eclipse for Javascript development. Thus, it is convenient to to execute JSLint directly from the Eclipse IDE.

Drag and drop jslint.js to your project/scripts folder in Eclipse.

Create a new External tool like in the example below.

screenshot-external-tools.png

Then you can run it by just clicking “run external tool” icon. Output comes to the console window.

screenshot-pydev-nurse-source-server-media-js-ui-mapjs-eclipse-sdk.png

Checking all Javascript files during unit test

verify_javascript.bash can be executed from shell and it checks all JS files in your project. This is a nice feature to integrate to your unit test cycle.

Example:

$ bash scripts/verify_javascript.bash source/server/media/jssource/server/media/js/jquery.js

Lint at line 11 character 30: Expected '{' and instead saw 'var'.

...

Eclipse web developer plug-in memo



Currently I work in quite wide field of software development: Python (standalone, Plone, Zope, Django), PHP, Java, Symbian and embedded Linux. I am using Eclipse for development, since it’s pretty much the only consistent platform filling my needs. The nature of work also forces me to use different computers (Mac/Windows/Linux) with different clients. This drives me to reinstall Eclipse now and then.

Below are my personal notes what plug-ins are needed to get “perfect” Eclipse set-up. Basically they are just my own notes so that I don’t need to Google everything all over again every time I reinstall. I hope the readers can find new pearls here or suggest improvements.

Eclipse setup

Eclipse has internal updater/web installer. All plug-ins are downloaded as ZIP files and extracted to Eclipse folder or installed through the internal updater. Paste Eclipse update site URLs to menu Help -> Software updates -> Find and Install, New Remote Location. You can use dummy text as the name of update site.

Eclipse WTP (Web Tools Platform)

Eclipse Web Tools Platform bundles Eclipse, Java development tools, HTML editor, CSS editor and some other generic useful stuff.

Python

PyDev is a plug-in for Python and Jython development.

Site URL: http://pydev.sourceforge.net

Eclipse update site URL: http://pydev.sourceforge.net/updates/

PDT

PDT download provides Eclipse, HTML editor, PHP editor and CSS editor.

Site URL: http://www.eclipse.org

Eclipse update site URL: http://download.eclipse.org/tools/pdt/updates/

Subclipse

Subclipse provides Subversion version control integration to Eclipse.

Eclipse update site URL: http://subclipse.tigris.org/update_1.2.x

In the installer, uncheck the integration modules checkbox or the installer will complain about missing modules.

JSEclipse

JSEclipse provides a better editor (over WTP) for Javascript files, with impressive outlining and autofill capabilities.

Download requires Adobe developer account or similar fill-in-the-fields crap.

Site URL: http://labs.adobe.com/technologies/jseclipse/

ShellEd

Syntax coloring for Unix shell scripts

Project site: http://sourceforge.net/projects/shelled

SQL Explorer

SQL editor with limited GUI capabilities. Based on Eclipse platform. Comes standalone and as Eclipse plug-in.

needs MySQL JDBC driver

Technorati tags: