Connecting SAP and PHP

SAP is said to be the most comprehensive and widely-used Enterprise Resource Planning system ever. Due to its widespread use, SAP users and developers come across a variety of situations where they need to connect an installation of SAP to a different platform/programming language so that the data and data interpretations contained in relation to that installation can be accessed by that platform or programming language. Hence a myriad of SAP connectors for different programming languages have been born.

PHP, being the most widely used server-side programming language on the web, is one of the most probable languages that would need to connect to SAP at some point or the other. For this, the extremely handy SAPRFC module for PHP has been developed. This post will discuss an end-to-end implementation of the SAPRFC PHP module and explain how to connect SAP to the module as well.

The first step is to go to the above link and download the complete saprfc package. The easiest way to interface saprfc with PHP and to get it to work is to download XAMPP 1.7.1 (NOT the latest version). This contains PHP 5.28 which seems to be the only PHP version which works correctly with the SAPRFC module.

Unzip XAMPP and copy php_saprfc_528.dll from your saprfc/ext folder over to the /php/ext/ folder in your XAMPP directory. Locate the php.ini file in XAMPP and open it for editing. Here you will have to find the extensions section and add the line

extension=php_saprfc_528.dll

to this section. Now open the XAMPP Control Panel and start Apache. If you followed the above steps correctly, Apache should start now without any problems. IF it gives an error, go over the above steps again.

First of all, we’ll see what we need to do on the SAP side to expose a Remote Function Module which can be accessed via RFC. All you need to do is to create a ‘remote-enabled’ Function Module in SAP. This can be done by ticking ‘Remote-enabled module’ under the function module’s attributes, as shown below:

SAP RFC

You can write whatever program logic in ABAP in the Function Module. Pay special attention to the Importing and Exporting parameters of the Function Module as these will be what are passed to and from your PHP program.

Next, you need to copy over the saprfc.php library file over from the saprfc module folder to your PHP project directory. You can use the functions contained in this file to create a saprfc object which can login to SAP and access your remote-enabled Function Module.

The following code is essential for your PHP program to access SAP:

require_once('saprfc.php');
$submitted = $_GET['submitted'];
/**
 * Login to SAP system
 * @param String $user
 * @param String $pwd
 */
function login($user, $pwd) {
	//Create SAPRFC instance
	$sap = new saprfc(array(
			"logindata" =>; array(
			      "ASHOST"	=>; "HOSTNAME",
			      "SYSNR"	=>; "SYSTEM NUMBER",
			      "CLIENT"	=>; "CLIENT NUMBEr",
		              "USER"	=>; "USERNAME",
			      "PASSWD"	=>; "PASSWORD"
					),
			"show_errors"=>;true,
			"debug"	     =>;false));

	return $sap;
}
function logoff($sap) {
	$sap->;logoff();
}
/**
 * Function to call SAP RFC
 * @param saprfc $sap
 */
function callRFC($sap, $params) {
	$cust_params = $params['cust_params'];
	$task_params = $params['task_params'];
	$proj_params = $params['proj_params'];
	$result = $sap->;callFunction("ZGRAPH_TOTALDAYS_RFC",
             array(
                   array("IMPORT","CUST_PARAMS",$cust_params),
                   array("IMPORT","TASK_PARAMS",$task_params),
                   array("IMPORT","PROJ_PARAMS",$proj_params),
                   array("EXPORT","CATEGORIES",array()),
                   array("EXPORT","DATA_ACTUAL",array()),
                   array("EXPORT","DATA_ESTIMATE",array())
				       )
					);
	return $result;
}

Note the callRFC function: Importing parameters on the SAP RFC are passed to the $sap->callFunction function as “IMPORT” and Exporting parameters are defined as “EXPORT”. By leaving the “debug” value as ‘true’ on the login function, it is possible to get debug output for each SAP RFC call, as seen here:

SAP PHP Debug Output

Now you’re ready to go out in to the world with your own PHP program that connects to SAP! Good Luck!

Here’s a final look at what my program looks like. It connects to SAP to pull out project data for reporting using the JavaScript HighCharts library:

Charts from SAP Data in PHP!

Use of EDXL protocol suite in Sahana implementations

Sahana at the Haiti Earthquake
Sahana at the Haiti Earthquake

