<?php
/**
 * Contains the classes for updating database tables
 *
 * @license GNU
 * @author marcan <marcan@smartfactory.ca>
 * @version $Id: databaseupdater.php 7996 2009-01-27 14:05:34Z malanciault $
 * @link http://www.smartfactory.ca The SmartFactory
 * @package SmartObject
 */
/**
 * IcmsDatabasetable class
 *
 * Information about an individual table
 *
 * @package SmartObject
 * @author marcan <marcan@smartfactory.ca>
 * @link http://www.smartfactory.ca The SmartFactory
 */
if (!defined("XOOPS_ROOT_PATH")) {
	die("ImpressCMS root path not defined");
}

/**
 * Include the language constants for the IcmsDatabaseupdater
 */
global $xoopsConfig;
$common_file = XOOPS_ROOT_PATH . "/language/" . $xoopsConfig['language'] . "/databaseupdater.php";
if (!file_exists($common_file)) {
	$common_file = XOOPS_ROOT_PATH . "/language/" . $xoopsConfig['language'] . "/databaseupdater.php";
}
include ($common_file);

class IcmsDatabasetable {
	/**
	 * @var string $_name name of the table
	 */
	var $_name;
	/**
	 * @var string $_structure structure of the table
	 */
	var $_structure;

	/**
	 * @var array $_data containing valued of each records to be added
	 */
	var $_data;

	/**
	 * @var array $_alteredFields containing fields to be altered
	 */
	var $_alteredFields;

	/**
	 * @var array $_newFields containing new fields to be added
	 */
	var $_newFields;

	/**
	 * @var array $_dropedFields containing fields to be droped
	 */
	var $_dropedFields;

	/**
	 * @var array $_flagForDrop flag table to drop it
	 */
	var $_flagForDrop = false;

	/**
	 * @var array $_updateAll containing items to be updated
	 */
	var $_updateAll;

	/**
	 * @var array $_deleteAll containing items to be deleted
	 */
	var $_deleteAll;

	var $_existingFieldsArray=false;

	/**
	 * @var bool $force force the query even in a GET process
	 */
	var $force=false;

	/** For backward compat */
	var $_db;

	/**
	 * xoopsDB database object
	 *
	 * @var @link XoopsDatabase object
	 */
	var $db;

	/**
	 * Constructor
	 *
	 * @param string $name name of the table
	 *
	 */
	function IcmsDatabasetable($name) {
		global $xoopsDB;

		$this->db = $xoopsDB;
		/** For backward compat */
		$this->_db = $xoopsDB;

		$this->_name = $name;
		$this->_data = array ();
	}

	/**
	 * Return the table name, prefixed with site table prefix
	 *
	 * @return string table name
	 *
	 */
	function name() {
		return $this->_db->prefix($this->_name);
	}

	/**
	 * Detemines if a table exists in the current db
	 *
	 * Checks if the table already exists in the database
	 * @return bool True if table exists, false if not
	 *
	 * @access public
	 * @author xhelp development team
	 */
	function exists() {
		$table = $this->_name;
		$bRetVal = false;
		//Verifies that a MySQL table exists
		$realname = $this->_db->prefix($table);
		$ret = mysql_list_tables(XOOPS_DB_NAME, $this->_db->conn);
		while (list ($m_table) = $this->_db->fetchRow($ret)) {
			if ($m_table == $realname) {
				$bRetVal = true;
				break;
			}
		}
		$this->_db->freeRecordSet($ret);
		return ($bRetVal);
	}

