Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/BaseExt.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/BaseExt.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/BaseExt.java	(revision 21919)
@@ -0,0 +1,78 @@
+package org.greenstone.admin.guiext;
+
+import java.awt.Insets;
+import java.io.File;
+
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.greenstone.gsdl3.util.GSPath;
+import org.greenstone.gsdl3.util.GlobalProperties;
+import org.w3c.dom.*;
+
+import org.greenstone.admin.GAI;
+
+public abstract class BaseExt {
+    
+    static protected JScrollPane description_pane;
+    static protected JPanel main_pane;
+    static protected JPanel control_pane;
+    static protected JTextArea descriptionTextArea;
+    static protected String description;
+	
+    public JPanel button_pane;
+    public String extension_name;
+    public String destination_folder;
+    public String url;
+    public String group;
+    public String download_type;
+    public Boolean configurable;
+    
+    GlobalProperties globalProperty = null;
+    static String fileSeparator = File.separator;
+    GSPath gspath = null;
+    
+    public BaseExt(){
+    }
+    
+    protected abstract JPanel getControlPane();
+    
+    protected abstract JPanel getDescriptionPane();    
+    
+    protected abstract boolean getCommandStatus(String Command);
+    
+    public boolean doCallback(String methodName)
+    {
+	return false;
+    }
+
+    public boolean doCommand(String cmd)
+    {
+	return false;
+    }
+    
+    public static Element getRootElement(String url){
+    	
+	Element rootNode = null;
+    	try{
+    	    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+    	    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+    	    Document doc = docBuilder.newDocument();
+    	    doc = docBuilder.parse (new File(url));
+    	    rootNode = doc.getDocumentElement();  
+    	    return rootNode;
+    	}catch (Exception e) {
+	    e.printStackTrace();
+	    return null;
+    	}
+    }
+    
+    protected abstract Object[][] getConfigureContent();
+    
+    //protected abstract Boolean isConfigurable();
+	
+    protected abstract JPanel getButtonPane();
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/BasePanel.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/BasePanel.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/BasePanel.java	(revision 21919)
@@ -0,0 +1,8 @@
+package org.greenstone.admin.guiext;
+
+import javax.swing.JPanel;
+
+public interface BasePanel
+{
+    public JPanel getControlPanel();
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Callback.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Callback.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Callback.java	(revision 21919)
@@ -0,0 +1,72 @@
+package org.greenstone.admin.guiext;
+
+import java.lang.reflect.Method;
+import javax.swing.JTextArea;
+
+import org.w3c.dom.Element;
+
+public class Callback implements Runnable
+{
+    String _param = null;
+    CommandStep _parent = null;
+    
+    public Callback(Element callbackElement, CommandStep parent)
+    {
+	_parent = parent;
+
+	if(callbackElement != null){
+	    _param = ExtXMLHelper.getValueFromSingleElement(callbackElement, true);
+	}
+	else{
+	    System.err.println("This <" + ExtXMLHelper.CALLBACK + "> element is null");
+	}
+    }
+
+    public void run()
+    {
+	JTextArea messageArea = _parent.getMessageArea();
+
+	Object extObj = _parent.getParent().getParent().getExtObject();
+	Class[] params = new Class[]{String.class};
+	
+	Method callbackMethod = null;
+	try{
+	    callbackMethod = extObj.getClass().getDeclaredMethod("doCallback", params);
+	}
+	catch(Exception ex){
+	    System.err.println("Specified class does not have a doCallback(String) method");
+	    _parent.threadError();
+	    return;
+	}
+
+	messageArea.append("\nCalling the callback method with \"" + _param +"\" as an argument");
+	Boolean success = null;
+	try{
+	    success = (Boolean)callbackMethod.invoke(extObj, (Object)_param);
+	}
+	catch(Exception ex){
+	    System.err.println("Error while attempting to invoke specified method -> ");
+	    ex.printStackTrace();
+	    _parent.threadError();
+	    return;
+	}
+	
+	if(!success){
+	    System.err.println("Call to doCallback method with the value \"" + _param + "\" returned unsuccessfully");
+	    _parent.threadError();
+	    return;
+	}
+
+	_parent.startNextThread();
+    }
+    
+    public String getParam()
+    {
+	return _param;
+    }
+    
+    public CommandStep getParent()
+    {
+	return _parent;
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Command.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Command.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Command.java	(revision 21919)
@@ -0,0 +1,119 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.File;
+
+import java.util.HashMap;
+
+import javax.swing.JFrame;
+import javax.swing.JTextArea;
+import javax.swing.JOptionPane;
+
+public class Command implements Runnable
+{
+    HashMap _osCommands = new HashMap();
+    CommandStep _parent = null;
+
+    public Command(Element commandElement, CommandStep parent) 
+    {
+	_parent = parent;						
+
+	if(commandElement != null){
+	    Element[] commands = ExtXMLHelper.getMultipleChildElements(commandElement, "os", true);
+	    for(int i = 0; i < commands.length; i++){
+		Element currentCommand = commands[i];
+		String os = currentCommand.getAttribute("name");
+
+		if(os.equals("")){
+		    System.err.println("A <" + ExtXMLHelper.COMMAND + "> element does not have an \"os\" attribute");
+		}
+		else{
+		    _osCommands.put(os, ExtXMLHelper.getValueFromSingleElement(currentCommand, true));
+		}
+	    }
+	}
+	else{
+	    System.err.println("This <" + ExtXMLHelper.COMMAND + "> element is null");
+	}
+    }
+
+    public void run()
+    {
+	JTextArea messageArea = _parent.getMessageArea();
+	String command = getCommandForCurrentOS();
+	
+	if(command == null){
+	    System.err.println("No commands exist for your current operating system, to avoid seeing this warning please place <os name=\"default\"/> into the set of commands for this step in the extension_project_list.xml file located in your extension directory");
+	    _parent.threadError();
+	    return;
+	}
+	else if(command == ""){
+	    _parent.threadError();
+	    return;
+	}
+		
+	File workingDirectory = new File(_parent.getParent().getParent().getExtensionDirectory());
+	Process commandLineProc = null;
+	try{
+	    messageArea.append("\nExecuting \"" + command + "\" on the command line\n");
+	    commandLineProc = Runtime.getRuntime().exec(command, null, workingDirectory);
+	    
+	    BufferedReader stdInput = new BufferedReader(new InputStreamReader(commandLineProc.getInputStream()));
+	    BufferedReader errInput = new BufferedReader(new InputStreamReader(commandLineProc.getErrorStream()));
+	    String s= "";
+	    
+	    while ((s = stdInput.readLine()) != null) {
+		messageArea.append(s + "\n");
+		messageArea.setSelectionEnd(messageArea.getDocument().getLength());
+	    }  
+	    
+	    boolean error = false;
+	    while ((s = errInput.readLine()) != null){
+		messageArea.append(s + "\n");
+		error = true;
+	    }  
+	    
+	    if(error){
+		JOptionPane.showMessageDialog(new JFrame(), "There was an error while running the command line process");	
+		_parent.threadError();
+		return;
+	    }
+	    
+	    int success = commandLineProc.waitFor();
+	    
+	    if(success != 0){
+		System.err.println("Command line process \"" + command  + "\" returned unsuccessfully with the value \"" + success + "\"");
+		_parent.threadError();
+		return;
+	    }
+	}
+	catch(Exception ex){
+	    ex.printStackTrace();
+	    _parent.threadError();
+	    return;
+	} 
+	_parent.startNextThread();
+    }
+
+    public boolean executeCommand()
+    {
+	return false;
+    }
+
+    public CommandStep getParent()
+    {
+	return _parent;
+    }
+
+    public String getCommandForCurrentOS()
+    {
+	String command = (String)_osCommands.get(System.getProperty("os.name"));
+	if(command != null){
+	    return command;
+	}
+	return (String)_osCommands.get("default");
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/CommandStep.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/CommandStep.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/CommandStep.java	(revision 21919)
@@ -0,0 +1,117 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+
+import java.awt.BorderLayout;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+import javax.swing.JTextArea;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import org.greenstone.admin.GAI;
+import org.greenstone.admin.LoggedMessageArea;
+
+public class CommandStep extends Step
+{
+    protected ArrayList _commandsAndCallbacks = new ArrayList();
+    protected LoggedMessageArea _messageArea = new LoggedMessageArea(this.getClass());
+    protected int _threadCount = -1;
+
+    /*
+    Command[] _commands = null;
+    Callback[] _callbacks = null;
+    */
+
+    public CommandStep(Element commandStepElement, SequenceList parent)
+    {
+	super(commandStepElement, parent);
+	
+	if(commandStepElement != null){
+	    NodeList commandAndCallbackElements = commandStepElement.getElementsByTagName("*");
+
+	    for(int i = 0; i < commandAndCallbackElements.getLength(); i++){
+		Element currentChild = (Element)commandAndCallbackElements.item(i);
+		if(currentChild.getTagName().equalsIgnoreCase(ExtXMLHelper.COMMAND)){
+		    _commandsAndCallbacks.add(new Command(currentChild, this));
+		}
+		else if (currentChild.getTagName().equalsIgnoreCase(ExtXMLHelper.CALLBACK)){
+		    _commandsAndCallbacks.add(new Callback(currentChild, this));
+		}
+		else if (currentChild.getTagName().equalsIgnoreCase(ExtXMLHelper.OS)){
+		}
+		else{
+		    System.err.println(currentChild.getTagName() + "A child of this command <" + ExtXMLHelper.STEP + "> is not either a <" + ExtXMLHelper.CALLBACK + "> element or a <" + ExtXMLHelper.COMMAND  + "> element");
+		}
+	    }
+
+	    if(_commandsAndCallbacks.size() == 0){
+		System.err.println("This command step does not have any <" + ExtXMLHelper.COMMAND  + "> or <" + ExtXMLHelper.CALLBACK  +"> elements");
+	    }
+
+	    _button.addActionListener(new CommandButtonListener());
+
+	}
+	else{
+	    System.err.println("This command <" + ExtXMLHelper.STEP + "> element is null");
+	}
+    }
+
+    public class CommandButtonListener implements ActionListener
+    {
+	public void actionPerformed(ActionEvent e)
+	{
+	    ExtensionInformation info = _parent.getParent();
+
+	    JPanel panel = new JPanel();
+	    panel.setLayout(new BorderLayout());
+	    panel.add(_messageArea, BorderLayout.CENTER);
+	    info.changeExtPane(panel);
+
+	    _button.setEnabled(false);
+	    _button.setText("Running...");
+
+	    _threadCount = 0;
+	    Thread newThread = new Thread((Runnable)_commandsAndCallbacks.get(0));
+	    newThread.start();
+	}
+    }
+
+    public JTextArea getMessageArea()
+    {
+	return _messageArea;
+    }
+
+    public void threadError()
+    {
+	_button.setText(_label);
+	_button.setEnabled(true);
+    }
+
+    public void startNextThread()
+    {
+	_threadCount++;
+
+	if(_threadCount < _commandsAndCallbacks.size()){
+	    Thread newThread = new Thread((Runnable)_commandsAndCallbacks.get(_threadCount));
+	    newThread.start();
+	}
+	else{
+	    _button.setText(_label);
+	    _button.setEnabled(true);
+	    _parent.registerStepCompletion(CommandStep.this);
+	}
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/DownloadStep.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/DownloadStep.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/DownloadStep.java	(revision 21919)
@@ -0,0 +1,344 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+import java.util.ArrayList;
+
+import java.io.File;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.JTextArea;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import java.awt.BorderLayout;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
+import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
+import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
+import org.tmatesoft.svn.core.wc.SVNClientManager;
+import org.tmatesoft.svn.core.wc.SVNWCUtil;
+import org.tmatesoft.svn.core.wc.SVNUpdateClient;
+import org.tmatesoft.svn.core.wc.ISVNEventHandler;
+import org.tmatesoft.svn.core.wc.SVNEvent;
+import org.tmatesoft.svn.core.wc.SVNEventAction;
+import org.tmatesoft.svn.core.wc.SVNRevision;
+import org.tmatesoft.svn.core.wc.SVNStatusType;
+import org.tmatesoft.svn.core.SVNURL;
+import org.tmatesoft.svn.core.SVNException;
+import org.tmatesoft.svn.core.SVNDepth;
+import org.tmatesoft.svn.core.SVNCancelException;
+
+import org.greenstone.admin.GAI;
+import org.greenstone.admin.LoggedMessageArea;
+
+public class DownloadStep extends Step
+{
+    Source _mainSource = null;
+    Source[] _auxSources = null;
+    LoggedMessageArea _messageArea = new LoggedMessageArea(this.getClass());
+
+    public DownloadStep(SequenceList parent)
+    {
+	super("AUTOMATIC_DOWNLOAD", "Download Extension", "", "", parent);
+
+	ExtensionInformation info = _parent.getParent();
+	
+	_mainSource = new Source("svn", info.getBaseURL() + info.getFileStem() + "/trunk/", "", this);
+
+	_button.addActionListener(new DownloadButtonListener());
+    }
+
+    public DownloadStep(Element downloadStepElement, SequenceList parent)
+    {
+	super(downloadStepElement, parent);
+	
+	if(downloadStepElement != null){    
+	    Element mainSourceElement = ExtXMLHelper.getSingleChildElement(downloadStepElement, ExtXMLHelper.MAIN_SOURCE, true);
+	    if(mainSourceElement == null){
+		System.err.println("This download <" + ExtXMLHelper.STEP + "> element has no <" + ExtXMLHelper.MAIN_SOURCE + "> element");
+	    }
+	    else{
+		_mainSource = new Source(mainSourceElement, this);
+	    }
+	
+	    Element[] auxSourceElements = ExtXMLHelper.getMultipleChildElements(downloadStepElement, ExtXMLHelper.AUX_SOURCE, false);
+	    if(auxSourceElements != null){
+		_auxSources = new Source[auxSourceElements.length];
+		for(int i = 0; i < auxSourceElements.length; i++){
+		    _auxSources[i] = new Source(auxSourceElements[i], this);
+		}
+	    }
+	    
+	    _button = new JButton(_label);
+	    _button.setEnabled(false);
+	    _button.addActionListener(new DownloadButtonListener());
+	}
+	else{
+	    System.err.println("This download <" + ExtXMLHelper.STEP + "> element is null");
+	}
+    }
+
+    public class DownloadButtonListener implements ActionListener
+    {
+	boolean _svnInitialised = false;
+	public DownloadButtonListener()
+	{
+	    if(!_svnInitialised){
+		//Setup the SVN client system
+		DAVRepositoryFactory.setup();
+		SVNRepositoryFactoryImpl.setup();
+		FSRepositoryFactory.setup();
+	    }
+	    _svnInitialised = true;
+	}
+	
+	public void actionPerformed(ActionEvent e)
+	{
+	    _button.setEnabled(false);
+	    _button.setText("Downloading...");
+
+	    DownloadThread downloadThread = new DownloadThread();
+	    downloadThread.start();
+	}
+    }
+
+    public class DownloadThread extends Thread
+    {
+	public void run()
+	{
+	    ExtensionInformation info = _parent.getParent();
+
+	    JPanel panel = new JPanel();
+	    panel.setLayout(new BorderLayout());
+	    panel.add(_messageArea, BorderLayout.CENTER);
+	    info.changeExtPane(panel);
+
+	    String fileStem = info.getFileStem();
+	    String defaultDownloadLocation = GAI.getGSDL3ExtensionHome() + System.getProperty("file.separator") + fileStem;
+
+	    ArrayList sourceElements = new ArrayList();
+	    sourceElements.add(_mainSource);
+
+	    if(_auxSources != null){
+		for(int i = 0; i < _auxSources.length; i++){
+		    sourceElements.add(_auxSources[i]);
+		}
+	    }
+
+	    for(int i = 0; i < sourceElements.size(); i++){
+		
+		Source currentSource = (Source)sourceElements.get(i);
+		String destinationFolder = null;
+
+		if(currentSource.getMethod().equals("svn")){
+		    String name = "";
+		    String password = "";
+
+		    //Setup the SVN client
+		    SVNClientManager svnManager = SVNClientManager.newInstance(SVNWCUtil.createDefaultOptions(true), name, password);
+		    SVNUpdateClient svnCheckout = svnManager.getUpdateClient();
+		    svnCheckout.setEventHandler(new SVNCheckoutEventHandler());
+		    svnCheckout.setIgnoreExternals(false);
+
+		    if(!currentSource.getFolder().equals("")){
+			destinationFolder = currentSource.getFolder();
+		    }
+		    else{
+			destinationFolder = defaultDownloadLocation;
+		    }
+		    
+		    //Check if the directory already exists. If it does exist then as the use if they wish to delete it
+		    File wcDir = new File(destinationFolder);
+		    if (wcDir.exists()) {
+			
+			Object[] option = {"Continue","Cancel"};
+			int n = JOptionPane.showOptionDialog(new JFrame(),"The folder for this extension already exists, continuing will remove all the files within this folder before proceeding.","Attention", 
+							     JOptionPane.YES_NO_CANCEL_OPTION,
+							     JOptionPane.QUESTION_MESSAGE,
+							     null,
+							     option,
+							     option[0]);
+			if(n == 0){
+			    ExtPane.deleteDir(wcDir);
+			}
+			else{
+			    _button.setEnabled(true);
+			    _button.setText(_label);
+			    return;
+			}
+		    }
+		    wcDir.mkdirs();
+		    
+		    //Perform the specified checkout
+		    String url = currentSource.getURL();
+		    try{
+			SVNURL repositoryURL = SVNURL.parseURIEncoded(url);
+			long x = svnCheckout.doCheckout(repositoryURL, wcDir, SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY, true);	
+		    }
+		    catch(SVNException ex){
+			ex.printStackTrace();
+			_messageArea.append("Error retrieving file from SVN");
+			_button.setEnabled(true);
+			_button.setText(_label);
+			return;
+		    }
+		}
+	    }
+	    
+	    _button.setEnabled(true);
+	    _button.setText(_label);
+	    _parent.registerStepCompletion(DownloadStep.this);
+	}
+    }
+
+    public class SVNCheckoutEventHandler implements ISVNEventHandler
+    {	
+	public void handleEvent(SVNEvent event, double progress)
+	{
+	    /*
+	     * Gets the current action. An action is represented by SVNEventAction.
+	     * In case of an update an  action  can  be  determined  via  comparing 
+	     * SVNEvent.getAction() and SVNEventAction.UPDATE_-like constants. 
+	     */
+		
+	    //_messageArea.append(" event occurs");
+	    SVNEventAction action = event.getAction();
+	    String pathChangeType = " ";
+	    if (action == SVNEventAction.UPDATE_ADD) {
+		/*
+		 * the item was added
+		 */
+		pathChangeType = "A";
+	    } else if (action == SVNEventAction.UPDATE_DELETE) {
+		/*
+		 * the item was deleted
+		 */
+		pathChangeType = "D";
+	    } else if (action == SVNEventAction.UPDATE_UPDATE) {
+		/*
+		 * Find out in details what  state the item is (after  having  been 
+		 * updated).
+		 * 
+		 * Gets  the  status  of  file/directory  item   contents.  It   is 
+		 * SVNStatusType  who contains information on the state of an item.
+		 */
+		SVNStatusType contentsStatus = event.getContentsStatus();
+		if (contentsStatus == SVNStatusType.CHANGED) {
+		    /*
+		     * the  item  was  modified in the repository (got  the changes 
+		     * from the repository
+		     */
+		    pathChangeType = "U";
+		}else if (contentsStatus == SVNStatusType.CONFLICTED) {
+		    /*
+		     * The file item is in  a  state  of Conflict. That is, changes
+		     * received from the repository during an update, overlap  with 
+		     * local changes the user has in his working copy.
+		     */
+		    pathChangeType = "C";
+		} else if (contentsStatus == SVNStatusType.MERGED) {
+		    /*
+		     * The file item was merGed (those  changes that came from  the 
+		     * repository  did  not  overlap local changes and were  merged 
+		     * into the file).
+		     */
+		    pathChangeType = "G";
+		}
+	    } else if (action == SVNEventAction.UPDATE_EXTERNAL) {
+		/*for externals definitions*/
+		_messageArea.append("Fetching external item into '"
+				   + event.getFile().getAbsolutePath() + "'");
+		_messageArea.append("External at revision " + event.getRevision());
+		return;
+	    } else if (action == SVNEventAction.UPDATE_COMPLETED) {
+		/*
+		 * Updating the working copy is completed. Prints out the revision.
+		 */
+		//_messageArea.append("At revision " + event.getRevision());
+        	_messageArea.append("At revision " + event.getRevision()+"\n");
+        	//_messageArea.append("The extension ("+  _extensionName +") has been downloaded to the local folder: \n"+ _destination +"\n"); //xxxxx
+        	_messageArea.setSelectionEnd(_messageArea.getDocument().getLength());
+		return;
+	    } else if (action == SVNEventAction.ADD){
+		_messageArea.append("A     " +  event.getURL().getPath());
+		return;
+	    } else if (action == SVNEventAction.DELETE){
+		_messageArea.append("D     " +  event.getURL().getPath());
+		return;
+	    } else if (action == SVNEventAction.LOCKED){
+		_messageArea.append("L     " +  event.getURL().getPath());
+		return;
+	    } else if (action == SVNEventAction.LOCK_FAILED){
+		_messageArea.append("failed to lock    " +  event.getURL().getPath());
+		return;
+	    }
+	    
+	    /*
+	     * Now getting the status of properties of an item. SVNStatusType  also
+	     * contains information on the properties state.
+	     */
+	    SVNStatusType propertiesStatus = event.getPropertiesStatus();
+	    /*
+	     * At first consider properties are normal (unchanged).
+	     */
+	    String propertiesChangeType = " ";
+	    if (propertiesStatus == SVNStatusType.CHANGED) {
+		/*
+		 * Properties were updated.
+		 */
+		propertiesChangeType = "U";
+	    } else if (propertiesStatus == SVNStatusType.CONFLICTED) {
+		/*
+		 * Properties are in conflict with the repository.
+		 */
+		propertiesChangeType = "C";
+	    } else if (propertiesStatus == SVNStatusType.MERGED) {
+		/*
+		 * Properties that came from the repository were  merged  with  the
+		 * local ones.
+		 */
+		propertiesChangeType = "G";
+	    }
+	    
+	    /*
+	     * Gets the status of the lock.
+	     */
+	    String lockLabel = " ";
+	    SVNStatusType lockType = event.getLockStatus();
+	    
+	    if (lockType == SVNStatusType.LOCK_UNLOCKED) {
+		/*
+		 * The lock is broken by someone.
+		 */
+		lockLabel = "B";
+	    }
+
+	    /*
+	    System.err.println("pathChangeType = " + pathChangeType);
+	    System.err.println("propertiesChangeType = " + propertiesChangeType);
+	    System.err.println("lockLabel = " + lockLabel);
+	    System.err.println("event = " + event);
+	    System.err.println("event.getURL = " + event.getURL());
+	    */
+
+	    final String content = pathChangeType
+		+ propertiesChangeType
+		+ lockLabel
+		+ "       "
+		+ (event.getURL() == null ? "" : event.getURL().getPath()) + "\n";
+
+	    _messageArea.append(content);
+	    _messageArea.setSelectionEnd(_messageArea.getDocument().getLength());
+	}
+
+	public void checkCancelled() throws SVNCancelException
+	{
+	}
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtPane.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtPane.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtPane.java	(revision 21919)
@@ -0,0 +1,521 @@
+package org.greenstone.admin.guiext;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import java.net.URLClassLoader;
+import java.net.URL;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.tree.*;
+import javax.swing.table.TableCellRenderer;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.greenstone.admin.GAIManager;
+import org.greenstone.admin.GAI;
+import org.greenstone.admin.Configuration;
+import org.greenstone.gsdl3.util.GSPath;
+import org.greenstone.gsdl3.util.GlobalProperties;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+
+public class ExtPane extends JPanel  {
+
+    /* The pane to demonstrate extension information*/
+
+    protected JSplitPane main_ext_pane = null;
+
+    /** The panel that contains a log_list */
+    private JPanel extensionList_pane =  null;
+
+    /** The panel that contains the log content. */
+    protected static JPanel extensionContent_pane =  null;
+
+    /** The List showing all the log files concerned. */
+    //protected JList extension_list=null;
+
+    /** The label at the top of the logList_pane. */
+    protected JLabel extensionList_label =  null;
+      
+    /** The label shown at the top of the logContent Pane. */
+    protected JLabel extensionContent_label  =  null;
+    
+    /** The scrollable area into which the log content is placed. */
+    protected JScrollPane description_content = null;
+    
+    /* Log TEXT AREA*/
+    protected JTextArea description_textarea = null;
+   
+    protected JScrollPane message_content = null;
+    
+    /* Log TEXT AREA*/
+    public static JTextArea message_textarea = null;
+
+    protected static JPanel current_description_pane = null;
+
+    protected JPanel button_pane = null;
+    
+    /** Buttons */
+    //protected JButton download_button = null;
+    //protected static JButton pre_configure_button = null;
+    //protected JButton install_button = null;
+    //protected JButton post_configure_button = null;
+    //protected JButton uninstall_button = null;
+    //protected JButton switch_button = null;
+
+    protected static JPanel control_pane = null;
+
+    protected JPanel main_content_pane = null;
+    
+    protected JTree extensionTreeList = null;
+      
+    protected DefaultMutableTreeNode top = new DefaultMutableTreeNode("Extension List");
+    
+    protected HashMap extensionInformation = null;
+      
+    protected JPanel extensionContentHeaderPane = null;
+    
+    protected String file_location;
+    
+    protected final String extension_status_root = "Configuration";
+    protected File extension_status_file = null;
+    protected String extension_file = null;
+    
+    /** The various sizes for the screen layout*/
+    static protected Dimension MIN_SIZE = new Dimension( 90,  90);
+    static protected Dimension LIST_SIZE  = new Dimension(200, 450);
+    static protected Dimension LOGPANE_SIZE = new Dimension (800,450);
+    static public int left_padding = 5;
+    public String fileSeparator = File.separator;
+    
+    protected Element extFileRootElement = null;
+    protected HashMap extensions = null;
+    protected HashMap extClasses = null;
+    protected Properties dependencies = new Properties();
+    protected JPanel extPanel = new JPanel();
+    protected JSplitPane extensionListContainer = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+       
+    public ExtPane() {
+  
+	String gsdl3Home = GAI.getGSDL3Home();
+
+	file_location = gsdl3Home+fileSeparator+"ext"+fileSeparator+"extension_project_list.xml";
+	
+	extFileRootElement = getRootElement();
+
+	//Load the steps that have already been performed (used for dependencies)
+	loadExtensionInformation();
+	    
+	// create all the necessary panes
+	control_pane = new JPanel();
+	button_pane = new JPanel();
+	current_description_pane = new JPanel();
+	extensionContentHeaderPane = new JPanel();
+	extensionInformation = new HashMap();
+
+	// Extension_Pane
+	main_ext_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+	main_ext_pane.setPreferredSize(LOGPANE_SIZE);
+
+	// Extension_TextArea
+	description_textarea = new JTextArea();
+	description_textarea.setEditable(false);
+	description_textarea.setLineWrap(true);
+	description_textarea.setWrapStyleWord(true);
+	description_textarea.setMargin(new Insets(0,left_padding,0,0));
+    }
+
+    public void loadExtensionInformation()
+    {
+	Element[] extensionElements = ExtXMLHelper.getMultipleChildElements(extFileRootElement, ExtXMLHelper.EXTENSION, true);
+	if(extensionElements != null){
+	    extensions = new HashMap();
+
+	    for(int i = 0; i < extensionElements.length; i++){
+		ExtensionInformation ei = new ExtensionInformation(extensionElements[i], extFileRootElement.getAttribute("baseURL"));
+		extensions.put(ei.getName(), ei);
+	    }
+	}
+	else{
+	    System.err.println("There are no <" + ExtXMLHelper.EXTENSION  + "> elements in the extension_project_list.xml file in the extension directory");
+	}
+    }
+
+    /** This method is callsed to actually layout the components.*/
+    public void display() {
+         	
+	load_Admin_UI();
+	HashMap projectNameMap = getProjectList();
+    	Set s = projectNameMap.keySet();
+    	Iterator it = s.iterator();
+    	int i = 0;
+    	
+	while(it.hasNext()){
+	    String projectNameGroup = (String)it.next(); 
+	    ArrayList alist = (ArrayList)projectNameMap.get(projectNameGroup);
+	    DefaultMutableTreeNode projecti = new DefaultMutableTreeNode (projectNameGroup);
+	    
+	    for (int j = 0; j< alist.size(); j++){
+		String projectName = (String)alist.get(j);
+		DefaultMutableTreeNode projectNameGroupj = new DefaultMutableTreeNode (projectName);
+		projecti.add(projectNameGroupj);
+	    }
+	    top.add(projecti);
+	    i++;
+    	}
+
+    	extensionTreeList  = new JTree(top); 
+    	extensionTreeList.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+    	extensionTreeList.addTreeSelectionListener(new ConfTreeListener());
+    	extensionTreeList.addMouseListener(new ConfTreeMouseListener());
+    	
+	// extensionList_Pane
+	extensionList_pane = new JPanel();
+	extensionList_pane.setBorder(BorderFactory.createLoweredBevelBorder());
+	extensionList_label = new JLabel();
+	extensionList_label.setOpaque(true);
+	extensionList_label.setBackground(Configuration.getColor("coloring.workspace_heading_background"));
+	extensionList_label.setForeground(Configuration.getColor("coloring.workspace_heading_foreground"));
+	extensionList_label.setText("Available Extensions");
+	extensionList_label.setBorder(BorderFactory.createEmptyBorder(0, left_padding, 0, 0));
+	// extensionContent_Pane
+	extensionContent_pane = new JPanel();
+	extensionContent_pane.setBorder(BorderFactory.createLoweredBevelBorder());
+	extensionContent_pane.setBackground(Configuration.getColor("coloring.workspace_selection_background"));
+	extensionContent_pane.setForeground(Configuration.getColor("coloring.workspace_selection_foreground"));
+	
+	extensionContent_label = new JLabel();
+	extensionContent_label.setBorder(BorderFactory.createEmptyBorder(0, left_padding, 0, 0));
+	extensionContent_label.setOpaque(false);
+	extensionContent_label.setBackground(Configuration.getColor("coloring.workspace_selection_background"));
+	extensionContent_label.setForeground(Configuration.getColor("coloring.workspace_selection_foreground"));
+	extensionContent_label.setText("Extension Content");
+
+	// Layout Components
+	extensionList_pane.setLayout(new BorderLayout());
+	extensionList_pane.add(extensionTreeList, BorderLayout.CENTER);
+	extensionList_pane.add(extensionList_label, BorderLayout.NORTH);
+	extensionListContainer.add(extensionList_pane, JSplitPane.TOP);
+	extensionListContainer.setDividerLocation(600);
+	
+	extPanel.setLayout(new BorderLayout());
+	ExtensionInformation.setExtPane(extPanel);
+
+	JScrollPane extScrollPane = new JScrollPane(extPanel);
+
+	extensionContent_pane.setLayout(new BorderLayout());
+	//extensionContent_pane.add(main_content_pane,BorderLayout.SOUTH);
+	extensionContent_pane.add(extensionContent_label, BorderLayout.NORTH);
+	extensionContent_pane.add(extScrollPane, BorderLayout.CENTER);
+	
+	main_ext_pane.add(extensionListContainer, JSplitPane.LEFT);
+	main_ext_pane.add(extensionContent_pane, JSplitPane.RIGHT);
+	main_ext_pane.setDividerLocation(LIST_SIZE.width);
+
+	this.setLayout(new BorderLayout());
+	this.add(main_ext_pane, BorderLayout.CENTER);
+    }
+    
+    
+    protected Element getRootElement(){
+    	
+	Element rootNode = null;
+    	
+	try{
+    	    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+    	    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+    	    Document doc = docBuilder.newDocument();
+    	    doc = docBuilder.parse (new File(file_location));
+    	    rootNode = doc.getDocumentElement();  
+    	    return rootNode;
+    	
+	}catch (Exception e) {
+	    e.printStackTrace();
+	    return null;
+    	}
+    }
+    
+    protected HashMap getProjectList(){
+    	
+	HashMap projectGroupMap = new HashMap();
+	Element rootNode = extFileRootElement;
+	NodeList projectList =  rootNode.getElementsByTagName(ExtXMLHelper.EXTENSION);
+
+    	for(int i = 0; i<projectList.getLength(); i++){
+	    
+	    Element projectNode = (Element)projectList.item(i);
+	    NodeList nameList = projectNode.getElementsByTagName(ExtXMLHelper.NAME);
+	    String name = nameList.item(0).getChildNodes().item(0).getNodeValue();
+    		
+	    NodeList groupList = projectNode.getElementsByTagName(ExtXMLHelper.GROUP);
+	    String group = groupList.item(0).getChildNodes().item(0).getNodeValue();
+
+	    if(projectGroupMap.containsKey(group)){
+		ArrayList alist = (ArrayList)projectGroupMap.get(group);
+		alist.add(name);
+		projectGroupMap.put(group, alist);
+	    }
+	    else{
+		ArrayList alist = new ArrayList();
+		alist.add(name);
+		projectGroupMap.put(group, alist);
+	    }
+    	}
+	return projectGroupMap;
+    }
+
+    protected class ConfTreeMouseListener implements MouseListener
+    {
+	public void mouseClicked(MouseEvent e)
+	{
+	    Object comp = extensionTreeList.getLastSelectedPathComponent();
+	    if(comp == null){
+		return;
+	    }
+
+	    String option = comp.toString();
+	    if(option == null){
+		return;
+	    }
+
+	    ExtensionInformation info = (ExtensionInformation)extensions.get(option);
+
+	    if(info != null){
+		extPanel.removeAll();
+		
+		JTextArea descArea = new JTextArea();
+		descArea.setText(info.getDescription());
+		descArea.setEditable(false);
+		descArea.setLineWrap(true);
+		descArea.setWrapStyleWord(true);
+		
+		//JEditorPane descArea = new JEditorPane("text/plain" info.getDescription());
+		//descArea.setEditable(false);
+
+		extPanel.add(descArea, BorderLayout.CENTER);
+		extPanel.revalidate();
+		extPanel.repaint();
+	    }
+	}
+         
+	public void mouseEntered(MouseEvent e){}
+	public void mouseExited(MouseEvent e){}
+	public void mousePressed(MouseEvent e){}
+	public void mouseReleased(MouseEvent e){} 
+    }
+     
+    protected class ConfTreeListener implements TreeSelectionListener{
+	
+	public void valueChanged (TreeSelectionEvent e){
+
+	    String option = new String();
+	    
+	    try{
+		option = extensionTreeList.getLastSelectedPathComponent().toString();
+	    }catch(Exception ex){
+	    	
+	    }
+	    
+	    if(extensionInformation.containsKey(option)){
+		
+		try{
+		    extensionListContainer.setBottomComponent(getProjectButtonPane(option));
+		    extensionListContainer.setDividerLocation(250);
+
+		    extensionContent_label.setText(option);    
+		}
+		catch(Exception ex){
+		    ex.printStackTrace();
+		    description_textarea.setText("");
+		    description_textarea.append("Sorry, this extension is not available!");
+		    extensionContent_pane.add(description_content, BorderLayout.CENTER);
+		    extensionContent_pane.validate();
+		    extensionContent_pane.repaint();
+		}
+	    }
+	    else{
+	    	if(control_pane.getComponentCount()>0){
+		    //System.out.println("remove");
+		    control_pane.remove(0);
+		    control_pane.revalidate();
+		    control_pane.repaint();
+		    control_pane.add(new JPanel());
+		    control_pane.revalidate();
+		    control_pane.repaint();
+	    	}
+		
+	    	if(extensionContent_pane.getComponentCount()>0){
+		    
+		    extensionContent_label.setText("Extension Content");
+		    //extensionContent_pane.remove(2);
+		    //extensionContent_pane.revalidate();
+		    //extensionContent_pane.repaint();
+		    
+		    
+		    //JTextArea ja= new JTextArea();
+		    //JScrollPane jp = new JScrollPane(ja);
+		    //extensionContent_pane.add(jp);
+		    //extensionContent_pane.revalidate();
+		    //extensionContent_pane.repaint();
+		    //xxxxx
+	    	}
+	    }
+	}
+    }
+
+    public String loadDescription(String className){
+	
+	String description = "";
+    	Element rootNode = extFileRootElement;
+    	NodeList projectNodeList = rootNode.getElementsByTagName(ExtXMLHelper.EXTENSION);
+    	
+    	for(int i = 0; i < projectNodeList.getLength(); i++){
+	    
+	    Element projectNode = (Element)projectNodeList.item(i);
+	    NodeList nameNodeList = projectNode.getElementsByTagName(ExtXMLHelper.NAME);
+	    String name = nameNodeList.item(0).getChildNodes().item(0).getNodeValue();
+	    
+	    if(name.equals(className)){
+		NodeList descriptionNodeList = projectNode.getElementsByTagName(ExtXMLHelper.DESCRIPTION);
+		description = descriptionNodeList.item(0).getChildNodes().item(0).getNodeValue();
+	    }
+    	}    	
+	return description;
+    }
+
+    public void load_Admin_UI(){
+    	
+	Element rootNode = extFileRootElement;
+    
+	NodeList projectList =  rootNode.getElementsByTagName(ExtXMLHelper.EXTENSION);
+
+	for(int i = 0; i < projectList.getLength(); i++){
+        	
+	    Element projectNode = (Element)projectList.item(i);
+	    ArrayList alist = new ArrayList();    		
+	    NodeList nameList = projectNode.getElementsByTagName(ExtXMLHelper.NAME);
+	    String name = nameList.item(0).getChildNodes().item(0).getNodeValue();
+	    
+	    NodeList uiList = projectNode.getElementsByTagName(ExtXMLHelper.BASE_EXT);
+	    String admin_ui = uiList.item(0).getChildNodes().item(0).getNodeValue();
+
+	    //Maybe recode this? //xxxxx
+	    try{
+		extensionInformation.put(name, alist);
+	    }catch (Exception ex){
+		ex.printStackTrace();
+	    }
+	}
+    }
+    
+    public static void updateButtonPane(JPanel pane){
+	    control_pane.remove(0);
+	    control_pane.revalidate();
+	    control_pane.repaint();
+	    control_pane.add(pane);
+    }
+    
+    public static void updateExtensionContentPane(){
+	     CardLayout cl = (CardLayout)( current_description_pane.getLayout());
+	     cl.next(current_description_pane);
+
+    }
+
+    public static boolean deleteDir(File dir) {
+        
+    	if (dir.isDirectory()) {
+	    String[] children = dir.list();
+	    for (int i=0; i<children.length; i++) {
+		boolean success = deleteDir(new File(dir, children[i]));
+		if (!success) {
+		    return false;
+		}
+	    }
+	}
+    	return dir.delete();
+    }
+
+   
+    
+  /*  
+    private void updateStatus(String project_name, String action){
+	
+	try{
+	    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
+	    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+	    Document doc = docBuilder.newDocument();
+	    Element root = doc.createElement(extension_status_root);
+			
+	    boolean fileExist = extension_status_file.exists();
+	    
+	    if(!fileExist){
+		extension_status_file = new File (extension_file);
+		root = doc.createElement(extension_status_root);
+	    }
+	    else{
+		doc = docBuilder.parse (extension_status_file);
+		root = doc.getDocumentElement();
+	    }
+	    
+	    
+	    TransformerFactory tf = TransformerFactory.newInstance();     
+	    Transformer transformer= tf.newTransformer();     
+	    DOMSource source= new DOMSource(root);     
+	    transformer.setOutputProperty(OutputKeys.INDENT,"yes");     
+			
+	    Writer pwx= new  BufferedWriter(new OutputStreamWriter(new FileOutputStream(extension_status_file),"UTF-8"));     
+	    StreamResult result= new StreamResult(pwx);     
+	    transformer.transform(source,result);  
+	    pwx.close();
+	    
+	    root = null;
+	    docBuilderFactory = null;
+	    docBuilder = null;
+	    doc = null;
+	    
+	}catch (Exception e) {
+	    System.out.println(e);
+	}	
+    }*/
+
+    public JPanel getProjectButtonPane(String projectName){
+
+    	String option = extensionTreeList.getLastSelectedPathComponent().toString();
+    	JPanel buttonPane = new JPanel();
+
+	ExtensionInformation extension = (ExtensionInformation)extensions.get(projectName);
+
+	String gsdl3ExtHome = GAI.getGSDL3ExtensionHome();
+
+	JButton[] buttons = extension.getSequenceList().getButtons();
+
+	GridLayout gl = new GridLayout(buttons.length, 1);
+	gl.setVgap(5);
+	buttonPane.setLayout(gl);
+	
+	for(int j = 0; j < buttons.length; j++){
+	    buttonPane.add(buttons[j]);
+	}
+	
+	extension.getSequenceList().updateButtons();
+	
+	return buttonPane;
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtXMLHelper.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtXMLHelper.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtXMLHelper.java	(revision 21919)
@@ -0,0 +1,144 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+
+public class ExtXMLHelper
+{
+    static public final String EXTENSION_LIST = "extension_list";
+    static public final String EXTENSION = "extension";
+    static public final String GROUP = "group";
+    static public final String BASE_EXT = "base_ext";
+    static public final String FILE_STEM = "file_stem";
+    static public final String NAME = "name";
+    static public final String DOWNLOAD = "download";
+    static public final String MAIN_SOURCE = "mainSource";
+    static public final String AUX_SOURCE = "auxSource";
+    static public final String SOURCE = "source";
+    static public final String INSTALL = "install";
+    static public final String UNINSTALL = "uninstall";
+    static public final String ENABLE = "enable";
+    static public final String DISABLE = "disable";
+    static public final String DESTINATION = "destination";
+    static public final String DESCRIPTION = "description";
+    static public final String PROPERTY = "property";
+    static public final String SEQUENCE_LIST = "sequence_list";
+    static public final String STEP ="step";
+    static public final String COMMAND ="command";
+    static public final String CALLBACK ="callback";
+    static public final String OPTION_LIST = "optionList";
+    static public final String OPTION = "option";
+    static public final String CLASS = "class";
+    static public final String OS = "os";
+
+    private ExtXMLHelper(){}
+    
+    public static String getValueFromSingleElement(Element parent, String valueName, boolean strict)
+    {
+	if(parent == null){
+	    System.err.println("Parent element is null");
+	    return null;
+	}
+	else if(valueName == null){
+	    System.err.println("Value name element is null");
+	    return null;
+	}
+
+	Element e = getSingleChildElement(parent, valueName, strict);
+	if(e == null){
+	    System.err.println("Cannot get a value from element \"" + valueName +"\" as it does not exist in this parent");
+	    return null;
+	}
+
+	String s = getValueFromSingleElement(e, strict);
+	if(s == null){
+	    if(strict){
+		System.err.println("Cannot get a value from the element \"" + valueName + "\" as it is empty");
+	    }
+	    
+	    return null;
+	}
+
+	return s;
+    }
+
+    public static String getValueFromSingleElement(Element e, boolean strict)
+    {
+	if(e == null){
+	    System.err.println("Element is null");
+	    return null;
+	}
+
+	Node textChild = e.getFirstChild();
+	if(textChild == null){
+	    if(strict){
+		System.err.println("This element has no value");
+	    }
+	    return null;
+	}
+
+	String value = textChild.getNodeValue();
+	if(value.equals("")){
+	    if(strict){
+		System.err.println("The value for this element is empty");
+	    }
+	    return null;
+	}
+	return value;
+    }
+
+    public static Element getSingleChildElement(Element parent, String elementName, boolean strict)
+    {
+	if(parent == null){
+	    System.err.println("Parent element is null");
+	    return null;
+	}
+	else if(elementName == null){
+	    System.err.println("Element name is null");
+	    return null;
+	}
+
+	NodeList nodeList = parent.getElementsByTagName(elementName);
+	if(nodeList == null || nodeList.getLength() == 0){
+	    if(strict){
+		System.err.println("The element " + elementName + " does not exist in this parent element");
+	    }
+	    return null;
+	}
+	else if(nodeList.getLength() > 1){
+	    System.err.println("The element " + elementName + " has more than one element in this parent element");
+	    return null;
+	}
+
+	return (Element)nodeList.item(0);
+    }
+    
+    public static Element[] getMultipleChildElements(Element parent, String elementName, boolean strict)
+    {
+	if(parent == null){
+	    System.err.println("Parent element is null");
+	    return null;
+	}
+	else if(elementName == null){
+	    System.err.println("Element name is null");
+	    return null;
+	}
+
+	NodeList nodeList = parent.getElementsByTagName(elementName);
+	if(nodeList == null || nodeList.getLength() == 0){
+	    if(strict){
+		System.err.println("The element " + elementName + " does not exist in this parent element");
+	    }
+	    return null;
+	}
+	
+	Element[] elements = new Element[nodeList.getLength()];
+	
+	for(int i = 0; i < nodeList.getLength(); i++){
+	    elements[i] = (Element)nodeList.item(i);
+	}
+
+	return elements;
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtensionInformation.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtensionInformation.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/ExtensionInformation.java	(revision 21919)
@@ -0,0 +1,174 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.net.URLClassLoader;
+import java.net.URL;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+
+import java.io.File;
+
+import java.awt.BorderLayout;
+
+import javax.swing.JTextArea;
+import javax.swing.JPanel;
+
+import org.greenstone.admin.GAI;
+
+public class ExtensionInformation
+{
+    protected String _name = null;
+    protected String _group = null;
+    protected String _fileStem = null;
+    protected String _description = null;
+    protected String _baseExt = null;
+    protected String _baseURL = null;
+    protected SequenceList _sequenceList= null;
+    protected boolean _guiExtLoaded = false;
+
+    protected static JPanel _extPane = new JPanel();
+
+    public ExtensionInformation(Element extensionElement, String baseURL){
+	if(extensionElement != null){
+	    _name = ExtXMLHelper.getValueFromSingleElement(extensionElement, ExtXMLHelper.NAME, true);
+	    _group = ExtXMLHelper.getValueFromSingleElement(extensionElement, ExtXMLHelper.GROUP, true);
+	    _fileStem = ExtXMLHelper.getValueFromSingleElement(extensionElement, ExtXMLHelper.FILE_STEM, true);
+	    _description = ExtXMLHelper.getValueFromSingleElement(extensionElement, ExtXMLHelper.DESCRIPTION, true);
+	    _baseExt = ExtXMLHelper.getValueFromSingleElement(extensionElement, ExtXMLHelper.BASE_EXT, true);
+	    _baseURL = baseURL;
+	    _sequenceList = new SequenceList(ExtXMLHelper.getSingleChildElement(extensionElement, ExtXMLHelper.SEQUENCE_LIST, true), this);
+	}
+	else{
+	    System.err.println("<" + ExtXMLHelper.EXTENSION + "> element is null");
+	}
+    }
+
+    public String getBaseURL()
+    {
+	return _baseURL;
+    }
+
+    public String getName()
+    {
+	return _name;
+    }
+
+    public String getGroup()
+    {
+	return _group;
+    }
+    
+    public String getFileStem()
+    {
+	return _fileStem;
+    }
+
+    public String getDescription()
+    {
+	return _description;
+    }
+ 
+    public String getBaseClassName()
+    {
+	return _baseExt;
+    }
+
+    public SequenceList getSequenceList()
+    {
+	return _sequenceList;
+    }
+
+    public Object getExtObject()
+    {
+	loadGuiExtFile();
+	Class extClass = null;
+	try{
+	    extClass = Class.forName(_baseExt);
+	}
+	catch(Exception ex){
+	    System.err.println("Could not create the extension class used for callback methods, either the class name is incorrect or the class does not exist inside the guiext.jar file");
+	}
+	Constructor classConstructor = null;
+	Object classObj = null;
+	
+	try{
+	    classConstructor = extClass.getConstructor(new Class[0]);
+	    classObj = classConstructor.newInstance(new Object[0]);
+	}
+	catch(Exception ex){
+	    ex.printStackTrace();
+	    System.err.println("Could not create the extension class used for callback methods, either the class name is incorrect or the class does not exist inside the guiext.jar file");
+	    return null;
+	}
+	
+	return classObj;
+    }
+
+    public void loadExternalJar(String filename){
+	//Load the dynamic jar loader
+	URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
+	Class sysclass = URLClassLoader.class;
+	final Class[] parameters = new Class[]{URL.class};
+	Method method = null;
+	try{
+	    method = sysclass.getDeclaredMethod("addURL", parameters);
+	}
+	catch(Exception ex){
+	    ex.printStackTrace();
+	}
+	method.setAccessible(true);
+
+	File externalFile = new File(filename);
+	if(!externalFile.exists()){
+	    System.err.println("Cannot load the external jar file at " + filename + " as it does not exist");
+	    return;
+	}
+
+	try {
+	    URL u = new URL(externalFile.toURI().toURL().toString());
+	    method.invoke(sysloader, new Object[]{u});
+	} catch (Throwable t) {
+	    t.printStackTrace();
+	}
+
+	_guiExtLoaded = true;
+    }
+
+    public void loadGuiExtFile()
+    {
+	if(!_guiExtLoaded){
+	    loadExternalJar(getExtensionDirectory() + System.getProperty("file.separator") + "guiext.jar");		
+	}
+    }    
+
+    public boolean isGuiExtLoaded()
+    {
+	return _guiExtLoaded;
+    }
+
+    public void changeExtPane(JPanel extPane)
+    {
+	_extPane.removeAll();
+	_extPane.add(extPane, BorderLayout.CENTER);
+	_extPane.revalidate();
+	_extPane.repaint();
+    }
+
+    public static void setExtPane(JPanel extPane)
+    {	
+	_extPane = extPane;
+    }
+
+    public JPanel getExtPane()
+    {
+	return _extPane;
+    }
+
+    public String getExtensionDirectory()
+    {
+	return GAI.getGSDL3ExtensionHome() + System.getProperty("file.separator") + _fileStem;
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Option.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Option.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Option.java	(revision 21919)
@@ -0,0 +1,62 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+public class Option
+{
+    String _name = null;
+    String _id = null;
+    String _value = null;    
+    OptionList _parent = null;
+
+    public Option(Element optionElement, OptionList parent)
+    {
+	_parent = parent;
+
+	if(optionElement != null){
+	    _name = optionElement.getAttribute("label");
+	    if(_name == null || _name.equals("")){
+		System.err.println("This option element does not have a label");
+	    }
+
+	    _id = optionElement.getAttribute("id");
+	    if(_id == null || _id.equals("")){
+		System.err.println("This option element does not have an id");
+	    }
+	    
+	    _value = ExtXMLHelper.getValueFromSingleElement(optionElement, false);
+	    
+	    if(_value == null){
+		_value = "";
+	    }
+	}
+	else{
+	    System.err.println("This <" + ExtXMLHelper.OPTION + "> element is null");
+	}
+    }
+    
+    public String getName()
+    {
+	return _name;
+    }
+
+    public String getValue()
+    {
+	return _value;
+    }
+
+    public void setValue(String value)
+    {
+	_value = value;
+    }
+
+    public String getId()
+    {
+	return _id;
+    }
+    
+    public OptionList getParent()
+    {
+	return _parent;
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/OptionList.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/OptionList.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/OptionList.java	(revision 21919)
@@ -0,0 +1,103 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+import java.io.File;
+import java.io.FileInputStream;
+
+import java.util.Properties;
+
+public class OptionList
+{
+    Option[] _options = null;
+    String _label = null;
+    String _id = null;
+    String _file = null;
+    PropertiesStep _parent = null;
+    
+    public OptionList(Element optionListElement, PropertiesStep parent, boolean fromFile)
+    {
+	_parent = parent;
+
+	if(optionListElement != null){
+	    _label = optionListElement.getAttribute("label");
+	    _id = optionListElement.getAttribute("id");
+	    _file = optionListElement.getAttribute("file");
+	    
+	    Element[] optionElements = ExtXMLHelper.getMultipleChildElements(optionListElement, ExtXMLHelper.OPTION, true);
+	    if(optionElements == null){
+		System.err.println("This optionList element does not contain any option elements");
+	    }
+
+	    Properties properties = null;
+	    if(fromFile){
+		File propertiesFile = null;
+		if(_file.equals("")){
+		    propertiesFile = new File(_parent.getParent().getParent().getExtensionDirectory() + System.getProperty("file.separator") + "build.properties");
+		}
+		else{
+		    propertiesFile = new File(_parent.getParent().getParent().getExtensionDirectory() + System.getProperty("file.separator") + _file);
+		}
+
+		if(propertiesFile.exists()){
+		    properties = new Properties();
+		    
+		    try{
+			properties.load(new FileInputStream(propertiesFile));
+		    }
+		    catch(Exception ex){
+			System.err.println("Error loading previous properties values from the properties file, using default values");
+			//ex.printStackTrace();
+		    }
+		}
+	    }
+	    
+	    _options = new Option[optionElements.length];
+	    for(int i = 0; i < optionElements.length; i++){
+		_options[i] = new Option(optionElements[i], this);
+
+		if(fromFile && properties != null){
+		    String value = null;
+		    if(_id.equals("")){
+			value = properties.getProperty(_options[i].getId());
+		    }
+		    else{
+			value = properties.getProperty(_id + "." + _options[i].getId());
+		    }
+
+		    if(value != null){
+			_options[i].setValue(value);
+		    }
+		}
+	    }
+	}
+	else{
+	    System.err.println("This <" + ExtXMLHelper.OPTION_LIST  + "> element is null");
+	}
+    }
+
+    public Option[] getOptions()
+    {
+	return _options;
+    }
+
+    public String getLabel()
+    {
+	return _label;
+    }
+
+    public String getId()
+    {
+	return _id;
+    }
+
+    public String getFilename()
+    {
+	return _file;
+    }
+    
+    public PropertiesStep getParent()
+    {
+	return _parent;
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/PanelStep.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/PanelStep.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/PanelStep.java	(revision 21919)
@@ -0,0 +1,66 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+import javax.swing.JButton;
+import javax.swing.JPanel;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+import java.lang.reflect.Constructor;
+
+public class PanelStep extends Step
+{
+    String _controlPanelClass = null;
+
+    public PanelStep(Element panelStepElement, SequenceList parent)
+    {
+	super(panelStepElement, parent);
+
+	if(panelStepElement != null){
+
+	    _controlPanelClass = ExtXMLHelper.getValueFromSingleElement(panelStepElement, ExtXMLHelper.CLASS, true);
+
+	    if(_controlPanelClass == null || _controlPanelClass.equals("")){
+		System.err.println("The <" + ExtXMLHelper.CLASS + "> element inside this panel <" + ExtXMLHelper.STEP + "> either does not exist or is empty");
+	    }
+	    
+	    _button.addActionListener(new PanelButtonListener());   
+	}
+	else{
+	     System.err.println("This panel <" + ExtXMLHelper.STEP + "> element is null");
+	}
+    }
+
+    public class PanelButtonListener implements ActionListener
+    {
+	public void actionPerformed(ActionEvent e)
+	{
+	    _parent.getParent().loadGuiExtFile();
+
+	    Class controlPanelClass = null;
+	    try{
+		controlPanelClass = Class.forName(_controlPanelClass);
+	    }
+	    catch(Exception ex){
+		ex.printStackTrace();
+		return;
+	    }
+
+	    Object classObj = null;
+	    try{
+		Constructor classConstructor = controlPanelClass.getConstructor(new Class[0]);
+		classObj = classConstructor.newInstance(new Object[0]);
+	    }
+	    catch(Exception ex){
+		System.err.println("Could not create the control panel class, either the class name is incorrect or the class does not exist inside the guiext.jar file");
+		return;
+	    }
+		
+	    _parent.getParent().changeExtPane(((BasePanel)(classObj)).getControlPanel());
+
+	    _parent.registerStepCompletion(PanelStep.this);
+	}
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/PropertiesStep.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/PropertiesStep.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/PropertiesStep.java	(revision 21919)
@@ -0,0 +1,277 @@
+package org.greenstone.admin.guiext;
+
+import javax.swing.BorderFactory;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JFrame;
+import javax.swing.JButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.SwingConstants;
+
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableCellEditor;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.KeyEvent;
+
+import java.awt.BorderLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+
+import java.util.Properties;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+import org.w3c.dom.Element;
+
+import org.greenstone.admin.Configuration;
+import org.greenstone.admin.LoggedMessageArea;
+
+public class PropertiesStep extends Step
+{
+    OptionList[] _optionLists = null;
+    JTable[] _tables = null;
+    OptionList[] _modifiedOptionLists = null;
+
+    public PropertiesStep(Element propertiesStepElement, SequenceList parent)
+    {
+	super(propertiesStepElement, parent);
+	
+	if(propertiesStepElement != null){
+	    ExtensionInformation info = _parent.getParent();
+
+	    Element[] optionListElements = ExtXMLHelper.getMultipleChildElements(propertiesStepElement, ExtXMLHelper.OPTION_LIST, true);
+
+	    if(optionListElements != null){
+		
+		_optionLists = new OptionList[optionListElements.length];
+		_modifiedOptionLists = new OptionList[optionListElements.length];
+
+		for(int i = 0; i < optionListElements.length; i++){
+		    _optionLists[i] = new OptionList(optionListElements[i], this, false);
+		    _modifiedOptionLists[i] = new OptionList(optionListElements[i], this, true);
+		}
+	    }
+	    else{
+		System.err.println("This properties <" + ExtXMLHelper.STEP + "> element has no <" + ExtXMLHelper.OPTION_LIST + "> elements");
+	    }
+	}
+	else{
+	    System.err.println("This properties <" + ExtXMLHelper.STEP + "> element is null" );
+	}
+	
+	_button.addActionListener(new PropertiesButtonListener());
+    }
+
+    public void setPropertiesToDefaults()
+    {
+	for(int i = 0; i < _optionLists.length; i++){
+	    Option[] _modifiedOptions = _modifiedOptionLists[i].getOptions();
+	    Option[] _defaultOptions = _optionLists[i].getOptions();
+	    
+	    for(int j = 0; j < _defaultOptions.length; j++){
+		_modifiedOptions[j].setValue(_defaultOptions[j].getValue());
+	    }
+	}
+    }
+
+    public class PropertiesButtonListener implements ActionListener
+    {
+	public void actionPerformed(ActionEvent e)
+	{
+	    ExtensionInformation extension = _parent.getParent();
+	    
+	    String fileStem = extension.getFileStem();
+	    
+	    JPanel mainPanel = new JPanel();
+	    mainPanel.setBorder(BorderFactory.createLoweredBevelBorder());
+	    mainPanel.setBackground(Configuration.getColor("coloring.workspace_selection_background"));
+	    mainPanel.setForeground(Configuration.getColor("coloring.workspace_selection_foreground"));
+	    mainPanel.setLayout(new BorderLayout());
+	    
+	    JPanel tablePanel = new JPanel(new GridLayout(_optionLists.length, 1));
+	    
+	    if(_tables == null){
+		_tables = new JTable[_optionLists.length];
+	    }
+	    for(int i = 0; i < _optionLists.length; i++){
+		OptionList currentList = _modifiedOptionLists[i];
+	    
+		JLabel singleTableLabel = new JLabel(currentList.getLabel()); 
+		singleTableLabel.setHorizontalAlignment(SwingConstants.CENTER);
+		singleTableLabel.setFont(new Font("Arial", Font.BOLD, 13));
+
+		JPanel singleTablePanel = new JPanel();
+		singleTablePanel.setLayout(new BorderLayout());
+		singleTablePanel.add(singleTableLabel, BorderLayout.NORTH);
+		
+		Option[] options = currentList.getOptions();		
+
+		JTable propertiesTable = new JTable(new CustomTableModel(new String[]{"Setting", "Value"}, options));
+		
+		if(_tables[i] == null){
+		    _tables[i] = propertiesTable;
+		}
+
+		//The line below is a workaround for a bug in the JTable class that does not store properties if focus is given to another component.
+		propertiesTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
+
+		singleTablePanel.add(propertiesTable, BorderLayout.CENTER);
+		
+		tablePanel.add(singleTablePanel);
+	    }
+
+	    JButton defaultsButton = new JButton("Restore default settings");
+	    defaultsButton.addActionListener(new DefaultSettingsButtonListener());
+	    
+	    JButton saveButton = new JButton("Save");
+	    saveButton.addActionListener(new SaveButtonListener());
+	    
+	    JPanel buttonPanel = new JPanel();
+	    buttonPanel.setLayout(new GridLayout(1, 2));
+	    buttonPanel.add(saveButton);
+	    buttonPanel.add(defaultsButton);
+	    
+	    mainPanel.setBorder(BorderFactory.createEmptyBorder(10,20,10,20));
+	    mainPanel.setBackground(Configuration.getColor("coloring.workspace_selection_background"));
+	    mainPanel.setForeground(Configuration.getColor("coloring.workspace_selection_foreground"));
+	    mainPanel.add(tablePanel, BorderLayout.CENTER);
+	    mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+	    
+	    _parent.getParent().changeExtPane(mainPanel);
+	}
+    }
+    
+    public class SaveButtonListener implements ActionListener
+    {
+	public void actionPerformed(ActionEvent e)
+	{
+	    for(int i = 0; i < _optionLists.length; i++){
+		OptionList currentOptionList = _modifiedOptionLists[i];
+		Option[] currentOptions = currentOptionList.getOptions();
+
+		Properties newProperties = new Properties();
+		File propertiesFile = null;
+		if(currentOptionList.getFilename().equals("")){
+		    propertiesFile = new File(_parent.getParent().getExtensionDirectory() + System.getProperty("file.separator") + "build.properties");
+		}
+		else{
+		    propertiesFile = new File(_parent.getParent().getExtensionDirectory() + System.getProperty("file.separator") + currentOptionList.getFilename());
+		}
+
+		if(propertiesFile.exists()){
+		    try{
+			newProperties.load(new FileInputStream(propertiesFile));
+		    }
+		    catch(Exception ex){
+			System.err.println("Could not load properties file before saving to it, existing values will be removed. Possible reasons for this include: \n - Invalid properties file format\n - File is write protected");
+		    }
+		}
+
+		for(int j = 0; j < currentOptions.length; j++){
+		    Option currentOption = currentOptions[j];
+		    if(currentOptionList.getId().equals("")){
+			newProperties.setProperty(currentOption.getId(), currentOption.getValue());
+		    }
+		    else{
+			newProperties.setProperty(currentOptionList.getId() + "." + currentOption.getId(), currentOption.getValue());
+		    }
+		}
+
+		try{
+		    if(currentOptionList.getFilename().equals("")){
+			newProperties.store(new FileOutputStream(_parent.getParent().getExtensionDirectory() + System.getProperty("file.separator") + "build.properties"), null);
+		    }
+		    else{
+			newProperties.store(new FileOutputStream(_parent.getParent().getExtensionDirectory() + System.getProperty("file.separator") + currentOptionList.getFilename()), null);
+		    }
+		}
+		catch(Exception ex){
+		    System.err.println("Could not save to properties file.");
+		}
+	    }
+
+	    _parent.registerStepCompletion(PropertiesStep.this);
+	}
+    }
+
+    public class DefaultSettingsButtonListener implements ActionListener
+    {
+	public void actionPerformed(ActionEvent e)
+	{
+	    for(int i = 0; i < _tables.length; i++){
+		JTable currentTable = _tables[i]; 
+		Option[] currentDefaultOptions = _optionLists[i].getOptions();
+
+		for(int j = 0; j < currentDefaultOptions.length; j++){
+		    Option currentOption = currentDefaultOptions[j];
+		    currentTable.setValueAt(currentOption.getValue(), j, 1);
+		}
+		
+		currentTable.revalidate();
+		currentTable.repaint();
+	    }
+	}
+    }
+
+    public class CustomTableModel extends AbstractTableModel
+    {
+	private String[] _columnNames;
+    	private Option[] _data;
+	
+    	public CustomTableModel(String[] columnNames, Option[] data){
+	    _columnNames = columnNames;
+	    _data = data;
+    	}
+	
+        public int getColumnCount() {
+            return _columnNames.length;
+        }
+	
+        public int getRowCount() {
+            return _data.length;
+        }
+
+        public String getColumnName(int col) {
+            return _columnNames[col];
+        }
+
+        public Object getValueAt(int row, int col) {
+	    Option o = _data[row];
+	    if(col == 0){
+		return o.getName();
+	    }
+	    else if(col == 1){
+		return o.getValue();
+	    }
+            else{
+		return null;
+	    }
+        }
+
+
+        public Class getColumnClass(int c) {
+            return getValueAt(0, c).getClass();
+        }
+
+	public boolean isCellEditable(int row, int col) {
+	    if (col < 1) {
+                return false;
+            } else {
+                return true;
+            }
+        }
+	
+        public void setValueAt(Object value, int row, int col) {
+	    if(isCellEditable(row, col)){
+		_data[row].setValue((String)value);
+	    }
+	    fireTableDataChanged();
+        }
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/RemoveStep.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/RemoveStep.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/RemoveStep.java	(revision 21919)
@@ -0,0 +1,67 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+import java.io.File;
+
+import java.lang.reflect.Constructor;
+
+public class RemoveStep extends Step
+{
+    public RemoveStep(DownloadStep dls, SequenceList parent)
+    {
+	super("AUTOMATIC_REMOVE", "Remove Extension", dls.getId(), dls.getId(), parent);
+
+	_button.addActionListener(new RemoveButtonListener());   
+    }
+
+    public RemoveStep(Element removeStepElement, SequenceList parent)
+    {
+	super(removeStepElement, parent);
+
+	if(removeStepElement != null){	    
+	    _button.addActionListener(new RemoveButtonListener());   
+	}
+	else{
+	     System.err.println("This remove <" + ExtXMLHelper.STEP + "> element is null");
+	}
+    }
+
+    public class RemoveButtonListener implements ActionListener
+    {
+	public void actionPerformed(ActionEvent e)
+	{
+	    _button.setEnabled(false);
+	    _button.setText("Removing...");
+	    ExtensionInformation info = _parent.getParent();
+	    JPanel descPanel = new JPanel();
+	    descPanel.setLayout(new BorderLayout());
+
+	    JTextArea descArea = new JTextArea();
+	    descArea.setEditable(false);
+	    descArea.setText(info.getDescription());
+	    descArea.setLineWrap(true);
+	    descArea.setWrapStyleWord(true);
+
+	    descPanel.add(descArea);
+
+	    _parent.getParent().changeExtPane(descPanel);
+
+	    ExtPane.deleteDir(new File(info.getExtensionDirectory()));
+
+	    _button.setEnabled(true);
+	    _button.setText(_label);
+
+	    _parent.clearPropertySteps();
+	    _parent.registerStepCompletion(RemoveStep.this);
+	}
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/SequenceList.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/SequenceList.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/SequenceList.java	(revision 21919)
@@ -0,0 +1,230 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+import org.greenstone.admin.GAI;
+
+import java.util.Properties;
+import java.util.ArrayList;
+
+import javax.swing.JButton;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+public class SequenceList
+{
+    protected ArrayList _steps = new ArrayList();
+    protected ExtensionInformation _parent = null;
+    protected Properties _dependencies = null;
+
+    public SequenceList(Element sequenceElement, ExtensionInformation parent)
+    {
+	_parent = parent;
+
+	loadExtensionStatesFromFile();
+
+	if(sequenceElement != null){
+	    Element[] stepElements = ExtXMLHelper.getMultipleChildElements(sequenceElement, ExtXMLHelper.STEP, true);
+	    if(stepElements == null || stepElements.length == 0){
+		System.err.println("This <" + ExtXMLHelper.SEQUENCE_LIST + "> element does not have any valid <" + ExtXMLHelper.STEP + "> elements");
+	    }
+	    else{
+		for(int i = 0; i < stepElements.length; i++){
+		    Element currentStep = stepElements[i];
+		    String action = currentStep.getAttribute("action");
+		    
+		    if(i == 0 && !action.equals("download")){
+			_steps.add(new DownloadStep(this));
+		    }
+
+		    if(action.equals("download")){
+			_steps.add(new DownloadStep(currentStep, this));
+		    }
+		    else if(action.equals("properties")){
+			_steps.add(new PropertiesStep(currentStep, this));
+		    }
+		    else if(action.equals("button")){
+			_steps.add(new CommandStep(currentStep, this));
+		    }
+		    else if(action.equals("panel")){
+			_steps.add(new PanelStep(currentStep, this));
+		    }
+		    else if(action.equals("remove")){
+			_steps.add(new RemoveStep(currentStep, this));
+		    }
+		    else{
+			System.err.println("\"" + action + "\" is not a valid action attribute for <" + ExtXMLHelper.STEP + "> elements (valid options are \"download\", \"properties\", \"panel\", \"button\") or \"remove\"");
+		    }
+
+		    if((i == stepElements.length-1) && !action.equals("remove")){
+			_steps.add(new RemoveStep((DownloadStep)_steps.get(0), this));
+		    }
+		}
+	    }
+	}
+	else{
+	    System.err.println("This <" + ExtXMLHelper.SEQUENCE_LIST + "> element is null");
+	}
+    }
+    
+    public ExtensionInformation getParent()
+    {
+	return _parent;
+    }
+
+    public Step[] getSteps()
+    {
+	return (Step[])(_steps.toArray(new Step[0]));
+    }
+
+    public JButton[] getButtons()
+    {
+	if(_steps == null){
+	    System.err.println("Cannot get buttons for this sequence list as it has no steps");
+	    return null;
+	}
+	
+	JButton[] buttons = new JButton[_steps.size()];
+	for(int i = 0; i < _steps.size(); i++){
+	    buttons[i] = ((Step)_steps.get(i)).getButton();
+	}
+	
+	return buttons;
+    }
+
+    private void loadExtensionStatesFromFile()
+    {
+	if(_dependencies == null){
+	    _dependencies = new Properties();
+	}
+
+	String fileName = GAI.getGSDL3ExtensionHome() + System.getProperty("file.separator") + "extensionStates.properties";
+	try{
+	    File stateFile = new File(fileName);
+	    
+	    if(stateFile.exists()){
+		_dependencies.load(new FileInputStream(new File(fileName)));
+	    }
+	}
+	catch(Exception ex){
+	    ex.printStackTrace();
+	}
+    }
+
+    private void saveExtensionStatesToFile()
+    {
+	String fileName = GAI.getGSDL3ExtensionHome() + System.getProperty("file.separator") + "extensionStates.properties";
+	try{
+	    _dependencies.store(new FileOutputStream(new File(fileName)), null);
+	}
+	catch(Exception ex){
+	    ex.printStackTrace();
+	}
+    }
+
+    public boolean addDependency(String id)
+    {
+	if(_dependencies != null){
+	    _dependencies.setProperty(_parent.getFileStem() + "." + id, "true");
+	    saveExtensionStatesToFile();
+	    return true;
+	}
+	else{
+	    System.err.println("Cannot add dependency as it has not yet been loaded");
+	    return false;
+	}
+    }
+
+    public void rollbackTo(String id)
+    {
+	if(_dependencies != null){
+	    rollbackToRecursive(id);
+	    saveExtensionStatesToFile();
+	}
+	else{
+	    System.err.println("Cannot remove dependency as it has not yet been loaded");
+	}
+    }
+
+    private void rollbackToRecursive(String id)
+    {
+	Step rollbackStep = null;
+	ArrayList dependsSteps = new ArrayList();
+	for(int i = 0; i < _steps.size(); i++){
+	    if(((Step)(_steps.get(i))).getId().equals(id)){
+		rollbackStep = (Step)_steps.get(i);
+	    }
+	    
+	    String[] depends = ((Step)_steps.get(i)).getDependsOn().split(",");
+
+	    for(int j = 0; j < depends.length; j++){
+		if(depends[j].equals(id)){
+		    dependsSteps.add(((Step)_steps.get(i)).getId());
+		}
+	    }
+	}
+	
+	_dependencies.setProperty(_parent.getFileStem() + "." + id, "false");
+
+	for(int i = 0; i < dependsSteps.size(); i++){
+	    rollbackToRecursive((String)dependsSteps.get(i));
+	}
+    }
+
+    public void clearPropertySteps()
+    {
+	for(int i = 0; i < _steps.size(); i++){
+	    Step currentStep = (Step)_steps.get(i);
+	    if(currentStep instanceof PropertiesStep){
+		((PropertiesStep)currentStep).setPropertiesToDefaults();
+	    }
+	}
+    }
+
+    public void registerStepCompletion(Step step)
+    {
+	addDependency(step.getId());
+	   
+	if(!step.getRollbacks().equals("")){
+	    String[] rollbacks = step.getRollbacks().split(",");
+	    for(int k = 0; k < rollbacks.length; k++){
+		rollbackTo(rollbacks[k]);
+	    }
+	}
+
+	updateButtons();
+    }
+
+    public void updateButtons()
+    {
+	loadExtensionStatesFromFile();
+	
+	ExtensionInformation currentExtension = _parent;
+	String fileStem = currentExtension.getFileStem();
+	
+	for(int i = 0; i < _steps.size(); i++){
+	    Step currentStep = (Step)_steps.get(i);
+	    JButton currentButton = currentStep.getButton();
+
+	    //If this step button depends on another button then disabled it
+	    String dependsOn = currentStep.getDependsOn();
+	    if(dependsOn.equals("")){
+		currentButton.setEnabled(true);
+	    }
+	    else{
+		String[] dependsList = dependsOn.split(",");
+		boolean enable = true;		
+		for(int j = 0; j < dependsList.length; j++){
+		    String propertyName = fileStem + "." + dependsList[j];
+
+		    if(_dependencies.getProperty(propertyName) == null || _dependencies.getProperty(propertyName).equals("false")){
+			enable = false;
+		    }
+		}
+		currentButton.setEnabled(enable);
+	    }
+	}
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Source.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Source.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Source.java	(revision 21919)
@@ -0,0 +1,61 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+public class Source
+{
+    String _method = null;
+    String _url = null;
+    String _folder = null;
+    DownloadStep _parent = null;
+
+    public Source(String method, String url, String folder, DownloadStep parent)
+    {
+	_parent = parent;
+	_method = method;
+	_url = url;
+	_folder = folder;
+    }
+
+    public Source(Element sourceElement, DownloadStep parent)
+    {
+	_parent = parent;
+
+	if(sourceElement != null){
+	    _method = sourceElement.getAttribute("method");
+	    if(_method.equals("")){
+		System.err.println("This <" + ExtXMLHelper.SOURCE + "> element does not contain a method attribute");
+	    }
+
+	    _folder = sourceElement.getAttribute("folder");
+	    
+	    _url = ExtXMLHelper.getValueFromSingleElement(sourceElement, true);
+	    if(_url == null || _url.equals("")){
+		System.err.println("This <" + ExtXMLHelper.SOURCE + "> element does not contain a url");
+	    }
+	}
+	else{
+	    System.err.println("This <" + ExtXMLHelper.SOURCE + "> element is null");
+	}
+    }
+
+    public DownloadStep getParent()
+    {
+	return _parent;
+    }
+
+    public String getMethod()
+    {
+	return _method;
+    }
+
+    public String getURL()
+    {
+	return _url;
+    }
+
+    public String getFolder()
+    {
+	return _folder;
+    }
+}
Index: main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Step.java
===================================================================
--- main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Step.java	(revision 21919)
+++ main/trunk/greenstone3/src/java/org/greenstone/admin/guiext/Step.java	(revision 21919)
@@ -0,0 +1,123 @@
+package org.greenstone.admin.guiext;
+
+import org.w3c.dom.Element;
+
+import java.awt.BorderLayout;
+import java.awt.Insets;
+
+import javax.swing.JButton;
+import javax.swing.JTextArea;
+import javax.swing.JPanel;
+
+/*
+if(_commonMessageArea == null){
+	    _commonMessageArea = new JTextArea();
+	    _commonMessageArea.setEditable(false);
+	    _commonMessageArea.setAutoscrolls(true);
+	    _commonMessageArea.setLineWrap(true);
+	    _commonMessageArea.setWrapStyleWord(true);
+	    _commonMessageArea.setMargin(new Insets(0,5,0,0));
+	}
+*/
+
+public class Step
+{
+    protected SequenceList _parent = null;
+    protected String _id = null;
+    protected String _action = null;
+    protected String _label = null;
+    protected String _dependsOn = null;
+    protected String _rollbacks = null;
+    protected JButton _button = null;
+    protected static JTextArea _commonMessageArea = new JTextArea();
+
+    public Step(String id, String label, String dependsOn, String rollbacks, SequenceList parent)
+    {
+	_parent = parent;
+	_id = id;
+	_label = label;
+	_dependsOn = dependsOn;
+	_rollbacks = rollbacks;
+
+	_button = new JButton(_label);
+	_button.setEnabled(false);
+    }
+
+    public Step(Element stepElement, SequenceList parent)
+    {
+	_parent = parent;
+	if(stepElement != null){
+	    _id = stepElement.getAttribute("id");
+	    if(_id.equals("")){
+		System.err.println("This <" + ExtXMLHelper.STEP + "> element has no id attribute or the id attribute is empty");
+	    }
+	    
+	    _label = stepElement.getAttribute("label");
+	    if(_label.equals("")){
+		System.err.println("This <" + ExtXMLHelper.STEP + "> element has no label attribute or the label attribute is empty");
+	    }
+	    
+	    _action = stepElement.getAttribute("action");
+	    if(_action.equals("")){
+		System.err.println("This <" + ExtXMLHelper.STEP + "> element has no action attribute or the action attribute is empty");
+	    }
+	    
+	    _dependsOn = stepElement.getAttribute("dependsOn");
+	    String[] dependencies = _dependsOn.split(",");
+	    for(int i = 0; i < dependencies.length; i++){
+		if(dependencies[i].equals(_id)){
+		    System.err.println("The " + _id + " step depends on itself which is not allowed");
+		    _dependsOn = "";
+		}
+	    }
+
+	    _rollbacks = stepElement.getAttribute("rollbackTo");
+
+	    _button = new JButton(_label);
+	    _button.setEnabled(false);
+	}
+	else{
+	    System.err.println("This <" + ExtXMLHelper.STEP + "> element is null");
+	}
+    }
+
+    public JButton getButton()
+    {
+	return _button;
+    }
+
+    public String getId()
+    {
+	return _id;
+    }
+
+    public String getAction()
+    {
+	return _action;
+    }
+
+    public String getLabel()
+    {
+	return _label;
+    }
+    
+    public String getDependsOn()
+    {
+	return _dependsOn;
+    }
+
+    public String getRollbacks()
+    {
+	return _rollbacks;
+    }
+
+    public SequenceList getParent()
+    {
+	return _parent;
+    }
+
+    public JTextArea getCommonMessageArea()
+    {
+	return _commonMessageArea;
+    }
+}