Sahana is considered to be the premier Disaster Management System available in the world today. It jump-started the Humanitarian Free and Open Source Software (HFOSS) movement, and has been implemented in various disaster situations around the world.

The Emergencey Data eXchange Language (EDXL) protocol suite is a set of protocols defined by the Organization for the Advancement of Structured Information Standards (OASIS) for communication between various organizations responding to a disaster/emergency situation. As a standard, EDXL allows disaster management software and personnel affiliated with different protocols to communicate with each other on a common platform. Therefore it is essential for the usability of any international disaster management system be compliant with EDXL, and Sahana is no exception.

I have taken the time to document some instances which prove Sahana’s extensive compliance with the EDXL protocol suite.

In 2010, a magnitude 7 earthquake struck the Caribbean nation of Haiti, in the city of Port-au-Prince. Tens of thousands of civilians were killed instantly, while thousands more were trapped beneath rubble, as well as leaving millions of civilians homeless.

During the Sahana Software Foundation’s response to the Haiti Earthquake[6], it deployed the Sahana Haiti 2010 Earthquake Disaster Response Portal – a live and active website hosted at http://haiti.sahanafoundation.org, which provided the following features for the use of disaster responders:

•Organization Registry: This was considered as Phase 1 of the implementation of Sahana. The Organization Registry was used to track the information and activities of the organizations involved in the disaster relief effort, as well as what assets and resources they had available to them. This module made use of the RM and CAP protocols from the EDXL protocol suite.

• Food Cluster Food Request Portal : Designed in order to collaborate with the World Food Program (WFP)’s efforts to distribute food properly among the victims of the disaster. It was used to note the availability of food resources as well as the distribution of these resources. This module made use of the RM protocol of the EDXL protocol suite. An SMS-based notification tool developed for this module is planned to be used as a component of the World Food Programme’s future systems.

• Hospital Management System (HMS) : This module was used to track the details of the hospitals available for the disaster relief efforts in Haiti, as well as other medical institutions. The EDXL-HAVE (Hospital AVailability Exchange) protocol was utilized to transmit data about hospitals in operations related to this module. Various instances of the Hospital Management System were able to transmit data between one another using HAVE successfully.

• Persons Registry : This is one of the main components of Sahana (Vesuvius) which was also implemented to search for missing victims of the Haiti Earthquake. This module was used to register the victims of the Earthquake who were missing and to track them via a collaborative method, by providing access to the system online. In order to store and transmit people data, this system made use of the CAP protocol from the EDXL Suite.

• Disaster Victim Identification Registry (DVI) : This registry was used as a module in the Haiti Disaster relief effort to identify and store the details of verified victims of the disaster. CAP (Common Access Protocol) of the EDXL protocol suite was used in this module.

• Shelter Registry : EDXL-RM was used in this module along with CAP to enable the reporting of the status of shelters available for victims of the Haiti Earthquake. By having an extensive methodology available to track the shelters available, the disaster relief workers were able to assign displaced victims to shelters effectively.

Out of the EDXL family, EDXL-HAVE was extensively used along with Person Finder Interchange Format (PFIF). EDXL-HAVE proved effective in the Hospital Management System and the Disaster Victim Identification Registry. This proved to be the ideal protocol needed to handle the immense amounts of data that needed to be exchanged in order to coordinate the Hospital Management effort.

Sources:

  • Prustalis, M., Bodduluri, P., de Silva, C., Bitner, D., Boon, F., König, D., Treadgold, G. (2010), The Sahana Software Foundation response to the 2010 Haiti Earthquake: A New Standard for Free and Open Source Disaster Data Management Systems
  • Rencoret, N., Stoddard, A., Haver, K., Taylor, G., Harvery, P. (2010), Haiti Earthquake Response – Context Analysis
  • Sahana Software Foundation official web page – http://www.sahanafoundation.org

Misadventures of a SAP n00b

SAP is considered to be the best Enterprise Resource Planning system in the world today, and there are thousands of SAP implementations in many mega-corporations around the world.

I had the opportunity to mess around (at a very beginner level) with ABAP and some Z Programs on the SD and MM modules, and would like to document some of the things I learnt. Especially after reading an awesome SAP blog by my friend Jeewana Premarathne at http://implementsap.blogspot.com, I thought of documenting my (rather minimal) knowledge as an ABAP programmer on my blog as well.

1. Search keyword chaining