	function getExistingFieldsArray() {
		$sql = "SHOW COLUMNS FROM " . $this->name();
		$result = $this->_db->queryF($sql);
		while ($existing_field = $this->_db->fetchArray($result)) {
			$fields[$existing_field['Field']] = $existing_field['Type'];
			if ($existing_field['Null'] != "YES") {
				$fields[$existing_field['Field']] .= " NOT NULL";
			}
			if ($existing_field['Extra']) {
				$fields[$existing_field['Field']] .= " " . $existing_field['Extra'];
			}
			if (!($existing_field['Default'] === NULL) && ($existing_field['Default'] || $existing_field['Default'] == '' || $existing_field['Default'] == 0)) {
				$fields[$existing_field['Field']] .= " default '" . $existing_field['Default'] . "'";
			}
		}
		return $fields;
	}
	function fieldExists($field) {
		$existingFields = $this->getExistingFieldsArray();
		return isset ($existingFields[$field]);
	}
	/**
	 * Set the table structure
	 *
	 * Example :
	 *
	 *     	$table->setStructure("`transactionid` int(11) NOT NULL auto_increment,
	 * 				  `date` int(11) NOT NULL default '0',
	 * 				  `status` int(1) NOT NULL default '-1',
	 * 				  `itemid` int(11) NOT NULL default '0',
	 * 				  `uid` int(11) NOT NULL default '0',
	 * 				  `price` float NOT NULL default '0',
	 * 				  `currency` varchar(100) NOT NULL default '',
	 * 				  PRIMARY KEY  (`transactionid`)");
	 *
	 * @param  string $structure table structure
	 *
	 */
	function setStructure($structure) {
		$this->_structure = $structure;
	}
	/**
	* Return the table structure
	*
	* @return string table structure
	*
	*/
	function getStructure() {
		return sprintf($this->_structure, $this->name());
	}
	/**
	 * Add values of a record to be added
	 *
	 * @param string $data values of a record
	 *
	 */
	function setData($data) {
		$this->_data[] = $data;
	}