Just like with any other keyword, it is possible to chain the search keyword as well to search for multiple strings at the same time. If any of the search strings are found within the target string, sy-subrc will be set to 0.

e.g.

SEARCH target_string FOR:
  'Search string 1',
  'Search string 2'.
IF sy-subrc = 0.
  "Do something if search string is found

2.  Some useful naming conventions: http://bit.ly/yoZGGp

3.   Function to give the user a dropdown list to choose a file from the server to save to

CALL FUNCTION '/SAPDMC/LSM_F4_SERVER_FILE'
EXPORTING
  directory = 'directory on server'
  filemask = ' '
IMPORTING
  serverfile = "return variable name
EXCEPTIONS
  canceled_by_user = 1
OTHERS = 2.

IF sy-subrc <> 0.
  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
  WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.

The above code will give the user a dropdown list to choose a file from. You can use this file as a variable within the program to write data to or read from. If the file is not available on the server or the directory path you provided is not available, this will result in a dump, so this should be handled accordingly.

4. Using OPEN DATASET to write to a file

Once you have obtained the path to the file, you can use the OPEN DATASET keyword to open this file for writing, and simply write a character string or an internal table to it.

"Open file for writing
OPEN DATASET output_file FOR OUTPUT IN TEXT MODE.

  IF sy-subrc = 0.
    * Write output string to file.
    TRANSFER t_output TO output_file.
  ELSE.
    WRITE 'The file could not be written.'.
  ENDIF.
CLOSE DATASET output_file.

5.   Concatenating char/string type variables

For this, it is possible to use the CONCATENATE keyword. If you want to separate the strings using a delimiter, this can be arranged by adding the SEPARATED BY keyword.

CONCATENATE
  lv_string1,
  lv_string2,
INTO lv_final_string SEPARATED BY 'delimiter'.

That’s about it for now from me. Since I’m such a newbie to this, there may be some mistakes. It should also be noted that SAP ABAP programming is not simply about learning the code and programming away like in other programming languages, but you have to have a thorough understanding of the business involved, as well as its processes. More on that later.

A PHP Singleton Database Access Class for MySQL based on Joomla’s DB Class

I’ve always been enamored with Joomla’s Database Access functions. They removed all the annoyance from accessing MySQL databases and made it an easy-to-do operation. The Joomla database access methodology follows a simple step-by-step sequence:

  1. Create an instance of the db access class.
  2. Assign your query to the newly created object.
  3. Execute the query and load the result, as you would prefer, in to an associative array, a list of arrays, a list of associative lists, an object, a list of objects and so many more.

Couple this with the flexibility of PHP and you have an almost endless list of options that you could turn to when loading results from your MySQL database. Having used this functionality when programming for Joomla, I really wanted to re-use it in my own PHP applications. So I developed the following class based on Joomla’s db access class:

/**
 * @name dbAccess
 *
 * @author Ramindu Deshapriya, derived from Joomla
 *
 * @desc Class to handle database connections
 */
class dbAccess {
	protected static $dParams = array(
						'host' 		=> 'HOST_NAME',
						'user' 		=> 'USERNAME',
						'password' 	=> 'PASSWORD',
						'database'	=> 'DB_NAME'
					);

	protected $_connection = null;

	/**
	 * The database driver name
	 *
	 * @var string
	 */
	protected $_sql = null;

	/**
	 *  The null/zero date string
	 *
	 * @var string
	 */
	protected $_nullDate = '0000-00-00 00:00:00';

	/**
	 * Quote for named objects
	 *
	 * @var string
	 */
	protected $_nameQuote = '`';
	protected static $dbInstance;
	final private function __construct($host,$user,$password,$db) {
		if ( !($this->_connection = @mysql_connect($host,$user,$password,true)) ) {
			die('Unable to connect to database, recheck your DB server');

		}
		mysql_select_db($db, $this->_connection);
		return $this;

	}
	final public static function getInstance() {
		if ( is_null(dbAccess::$dbInstance) ) {
			dbAccess::$dbInstance = new dbAccess(	dbAccess::$dParams['host'],
											dbAccess::$dParams['user'],
											dbAccess::$dParams['password'],
											dbAccess::$dParams['database']
										);
		}
		return dbAccess::$dbInstance;

	}
	public function setQuery($query, $offset = 0, $limit = 0)
	{
		$this->_sql		= $query;
		$this->_limit	= (int) $limit;
		$this->_offset	= (int) $offset;

		return $this;
	}
	public function query()
	{
		if (!is_resource($this->_connection)) {
			echo 'DB Connection not available';
			return false;
		}

		// Take a local copy so that we don't modify the original&nbsp;
  <span></span> //query and cause issues later
	$sql = (string) $this->_sql;
		if ($this->_limit > 0 || $this->_offset > 0) {
			$sql .= ' LIMIT '.$this->_offset.', '.$this->_limit;
		}
		$this->_errorNum = 0;
		$this->_errorMsg = '';
		$this->_cursor = mysql_query($sql, $this->_connection);

		if (!$this->_cursor) {
			$this->_errorNum = mysql_errno($this->_connection);
			$this->_errorMsg = mysql_error($this->_connection)." SQL=$sql";

			return false;
		}
		return $this->_cursor;
	}
	/**
	 * Description
	 *
	 * @access public
	 */
	function insertid()
	{
		return mysql_insert_id( $this->_connection);
	}
	/**
	 * Get a database escaped string
	 *
	 * @param	string	The string to be escaped
	 * @param	boolean Optional parameter to provide extra escaping
	 * @return	string
	 * @access	public
	 * @abstract
	 */
	function getEscaped( $text, $extra = false )
	{
		$result = mysql_real_escape_string( $text, $this->_connection );
		if ($extra) {
			$result = addcslashes( $result, '%_' );
		}
		return $result;
	}
	/**
	* Get a quoted database escaped string
	*
	* @param	string	A string
	* @param	boolean Default true to escape string, false to leave the string unchanged
	* @return	string
	* @access public
	*/
	function Quote( $text, $escaped = true )
	{
		return '\''.($escaped ? $this->getEscaped( $text ) : $text).'\'';
	}
	public function nameQuote($s)
	{
		$q = $this->_nameQuote;

		if (strlen($q) == 1) {
			return $q.$s.$q;
		} else {
			return $q{0}.$s.$q{1};
		}
	}
	public function isQuoted($fieldName)
	{
		if ($this->_hasQuoted) {
			return in_array($fieldName, $this->_quoted);
		} else {
			return true;
		}
	}

/**
	 * This method loads the first field of the first row returned by the query.
	 *
	 * @return	mixed The value returned in the query or null if the query failed.
	 */
	public function loadResult()
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$ret = null;
		if ($row = mysql_fetch_row($cur)) {
			$ret = $row[0];
		}
		mysql_free_result($cur);
		return $ret;
	}

	/**
	 * Load an array of single field results into an array
	 */
	public function loadResultArray($numinarray = 0)
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$array = array();
		while ($row = mysql_fetch_row($cur)) {
			$array[] = $row[$numinarray];
		}
		mysql_free_result($cur);
		return $array;
	}

	/**
	 * Fetch a result row as an associative array
	 *
	 * @return	array
	 */
	public function loadAssoc()
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$ret = null;
		if ($array = mysql_fetch_assoc($cur)) {
			$ret = $array;
		}
		mysql_free_result($cur);
		return $ret;
	}

	/**
	 * Load a assoc list of database rows.
	 *
	 * @param	string	The field name of a primary key.
	 * @param	string	An optional column name.&nbsp;
<span> </span>* Instead of the whole row, only this column value will be in the return array.
	 * @return	array	If key is empty as sequential list of returned records.
	 */
	public function loadAssocList($key = null, $column = null)
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$array = array();
		while ($row = mysql_fetch_assoc($cur)) {
			$value = ($column) ? (isset($row[$column]) ? $row[$column] : $row) : $row;
			if ($key) {
				$array[$row[$key]] = $value;
			} else {
				$array[] = $value;
			}
		}
		mysql_free_result($cur);
		return $array;
	}

	/**
	 * This global function loads the first row of a query into an object.
	 *
	 * @param	string	The name of the class to return (stdClass by default).
	 *
	 * @return	object
	 */
	public function loadObject($className = 'stdClass')
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$ret = null;
		if ($object = mysql_fetch_object($cur, $className)) {
			$ret = $object;
		}
		mysql_free_result($cur);
		return $ret;
	}

	/**
	 * Load a list of database objects
	 *
	 * If key is not empty then the returned array is indexed by the value
	 * the database key.  Returns null if the query fails.
	 *
	 * @param	string	The field name of a primary key
	 * @param	string	The name of the class to return (stdClass by default).
	 *
	 * @return	array	If key is empty as sequential list of returned records.
	 */
	public function loadObjectList($key='', $className = 'stdClass')
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$array = array();
		while ($row = mysql_fetch_object($cur, $className)) {
			if ($key) {
				$array[$row->$key] = $row;
			} else {
				$array[] = $row;
			}
		}
		mysql_free_result($cur);
		return $array;
	}

	/**
	 * Description
	 *
	 * @return The first row of the query.
	 */
	public function loadRow()
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$ret = null;
		if ($row = mysql_fetch_row($cur)) {
			$ret = $row;
		}
		mysql_free_result($cur);
		return $ret;
	}

	/**
	 * Load a list of database rows (numeric column indexing)
	 *
	 * @param	string	The field name of a primary key
	 * @return	array	If key is empty as sequential list of returned records.
	 * If key is not empty then the returned array is indexed by the value
	 * the database key.  Returns null if the query fails.
	 */
	public function loadRowList($key=null)
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$array = array();
		while ($row = mysql_fetch_row($cur)) {
			if ($key !== null) {
				$array[$row[$key]] = $row;
			} else {
				$array[] = $row;
			}
		}
		mysql_free_result($cur);
		return $array;
	}

	/**
	 * Load the next row returned by the query.
	 *
	 * @return	mixed The result of the query as an array, false if there are no more rows, or null on an error.
	 *
	 * @since	1.6.0
	 */
	public function loadNextRow()
	{
		static $cur;

		if (!($cur = $this->query())) {
			return $this->_errorNum ? null : false;
		}

		if ($row = mysql_fetch_row($cur)) {
			return $row;
		}

		mysql_free_result($cur);
		$cur = null;

		return false;
	}

	/**
	 * Load the next row returned by the query.
	 *
	 * @param	string	The name of the class to return (stdClass by default).
	 *
	 * @return	mixed The result of the query as an object, false if there are no more rows, or null on an error.
	 *
	 * @since	1.6.0
	 */
	public function loadNextObject($className = 'stdClass')
	{
		static $cur;

		if (!($cur = $this->query())) {
			return $this->_errorNum ? null : false;
		}

		if ($row = mysql_fetch_object($cur, $className)) {
			return $row;
		}

		mysql_free_result($cur);
		$cur = null;

		return false;
	}

	/**
	 * Inserts a row into a table based on an objects properties
	 *
	 * @param	string	The name of the table
	 * @param	object An object whose properties match table fields
	 * @param	string	The name of the primary key. If provided the object property is updated.
	 */
	public function insertObject($table, &amp;$object, $keyName = NULL)
	{
		$fmtsql = 'INSERT INTO '.$this->nameQuote($table).' (%s) VALUES (%s) ';
		$fields = array();

		foreach (get_object_vars($object) as $k => $v) {
			if (is_array($v) or is_object($v) or $v === NULL) {
				continue;
			}
			if ($k[0] == '_') { // internal field
				continue;
			}
			$fields[] = $this->nameQuote($k);
			$values[] = $this->isQuoted($k) ? $this->Quote($v) : (int) $v;
		}
		$this->setQuery(sprintf($fmtsql, implode(",", $fields) ,  implode(",", $values)));
		if (!$this->query()) {
			return false;
		}
		$id = $this->insertid();
		if ($keyName &amp;&amp; $id) {
			$object->$keyName = $id;
		}
		return true;
	}

	/**
	 * Description
	 *
	 * @param [type] $updateNulls
	 */
	public function updateObject($table, &amp;$object, $keyName, $updateNulls=false)
	{
		$fmtsql = 'UPDATE '.$this->nameQuote($table).' SET %s WHERE %s';
		$tmp = array();

		foreach (get_object_vars($object) as $k => $v) {
			if (is_array($v) or is_object($v) or $k[0] == '_') { // internal or NA field
				continue;
			}

			if ($k == $keyName) {
				// PK not to be updated
				$where = $keyName . '=' . $this->Quote($v);
				continue;
			}

			if ($v === null) {
				if ($updateNulls) {
					$val = 'NULL';
				} else {
					continue;
				}
			} else {
				$val = $this->isQuoted($k) ? $this->Quote($v) : (int) $v;
			}
			$tmp[] = $this->nameQuote($k) . '=' . $val;
		}

		// Nothing to update.
		if (empty($tmp)) {
			return true;
		}

		$this->setQuery(sprintf($fmtsql, implode(",", $tmp) , $where));
		return $this->query();
	}
}