	/**
	 * Get the data array
	 *
	 * @return array containing the records values to be added
	 *
	 */
	function getData() {
		return $this->_data;
	}
	/**
	 * Use to insert data in a table
	 *
	 * @return bool true if success, false if an error occured
	 *
	 */
	function addData() {
		foreach ($this->getData() as $data) {
			$query = sprintf('INSERT INTO %s VALUES (%s)', $this->name(), $data);
			if ($this->force) {
				$ret = $this->_db->queryF($query);
			} else {
				$ret = $this->_db->query($query);
			}
			if (!$ret) {
				echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_ADD_DATA_ERR, $this->name()) . "<br />";
			} else {
				echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_ADD_DATA, $this->name()) . "<br />";
			}
		}
		return $ret;
	}
	/**
	 * Add a field to be added
	 *
	 * @param string $name name of the field
	 * @param string $properties properties of the field
	 *
	 */
	function addAlteredField($name, $properties, $newname=false, $showerror = true) {
		$field['name'] = $name;
		$field['properties'] = $properties;
		$field['showerror'] = $showerror;
		$field['newname'] = $newname;
		$this->_alteredFields[] = $field;
	}

	/**
	 * Add new field of a record to be added
	 *
	 * @param string $name name of the field
	 * @param string $properties properties of the field
	 *
	 */
	function addNewField($name, $properties) {
		$field['name'] = $name;
		$field['properties'] = $properties;
		$this->_newFields[] = $field;
	}
	/**
	 * Get fields that need to be altered
	 *
	 * @return array fields that need to be altered
	 *
	 */
	function getAlteredFields() {
		return $this->_alteredFields;
	}
	/**
	 * Add item to be updated on the the table via the UpdateAll method
	 *
     * @param   string  $fieldname  Name of the field
     * @param   string  $fieldvalue Value to write
     * @param   object  $criteria   {@link CriteriaElement}
     * @param 	bool	$fieldvalueIsOperation TRUE if fieldvalue is an operation, for example, conf_order+1
	 *
     * @return  bool
	 */
	function addUpdateAll($fieldname, $fieldvalue, $criteria, $fieldvalueIsOperation) {
		$item['fieldname'] = $fieldname;
		$item['fieldvalue'] = $fieldvalue;
		$item['criteria'] = $criteria;
		$item['fieldvalueIsOperation'] = $fieldvalueIsOperation;
		$this->_updateAll[] = $item;
	}
	/**
	 * Add item to be updated on the the table via the UpdateAll method
	 *
     * @param   string  $fieldname  Name of the field
     * @param   string  $fieldvalue Value to write
     * @param   object  $criteria   {@link CriteriaElement}
	 *
     * @return  bool
	 */
	function addDeleteAll($criteria) {
		$item['criteria'] = $criteria;
		$this->_deleteAll[] = $item;
	}
	/**
	 * Get new fields to be added
	 *
	 * @return array fields to be added
	 *
	 */
	function getNewFields() {
		return $this->_newFields;
	}
	/**
	 * Get items to be updated
	 *
	 * @return array items to be updated
	 *
	 */
	function getUpdateAll() {
		return $this->_updateAll;
	}
	/**
	 * Get items to be deleted
	 *
	 * @return array items to be deleted
	 *
	 */
	function getDeleteAll() {
		return $this->_deleteAll;
	}
	/**
	 * Add values of a record to be added
	 *
	 * @param string $name name of the field
	 *
	 */
	function addDropedField($name) {
		$this->_dropedFields[] = $name;
	}
	/**
	 * Get fields that need to be droped
	 *
	 * @return array fields that need to be droped
	 *
	 */
	function getDropedFields() {
		return $this->_dropedFields;
	}
	/**
	 * Set the flag to drop the table
	 *
	 */
	function setFlagForDrop() {
		$this->_flagForDrop = true;
	}
	/**
	 * Use to create a table
	 *
	 * @return bool true if success, false if an error occured
	 *
	 */
	function createTable() {
		$query = $this->getStructure();
		$query = "CREATE TABLE `" . $this->name() . "` (" . $query . ") TYPE=MyISAM";

		if ($this->force) {
			$ret = $this->_db->queryF($query);
		} else {
			$ret = $this->_db->query($query);
		}
		if (!$ret) {
			echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_CREATE_TABLE_ERR, $this->name()) . " (" . $this->_db->error(). ")<br />";

		} else {
			echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_CREATE_TABLE, $this->name()) . "<br />";
		}
		return $ret;
	}
	/**
	 * Use to drop a table
	 *
	 * @return bool true if success, false if an error occured
	 *
	 */
	function dropTable() {
		$query = sprintf("DROP TABLE %s", $this->name());
		if ($this->force) {
			$ret = $this->_db->queryF($query);
		} else {
			$ret = $this->_db->query($query);
		}
		if (!$ret) {
			echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_DROP_TABLE_ERR, $this->name()) . " (" . $this->_db->error(). ")<br />";
			return false;
		} else {
			echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_DROP_TABLE, $this->name()) . "<br />";
			return true;
		}
	}
	/**
	 * Use to alter a table
	 *
	 * @return bool true if success, false if an error occured
	 *
	 */
	function alterTable() {
		$ret = true;

		foreach ($this->getAlteredFields() as $alteredField) {
			if (!$alteredField['newname']) {
				$alteredField['newname'] = $alteredField['name'];
			}

			$query = sprintf("ALTER TABLE `%s` CHANGE `%s` `%s` %s", $this->name(), $alteredField['name'], $alteredField['newname'], $alteredField['properties']);
			if ($this->force) {
				$ret = $ret && $this->_db->queryF($query);
			} else {
				$ret = $ret && $this->_db->query($query);
			}

			if ($alteredField['showerror']) {
				if (!$ret) {
				echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_CHGFIELD_ERR, $alteredField['name'], $this->name()) . " (" . $this->_db->error(). ")<br />";
				} else {
					echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_CHGFIELD, $alteredField['name'], $this->name()) . "<br />";
				}
			}
		}

		return $ret;
	}
	/**
	 * Use to add new fileds in the table
	 *
	 * @return bool true if success, false if an error occured
	 *
	 */
	function addNewFields() {
		$ret = true;
		foreach ($this->getNewFields() as $newField) {
			$query = sprintf("ALTER TABLE `%s` ADD `%s` %s", $this->name(), $newField['name'], $newField['properties']);

			if ($this->force) {
				$ret = $ret && $this->_db->queryF($query);
			} else {
				$ret = $ret && $this->_db->query($query);
			}

			if (!$ret) {
				echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_NEWFIELD_ERR, $newField['name'], $this->name()) . "<br />";
			} else {
				echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_NEWFIELD, $newField['name'], $this->name()) . "<br />";
			}
		}
		return $ret;
	}

    /**
     * Change a value for objects with a certain criteria
     *
     * @param   string  $fieldname  Name of the field
     * @param   string  $fieldvalue Value to write
     * @param   object  $criteria   {@link CriteriaElement}
     *
     * @return  bool
     **/
    function updateAll()
    {
    	$ret = true;
    	foreach ($this->getUpdateAll() as $item) {
    		$fieldname = $item['fieldname'];
    		$fieldvalue = $item['fieldvalue'];
    		$criteria = isset($item['criteria']) ? $item['criteria'] : null;

    		$set_clause = $fieldname . ' = ';
	    	if ( is_numeric( $fieldvalue ) || $item['fieldvalueIsOperation']) {
	    		$set_clause .=  $fieldvalue;
	    	} elseif ( is_array( $fieldvalue ) ) {
	    		$set_clause .= $this->_db->quoteString( implode( ',', $fieldvalue ) );
	    	} else {
	    		$set_clause .= $this->_db->quoteString( $fieldvalue );
	    	}
	        $sql = 'UPDATE '.$this->name().' SET '.$set_clause;
	        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
	            $sql .= ' '.$criteria->renderWhere();
	        }
	        if ($this->force) {
	        	$ret = $this->_db->queryF($sql);
	        } else {
	        	$ret = $this->_db->query($sql);
	        }
            if (!$ret) {
	        	echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_UPDATE_TABLE_ERR, $this->name()) . " (" . $this->_db->error(). ")<br />";
	        } else {
	        	echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_UPDATE_TABLE, $this->name()) . "<br />";
	        }
    	}
    	return $ret;
    }

    /**
     * delete all objects meeting the conditions
     *
     * @param object $criteria {@link CriteriaElement} with conditions to meet
     * @return bool
     */

    function deleteAll()
    {
		$ret = true;
    	foreach ($this->getDeleteAll() as $item) {
    		$criteria = isset($item['criteria']) ? $item['criteria'] : null;
	        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
	            $sql = 'DELETE FROM '.$this->table;
	            $sql .= ' '.$criteria->renderWhere();
	            if ($this->force) {
	            	$result = $this->_db->queryF($sql);
	            } else {
	            	$result = $this->_db->query($sql);
	            }
	            if (!$result) {
	                echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_DELETE_TABLE_ERR, $this->name()) . " (" . $this->_db->error(). ")<br />";
	            } else {
	            	echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_DELETE_TABLE, $this->name()) . " (" . $this->_db->error(). ")<br />";
	            }
	        }
	        $ret = $result && $ret;
    	}
	    return $ret;
       }


	/**
	 * Use to drop fields
	 *
	 * @return bool true if success, false if an error occured
	 *
	 */
	function dropFields() {
		$ret = true;
		foreach ($this->getdropedFields() as $dropedField) {
			$query = sprintf("ALTER TABLE %s DROP %s", $this->name(), $dropedField);
			if ($this->force) {
				$ret = $ret && $this->_db->queryF($query);
			} else {
				$ret = $ret && $this->_db->query($query);
			}

			if (!$ret) {
				echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_DROPFIELD_ERR, $dropedField, $this->name()) . " (" . $this->_db->error(). ")<br />";
			} else {
				echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_DROPFIELD, $dropedField, $this->name()) . "<br />";
			}
		}
		return $ret;
	}
}
/**
 * IcmsDatabaseupdater class
 *
 * Class performing the database update for the module
 *
 * @package SmartObject
 * @author marcan <marcan@smartfactory.ca>
 * @link http://www.smartfactory.ca The SmartFactory
 */