A Singleton Java class for MySQL DB connection

I generally use MySQL along with my Java applications, and re-use a Singleton class I created a while back to connect to the database. It contains insert and query methods, but apart from that, it’s pretty basic. Just thought it might be useful for someone if I post it up here.

import com.mysql.jdbc.Connection;
import java.sql.*;
import java.sql.DriverManager;
/**
 * @desc A singleton database access class for MySQL
 * @author Ramindu
 */
public final class MysqlConnect {
    public Connection conn;
    private Statement statement;
    public static MysqlConnect db;
    private MysqlConnect() {
        String url= "jdbc:mysql://localhost:3306/";
        String dbName = "database_name";
        String driver = "com.mysql.jdbc.Driver";
        String userName = "username";
        String password = "password";
        try {
            Class.forName(driver).newInstance();
            this.conn = (Connection)DriverManager.getConnection(url+dbName,userName,password);
        }
        catch (Exception sqle) {
            sqle.printStackTrace();
        }
    }
    /**
     *
     * @return MysqlConnect Database connection object
     */
    public static synchronized MysqlConnect getDbCon() {
        if ( db == null ) {
            db = new MysqlConnect();
        }
        return db;

    }
    /**
     *
     * @param query String The query to be executed
     * @return a ResultSet object containing the results or null if not available
     * @throws SQLException
     */
    public ResultSet query(String query) throws SQLException{
        statement = db.conn.createStatement();
        ResultSet res = statement.executeQuery(query);
        return res;
    }
    /**
     * @desc Method to insert data to a table
     * @param insertQuery String The Insert query
     * @return boolean
     * @throws SQLException
     */
    public int insert(String insertQuery) throws SQLException {
        statement = db.conn.createStatement();
        int result = statement.executeUpdate(insertQuery);
        return result;

    }

}