class IcmsDatabaseupdater {

	var $_dbTypesArray;

	/**
	 * xoopsDB database object
	 *
	 * @var @link XoopsDatabase object
	 */
	var $_db;
	var $db;

	function IcmsDatabaseupdater() {
		global $xoopsDB;

		// backward compat
		$this->_db = $xoopsDB;
		$this->db = $xoopsDB;

		$this->_dbTypesArray[XOBJ_DTYPE_TXTBOX] = 'varchar(255)';
		$this->_dbTypesArray[XOBJ_DTYPE_TXTAREA] = 'text';
		$this->_dbTypesArray[XOBJ_DTYPE_INT] = 'int(11)';
		$this->_dbTypesArray[XOBJ_DTYPE_URL] = 'varchar(255)';
		$this->_dbTypesArray[XOBJ_DTYPE_EMAIL] = 'varchar(255)';
		$this->_dbTypesArray[XOBJ_DTYPE_ARRAY] = 'text';
		$this->_dbTypesArray[XOBJ_DTYPE_OTHER] = 'text';
		$this->_dbTypesArray[XOBJ_DTYPE_SOURCE] = 'text';
		$this->_dbTypesArray[XOBJ_DTYPE_STIME] = 'int(11)';
		$this->_dbTypesArray[XOBJ_DTYPE_MTIME] = 'int(11)';
		$this->_dbTypesArray[XOBJ_DTYPE_LTIME] = 'int(11)';
		$this->_dbTypesArray[XOBJ_DTYPE_SIMPLE_ARRAY] = 'text';
		$this->_dbTypesArray[XOBJ_DTYPE_CURRENCY] = 'text';
		$this->_dbTypesArray[XOBJ_DTYPE_FLOAT] = 'float';
		$this->_dbTypesArray[XOBJ_DTYPE_TIME_ONLY] = 'int(11)';
		$this->_dbTypesArray[XOBJ_DTYPE_URLLINK] = 'int(11)';
		$this->_dbTypesArray[XOBJ_DTYPE_FILE] = 'int(11)';
		$this->_dbTypesArray[XOBJ_DTYPE_IMAGE] = 'varchar(255)';
	}
	/**
	 * Use to execute a general query
	 *
	 * @param string $query query that will be executed
	 * @param string $goodmsg message displayed on success
	 * @param string $badmsg message displayed on error
	 * @param bool 	$force force the query even in a GET process
	 *
	 * @return bool true if success, false if an error occured
	 *
	 */
	function runQuery($query, $goodmsg, $badmsg, $force=false) {
		if ($force) {
			$ret = $this->_db->queryF($query);
		} else {
			$ret = $this->_db->query($query);
		}

		if (!$ret) {
			echo "&nbsp;&nbsp;$badmsg<br />";
			return false;
		} else {
			echo "&nbsp;&nbsp;$goodmsg<br />";
			return true;
		}
	}
	/**
	 * Use to rename a table
	 *
	 * @param string $from name of the table to rename
	 * @param string $to new name of the renamed table
	 * @param bool 	$force force the query even in a GET process
	 *
	 * @return bool true if success, false if an error occured
	 */
	function renameTable($from, $to, $force=false) {
		$from = $this->_db->prefix($from);
		$to = $this->_db->prefix($to);
		$query = sprintf("ALTER TABLE %s RENAME %s", $from, $to);
		if ($force) {
			$ret = $this->_db->queryF($query);
		} else {
			$ret = $this->_db->query($query);
		}
		if (!$ret) {
			echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_RENAME_TABLE_ERR, $from) . "<br />";
			return false;
		} else {
			echo "&nbsp;&nbsp;" . sprintf(_DATABASEUPDATER_MSG_RENAME_TABLE, $from, $to) . "<br />";
			return true;
		}
	}
	/**
	 * Use to update a table
	 *
	 * @param object $table {@link IcmsDatabasetable} that will be updated
	 * @param bool	$force force the query even in a GET process
	 *
	 * @see IcmsDatabasetable
	 *
	 * @return bool true if success, false if an error occured
	 */
	function updateTable($table, $force=false) {
		$ret = true;
		$table->force = $force;

		// If table has a structure, create the table
		if ($table->getStructure()) {
			$ret = $table->createTable() && $ret;
		}
		// If table is flag for drop, drop it
		if ($table->_flagForDrop) {
			$ret = $table->dropTable() && $ret;
		}
		// If table has data, insert it
		if ($table->getData()) {
			$ret = $table->addData() && $ret;
		}
		// If table has new fields to be added, add them
		if ($table->getNewFields()) {
			$ret = $table->addNewFields() && $ret;
		}
		// If table has altered field, alter the table
		if ($table->getAlteredFields()) {
			$ret = $table->alterTable() && $ret;
		}
		// If table has droped field, alter the table
		if ($table->getDropedFields()) {
			$ret = $table->dropFields() && $ret;
		}
		// If table has updateAll items, update the table
		if ($table->getUpdateAll()) {
			$ret = $table->updateAll() && $ret;
		}
		return $ret;
	}

	/**
	 * Upgrade automaticaly an item of a module
	 *
	 * Note that currently, $item needs to represent the name of an object derived
	 * from SmartObject, for example, $item == 'invoice' wich will represent $dirnameInvoice
	 * for example SmartbillingInvoice which extends SmartObject class
	 *
	 * @param string $dirname dirname of the module
	 * @param mixed $item name or array of names of the item to upgrade
	 */
	function automaticUpgrade($dirname, $item) {
		if (is_array($item)) {
			foreach($item as $v) {
				$this->upgradeObjectItem($dirname, $v);
			}
		} else {
			$this->upgradeObjectItem($dirname, $item);
		}
	}

	/**
	 * Get the type of the field based on the info of the var
	 *
	 * @param array $var array containing information about the var
	 * @return string type of the field
	 */
	function getFieldTypeFromVar($var) {
		$ret = isset($this->_dbTypesArray[$var['data_type']]) ? $this->_dbTypesArray[$var['data_type']] : 'text';
		return $ret;
	}

	/**
	 * Get the default value based on the info of the var
	 *
	 * @param array $var array containing information about the var
	 * @param bool $key TRUE if the var is the primary key
	 * @return string default value
	 */
	function getFieldDefaultFromVar($var, $key = false) {
		if ($var['data_type'] == XOBJ_DTYPE_TXTAREA) {
			return 'nodefault';
		} elseif ($var['value']) {
			return $var['value'];
		} else {
			if (in_array($var['data_type'], array(
							XOBJ_DTYPE_INT,
							XOBJ_DTYPE_STIME,
							XOBJ_DTYPE_MTIME,
							XOBJ_DTYPE_LTIME,
							XOBJ_DTYPE_TIME_ONLY,
							XOBJ_DTYPE_URLLINK,
							XOBJ_DTYPE_FILE
						))) {
				return '0';
			} else {
				return '';
			}
		}
	}

	function upgradeObjectItem($dirname, $item) {
		$module_handler = xoops_getModuleHandler($item, $dirname);
		if (!$module_handler) {
			return false;
		}

		$table = new IcmsDatabasetable($dirname . '_' . $item);
		$object = $module_handler->create();
		$objectVars = $object->getVars();

		if (!$table->exists()) {
			// table was never created, let's do it
			$structure = "";
			foreach($objectVars as $key=>$var) {
				if ($var['persistent']) {
					$type = $this->getFieldTypeFromVar($var);
					if ($key == $module_handler->keyName) {
						$extra = "auto_increment";
					} else {
						$default =  $this->getFieldDefaultFromVar($var);
						if ($default != 'nodefault') {
							$extra = "default '$default'
";
						} else {
							$extra = false;
						}
					}
					if ($extra) {
						$structure .= "`$key` $type not null $extra,
";
					} else {
						$structure .= "`$key` $type not null,
";
					}

				}
			}
			$structure .= "PRIMARY KEY  (`" . $module_handler->keyName . "`)
";
			$table->setStructure($structure);
			if (!$this->updateTable($table)) {
		        /**
		         * @todo trap the errors
		         */
		    }
		} else {
			$existingFieldsArray = $table->getExistingFieldsArray();
			foreach($objectVars as $key=>$var) {
				if ($var['persistent']) {
					if (!isset($existingFieldsArray[$key])) {
						// the fiels does not exist, let's create it
						$type = $this->getFieldTypeFromVar($var);
						$default =  $this->getFieldDefaultFromVar($var);
						if ($default != 'nodefault') {
							$extra = "default '$default'";
						} else {
							$extra = false;
						}
						$table->addNewField($key, "$type not null " . $extra);
					} else {
						// if field already exists, let's check if the definition is correct
						$definition =  strtolower($existingFieldsArray[$key]);
						$type = $this->getFieldTypeFromVar($var);

						if ($key == $module_handler->keyName) {
							$extra = "auto_increment";
						} else {
							$default =  $this->getFieldDefaultFromVar($var, $key);
							if ($default != 'nodefault') {
								$extra = "default '$default'";
							} else {
								$extra = false;
							}
						}
						$actual_definition = "$type not null";
						if ($extra) {
							$actual_definition .= " $extra";
						}
						if ($definition != $actual_definition) {
							$table->addAlteredField($key, $actual_definition);
						}
					}
				}
			}

			// check to see if there are some unused fields left in the table
			foreach ($existingFieldsArray as $key=>$v) {
				if (!isset($objectVars[$key]) || !$objectVars[$key]['persistent']) {
					$table->addDropedField($key);
				}
			}

			if (!$this->updateTable($table)) {
		        /**
		         * @todo trap the errors
		         */
		    }
		}
	}
	/**
	 * Insert a config in System Preferences
	 *
	 * @param int $conf_catid
	 * @param string $conf_name
	 * @param string $conf_title
	 * @param mixed $conf_value
	 * @param string $conf_desc
	 * @param string $conf_formtype
	 * @param string $conf_valuetype
	 * @param int $conf_order
	 */
	function insertConfig($conf_catid, $conf_name, $conf_title, $conf_value, $conf_desc, $conf_formtype, $conf_valuetype, $conf_order) {
	    $configitem_handler = xoops_gethandler('configitem');
	    $configitemObj = $configitem_handler->create();
	    $configitemObj->setVar('conf_modid', 0);
	    $configitemObj->setVar('conf_catid', $conf_catid);
	    $configitemObj->setVar('conf_name', $conf_name);
	    $configitemObj->setVar('conf_title', $conf_title);
	    $configitemObj->setVar('conf_value', $conf_value);
	    $configitemObj->setVar('conf_desc', $conf_desc);
	    $configitemObj->setVar('conf_formtype', $conf_formtype);
	    $configitemObj->setVar('conf_valuetype', $conf_valuetype);
	    $configitemObj->setVar('conf_order', $conf_order);
	    if (!$configitem_handler->insert($configitemObj)) {
	    	echo "Unable to insert config $conf_name'<br />";
	    } else{
			echo "Successfully inserted '$conf_name' config<br />";
	    }
	}
	function moduleUpgrade(&$module) {
		$dirname = $module->getVar('dirname');

	    ob_start();

	    $dbVersion  = $module->getDbversion();

	    $newDbVersion = constant(strtoupper($dirname . '_db_version')) ? constant(strtoupper($dirname . '_db_version')) : 0;
		echo 'Current database version : ' . $dbVersion . '<br />';
		echo '&nbsp;&nbsp;Latest database version : ' . $newDbVersion . '<br />';

	    if ($newDbVersion > $dbVersion) {
	    	for($i=$dbVersion+1;$i<=$newDbVersion; $i++) {
				$upgrade_function = $dirname . '_db_upgrade_' . $i;
				if (function_exists($upgrade_function)) {
					$upgrade_function();
				}
	    	}
	    }

		echo "<code>" . _DATABASEUPDATER_UPDATE_UPDATING_DATABASE . "<br />";

		// if there is a function to execute for this DB version, let's do it
		//$function_

		$module_info = XoopsModuleHandler::getByDirname($dirname);
		$this->automaticUpgrade($dirname, $module_info->modinfo['object_items']);

		echo "</code>";

	    $feedback = ob_get_clean();
	    if (method_exists($module, "setMessage")) {
	        $module->setMessage($feedback);
	    } else {
	        echo $feedback;
	    }
	    $this->updateModuleDBVersion($newDbVersion, $dirname);
	    return true;
	}

	/**
	 * Update the DBVersion of a module
	 *
	 * @param int $newDVersion new database version
	 * @param string $dirname dirname of the module
	 *
	 * @return TRUE if success FALSE if not
	 */
	function updateModuleDBVersion($newDBVersion, $dirname) {
		if (!$dirname) {
			$dirname = icms_getCurrentModuleName();
		}
		$module = XoopsModuleHandler::getByDirname($dirname);
		$module->setVar('dbversion', $newDBVersion);
		$module_handler = xoops_getHandler('module');

		if (!$module_handler->insert($module)) {
			$module->setErrors('Unable to update module dbversion');
			return false;
		}
		return true;

	}
}
?>