Cheers!

Updating your HTC Wildfire from Android 2.2 (Froyo) to 2.3 (Gingerbread)

DISCLAIMER: I WILL ACCEPT NO RESPONSIBILITY WHATSOEVER IF YOUR WILDFIRE GETS BRICKED AS A RESULT OF YOU BEING A DAREDEVIL, OR SIMPLY BEING STUPID. ROOTING YOUR PHONE WILL VOID YOUR WARRANTY. I WOULD NOT RECOMMEND THIS PROCEDURE FOR THE FAINT OF HEART.

NOTE: This procedure will cause all your personal data on the phone such as contacts, messages etc to be lost. Data on the SD card will not be lost. I would suggest backing everything up before you start. There are some awesome tools on the Android Market such as Astro File Manager and SMS Backup and Restore which you can use to backup your applications and messages.

Thanks to the AWESOME people at Cyanogenmod, now you can have Android 2.3 Gingerbread on your HTC Wildfire! I also got some help from the Learn How To Hack blog, which was very informative.

Cyanogenmod incorporates a large amount of awesome tools and provides an authentic experience on your phone based on Gingerbread. I would recommend it to anyone who is tired of the dumb old HTC UI.

WARNING: HTC Sense will NOT work on your phone after you have installed Cyanogenmod. There is NO fix for this yet. (but on the other hand, who needs HTC Sense anyway? :P)

One of the sad things that happens to you is that if you upgrade your HTC Wildfire to Froyo (2.2), you can’t immediately install Cyanogenmod on it. So you need to go back to 2.1 first. Most documentation on this topic won’t tell you how to get back to 2.1.

DOWNLOADS SECTION

Before you start, make sure your phone is set to turn on Debugging mode when it’s connected to the computer. If you don’t know how to do that, I wouldn’t recommend continuing with this operation.

The first thing you need to do is to set your phone’s Security Flag to S-OFF. Doing this enables you to do a factory reset on the phone, and allows you to write to any partition. This is basically the first step towards rooting your phone. An easy way to get S-OFF is to use the Revolutionary tool which is built by Unrevoked and AlpaRev. Download the Revolutionary tool (it truly IS revolutionary), connect your phone on ‘Charge Only’ mode (you’ll have to have drivers installed and HTC Sync off if you’re on Windows) and run it. It’ll set your phone to S-OFF.

Download the USB Drivers for Windows. Make sure HTC Sync is disabled, turn off your phone. Boot it into HBOOT by holding down POWER BUTTON + DOWN VOLUME KEY. Your phone will boot in to the HBOOT interface (which is pretty much like a bootloader on Android). Connect your phone to the PC using the USB cable. The computer will search for drivers, at which point you should point out the drivers you downloaded earlier. They will install, and your phone will now be recognized by Windows as an ‘Android Bootloader interface’.

Now it’s time to go back to Android 2.1 Éclair. Download the RUU Android 2.1 Factory ROM for this, and run it with your phone connected. WARNING! At this point you will lose all your data such as contacts, messages and other personal data stored on the phone. Data on the SD card will not be harmed. Progress of installing Éclair on your phone will be shown both on the GUI interface on your PC and your phone itself. Soon it’ll be back to Éclair, but you won’t have to stare at that outdated desktop for long, because we’ll be moving on.

Now you’ll have to get the UnRevoked tool, which will help you to root your phone. PLEASE NOTE THAT THIS STEP WILL VOID YOUR PHONE’S WARRANTY. Simply download the tool, connect your phone and run it. It’ll set your phone to root.

The next step is to download Cyanogenmod 7 ROM for HTC Wildfire and Google Apps, which will get downloaded as ZIP files. Place these ZIP files on your SD card, and reboot the phone into HBOOT by holding down the POWER BUTTON + VOLUME DOWN BUTTON. In the HBOOT menu, use VOLUME DOWN to navigate through the options, and choose the ‘Recovery’ option using the POWER BUTTON. Your phone will go into ClockworkMod Recovery mode. Here, use the Volume buttons to navigate, but the TRACKBALL to select an option.

  1. Select Wipe data/Factory Reset. (you will get an ‘Are You Sure?’ page, just choose Wipe from the options)
  2. Select Wipe Cache Partition.
  3. Select Install ZIP from SD Card
  4. Choose the Cyanogenmod ZIP file you copied to the SD Card earlier and let it install.
  5. Select Install ZIP from SD Card again.
  6. Choose the Google Apps ZIP file you copied to the SD Card earlier and let it install as well.
  7. Reboot your phone. It’ll boot into Cyanogenmod!

Enjoy your new-found OS! 🙂

Sahana Agasti – Vesuvius

I thought of writing up a blog post related to my ongoing Google Summer of Code project, which is to re-implement the translation structure in Sahana Agasti’s Vesuvius release.

Sahana, as you may or may not know, is one of the most prominent HFOSS (Humanitarian FOSS) projects in the world, and is aimed primarily at disaster management. It was developed as a response to the December Tsunami in 2004 which devastated South Asian countries such as Sri Lanka, where Sahana originated. Sahana was brought forward at the time as an effort to co-ordinate the search for missing persons, manage shelters and the distribution of relief efforts.

Due to its robust structure and easy-to-deploy nature, Sahana has been deployed around the world as the primary disaster management system for most situations. The floods in Pakistan and India were mitigated with the aid of Sahana. The City of New York maintains its own installation of Sahana as a measure to manage disasters that could occur. Recently, Sahana was deployed in response to the tsunami in Japan as well.

Sahana is mainly comprised of 2 main projects: Eden and Agasti. Eden is written in Python as a desktop application that can help with co-ordinating the disaster relief effort from a centralized management point. Agasti is written in PHP and focuses on providing a more widespread relief effort through providing interconnection between a large number of disaster relief centers.

Agasti is in turn, made up of 2 parts: Mayon and Vesuvius. While Mayon focuses on statistical analysis and predictions, Vesuvius focuses primarily on finding missing persons. And it is Vesuvius that I’m doing my project on.

Vesuvius has a simple yet efficient structure which helps to set up a disaster event and co-ordinate efforts between a large number of triage centers over a large area very quickly. The most interesting thing about Vesuvius is that it can manage an advanced Access Control List that can include a large number of different types of users from Researchers to Administrators, each with their own set of privileges on-site.

The process of reporting a missing person on Vesuvius is pretty straightforward and self-explanatory. This design would help people with little experience in using Vesuvius.

My project focuses on making Vesuvius more easily translatable, so that it can be adapted for different disaster situations in different locales quickly and effectively. More on that later.