Author: ralfbecker
New Revision: 55277
URL: http://svn.stylite.de/viewvc/egroupware?rev=55277&view=rev
Log:
moving so_sql* to Api\Storage
Added:
trunk/egroupware/api/src/Storage/
trunk/egroupware/api/src/Storage.php
- copied, changed from r55263, trunk/etemplate/inc/class.so_sql_cf.inc.php
trunk/egroupware/api/src/Storage/Base.php
- copied, changed from r55263, trunk/etemplate/inc/class.so_sql.inc.php
trunk/egroupware/api/src/Storage/Base2.php
- copied, changed from r55263, trunk/etemplate/inc/class.so_sql2.inc.php
trunk/egroupware/api/src/Storage/Db2DataIterator.php
- copied, changed from r55263, trunk/etemplate/inc/class.so_sql.inc.php
Modified:
trunk/etemplate/inc/class.so_sql.inc.php
trunk/etemplate/inc/class.so_sql2.inc.php
trunk/etemplate/inc/class.so_sql_cf.inc.php
— trunk/etemplate/inc/class.so_sql_cf.inc.php (original)
+++ trunk/egroupware/api/src/Storage.php Sun Mar 6 10:05:20 2016
@@ -3,17 +3,20 @@
+namespace EGroupware\Api;
/**
- Generalized SQL Storage Object with build in custom field support
-
-
- This class allows to display, search, order and filter by custom fields simply by replacing so_sql
-
- This class allows to display, search, order and filter by custom fields simply by replacing Storage\Base
- by it and adding custom field widgets to the eTemplates of an applications.
- It’s inspired by the code from Klaus Leithoff, which does the same thing limited to addressbook.
-
@@ -37,7 +40,7 @@
-
- Please note the different params compared to so_sql!
-
- Please note the different params compared to Storage\Base!
-
-
@param string $app application name to load table schemas
-
@param string $table name of the table to use
@@ -109,17 +112,17 @@
-
@param string $extra_key =’_name’ column name for cf name column (will be prefixed with colum prefix, if starting with _)
-
@param string $extra_value =’_value’ column name for cf value column (will be prefixed with colum prefix, if starting with _)
-
@param string $extra_id =’_id’ column name for cf id column (will be prefixed with colum prefix, if starting with _)
-
-
@param egw_db $db =null database object, if not the one in $GLOBALS[‘egw’]->db should be used, eg. for an other database
-
-
@param boolean $no_clone =true can we avoid to clone the db-object, default yes (different from so_sql!)
-
-
@param Db $db =null database object, if not the one in $GLOBALS[‘egw’]->db should be used, eg. for an other database
-
-
@param boolean $no_clone =true can we avoid to clone the db-object, default yes (different from Storage\Base!)
- new code using appnames and foreach(select(…,$app) can set it to avoid an extra instance of the db object
-
@param boolean $allow_multiple_values =false should we allow AND store multiple values (1:N relations)
-
-
@param string $timestamp_type =null default null=leave them as is, ‘ts’|‘integer’ use integer unix timestamps, ‘object’ use egw_time objects
-
-
@param string $timestamp_type =null default null=leave them as is, ‘ts’|‘integer’ use integer unix timestamps, ‘object’ use DateTime objects
*/
function __construct($app,$table,$extra_table,$column_prefix=’’,
$extra_key=’_name’,$extra_value=’_value’,$extra_id=’_id’,
-
$db=null,$no_clone=true,$allow_multiple_values=false,$timestamp_type=null)
- {
-
// calling the so_sql constructor
-
Db $db=null,$no_clone=true,$allow_multiple_values=false,$timestamp_type=null)
- {
-
// calling the Storage\Base constructor
parent::__construct($app,$table,$db,$column_prefix,$no_clone,$timestamp_type);
$this->allow_multiple_values = $allow_multiple_values;
@@ -147,13 +150,13 @@
// some sanity checks, maybe they should be active only for development
if (!($extra_defs = $this->db->get_table_definitions($app,$extra_table)))
{
-
throw new Exception\WrongParameter("extra table $extra_table is NOT defined!");
}
foreach(array('extra_id','extra_key','extra_value') as $col)
{
if (!$this->$col || !isset($extra_defs['fd'][$this->$col]))
{
@@ -164,7 +167,7 @@
$this->extra_join_order = " LEFT JOIN $extra_table extra_order ON $table.$this->autoinc_id=extra_order.$this->extra_id";
$this->extra_join_filter = " JOIN $extra_table extra_filter ON $table.$this->autoinc_id=extra_filter.$this->extra_id";
-
$this->customfields = Customfields::get($app, false, null, $db);
}
/**
@@ -445,7 +448,7 @@
$criteria[$this->extra_value] = substr($criteria[$this->extra_value],1);
}
$criteria[] = $this->extra_table.’.’.$this->extra_value . ’ ’ .($negate ? 'NOT ’ : ‘’).
@@ -490,7 +493,7 @@
$val = substr($val,1);
}
$cfcriteria[] = ‘(’ . $this->extra_table.’.’.$this->extra_value . ’ ’ .($negate ? 'NOT ’ : ‘’).
@@ -601,7 +604,7 @@
{
$sql_filter = ‘extra_filter.’.$this->extra_value.’!=’.$this->db->quote(substr($val,1));
}
@@ -609,7 +612,7 @@
// Multi-select - any entry with the filter value selected matches
$sql_filter = str_replace($this->extra_value,‘extra_filter.’.
$this->extra_value,$this->db->expression($this->extra_table,array(
-
$this->db->concat("','",$this->extra_value,"','").' '.$this->db->capabilities[egw_db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote('%,'.$val.',%')
-
$this->db->concat("','",$this->extra_value,"','").' '.$this->db->capabilities[Db::CAPABILITY_CASE_INSENSITIV_LIKE].' '.$this->db->quote('%,'.$val.',%')
))
);
}
@@ -617,7 +620,7 @@
{
$sql_filter = str_replace($this->extra_value,‘extra_filter.’.$this->extra_value,
$this->db->expression($this->extra_table,array(
— trunk/etemplate/inc/class.so_sql.inc.php (original)
+++ trunk/egroupware/api/src/Storage/Base.php Sun Mar 6 10:05:20 2016
@@ -1,14 +1,19 @@
<?php
/**
- * eGroupWare generalized SQL Storage Object
+ * EGroupware generalized SQL Storage Object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
- * @package etemplate
+ * @package api
+ * @subpackage storage
* @link http://www.egroupware.org
* @author Ralf Becker
- * @copyright 2002-14 by RalfBecker@outdoor-training.de
+ * @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
+
+namespace EGroupware\Api\Storage;
+
+use EGroupware\Api;
/**
* generalized SQL Storage Object
@@ -18,13 +23,9 @@
* 2) by setting the following documented class-vars in a class derived from this one
* Of cause you can derive from the class and call the constructor with params.
*
- * @package etemplate
- * @subpackage api
- * @author RalfBecker-AT-outdoor-training.de
- * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @todo modify search() to return an interator instead of an array
*/
-class so_sql
+class Base
{
/**
* need to be set in the derived class to the db-table-name
@@ -65,7 +66,7 @@
/**
* protected instance or reference (depeding on $no_clone param of constructor) of the db-object
*
- * @var egw_db
+ * @var Api\Db
*/
protected $db;
/**
@@ -120,7 +121,7 @@
* Possible values:
* - 'ts'|'integer' convert every timestamp to an integer unix timestamp
* - 'string' convert every timestamp to a 'Y-m-d H:i:s' string
- * - 'object' convert every timestamp to a egw_time object
+ * - 'object' convert every timestamp to a Api\DateTime object
*
* @var string
*/
@@ -130,7 +131,7 @@
* or substracted from a user-time to get the server-time
*
* @var int
- * @deprecated use egw_time methods instead, as the offset between user and server time is only valid for current time
+ * @deprecated use Api\DateTime methods instead, as the offset between user and server time is only valid for current time
*/
var $tz_offset_s;
/**
@@ -171,16 +172,14 @@
*
* @param string $app should be set if table-defs to be read from /setup/tables_current.inc.php
* @param string $table should be set if table-defs to be read from /setup/tables_current.inc.php
- * @param egw_db $db database object, if not the one in $GLOBALS['egw']->db should be used, eg. for an other database
+ * @param Api\Db $db database object, if not the one in $GLOBALS['egw']->db should be used, eg. for an other database
* @param string $column_prefix ='' column prefix to automatic remove from the column-name, if the column name starts with it
* @param boolean $no_clone =false can we avoid to clone the db-object, default no
* new code using appnames and foreach(select(...,$app) can set it to avoid an extra instance of the db object
* @param string $timestamp_type =null default null=leave them as is, 'ts'|'integer' use integer unix timestamps,
- * 'object' use egw_time objects or 'string' use DB timestamp (Y-m-d H:i:s) string
- *
- * @return so_sql
- */
- function __construct($app='',$table='',$db=null,$column_prefix='',$no_clone=false,$timestamp_type=null)
+ * 'object' use Api\DateTime objects or 'string' use DB timestamp (Y-m-d H:i:s) string
+ */
+ function __construct($app='',$table='',Api\Db $db=null,$column_prefix='',$no_clone=false,$timestamp_type=null)
{
if ($no_clone)
{
@@ -204,7 +203,7 @@
if ((int) $this->debug >= 4)
{
- echo "
so_sql('$app','$table')
\n";
+ echo "
".__METHOD__."('$app','$table')
\n";
_debug_array($this);
}
$this->set_times($timestamp_type);
@@ -214,7 +213,7 @@
* Set class vars timestamp_type, now and tz_offset_s
*
* @param string|boolean $timestamp_type =false default false do NOT set time_stamptype,
- * null=leave them as is, 'ts'|'integer' use integer unix timestamps, 'object' use egw_time objects,
+ * null=leave them as is, 'ts'|'integer' use integer unix timestamps, 'object' use Api\DateTime objects,
* 'string' use DB timestamp (Y-m-d H:i:s) string
*/
public function set_times($timestamp_type=false)
@@ -225,15 +224,15 @@
switch($this->timestamp_type)
{
case 'object':
- $this->now = new egw_time('now');
+ $this->now = new Api\DateTime('now');
break;
case 'string':
- $this->now = egw_time::to('now',egw_time::DATABASE);
+ $this->now = Api\DateTime::to('now',Api\DateTime::DATABASE);
break;
default:
- $this->now = egw_time::to('now','ts');
- }
- $this->tz_offset_s = egw_time::tz_offset_s();
+ $this->now = Api\DateTime::to('now','ts');
+ }
+ $this->tz_offset_s = Api\DateTime::tz_offset_s();
}
/**
@@ -251,7 +250,7 @@
$this->table_def = $this->db->get_table_definitions($app,$table);
if (!$this->table_def || !is_array($this->table_def['fd']))
{
- throw new egw_exception_wrong_parameter(__METHOD__."('$app','$table'): No table definition for '$table' found !!!");
+ throw new Api\Exception\WrongParameter(__METHOD__."('$app','$table'): No table definition for '$table' found !!!");
}
$this->db_key_cols = $this->db_data_cols = $this->db_cols = array();
$this->autoinc_id = '';
@@ -382,7 +381,7 @@
}
else
{
- $data[$name] = egw_time::server2user($data[$name],$this->timestamp_type);
+ $data[$name] = Api\DateTime::server2user($data[$name],$this->timestamp_type);
}
}
}
@@ -395,7 +394,7 @@
$this->table_def = $this->db->get_table_definitions($this->app, $this->table);
if (!$this->table_def || !is_array($this->table_def['fd']))
{
- throw new egw_exception_wrong_parameter(__METHOD__."(): No table definition for '$this->table' found !!!");
+ throw new Api\Exception\WrongParameter(__METHOD__."(): No table definition for '$this->table' found !!!");
}
}
foreach($this->table_def['fd'] as $col => $def)
@@ -442,7 +441,7 @@
{
if (isset($data[$name]) && $data[$name])
{
- $data[$name] = egw_time::user2server($data[$name],$this->timestamp_type);
+ $data[$name] = Api\DateTime::user2server($data[$name],$this->timestamp_type);
}
}
}
@@ -566,7 +565,7 @@
$this->db2data();
// store user timezone used for reading
- $this->data[self::USER_TIMEZONE_READ] = egw_time::$user_timezone->getName();
+ $this->data[self::USER_TIMEZONE_READ] = Api\DateTime::$user_timezone->getName();
if ((int) $this->debug >= 4)
{
@@ -601,12 +600,12 @@
// this only a grude hack, better handle this situation in app code:
// history logging eg. depends on old data read before calling save, which is then in new timezone!
// anyway it's better fixing it here then not fixing it at all ;-)
- if (isset($this->data[self::USER_TIMEZONE_READ]) && $this->data[self::USER_TIMEZONE_READ] != egw_time::$user_timezone->getName())
- {
- //echo "
".__METHOD__."() User change TZ since read! tz-read=".$this->data[self::USER_TIMEZONE_READ].' != current-tz='.egw_time::$user_timezone->getName()." --> fixing
\n";
- error_log(__METHOD__."() User changed TZ since read! tz-read=".$this->data[self::USER_TIMEZONE_READ].' != current-tz='.egw_time::$user_timezone->getName()." --> fixing");
+ if (isset($this->data[self::USER_TIMEZONE_READ]) && $this->data[self::USER_TIMEZONE_READ] != Api\DateTime::$user_timezone->getName())
+ {
+ //echo "
".__METHOD__."() User change TZ since read! tz-read=".$this->data[self::USER_TIMEZONE_READ].' != current-tz='.Api\DateTime::$user_timezone->getName()." --> fixing
\n";
+ error_log(__METHOD__."() User changed TZ since read! tz-read=".$this->data[self::USER_TIMEZONE_READ].' != current-tz='.Api\DateTime::$user_timezone->getName()." --> fixing");
$GLOBALS['egw_info']['user']['preferences']['common']['tz'] = $this->data[self::USER_TIMEZONE_READ];
- egw_time::setUserPrefs($this->data[self::USER_TIMEZONE_READ]);
+ Api\DateTime::setUserPrefs($this->data[self::USER_TIMEZONE_READ]);
$this->set_times();
}
$this->data2db();
@@ -884,7 +883,7 @@
$type = $this->db->get_column_attribute($only_col, $table, true, 'type');
if (empty($type))
{
- throw new egw_exception_db("Can not determine type of column '$only_col' in table '$table'!");
+ throw new Api\Db\Exception("Can not determine type of column '$only_col' in table '$table'!");
}
if (is_array($val) && count($val) > 1)
{
@@ -1239,7 +1238,7 @@
{
continue; // will only give sql error
}
- $columns[] = sprintf($this->db->capabilities[egw_db::CAPABILITY_CAST_AS_VARCHAR],"COALESCE($col,'')");
+ $columns[] = sprintf($this->db->capabilities[Api\Db::CAPABILITY_CAST_AS_VARCHAR],"COALESCE($col,'')");
}
if(!$columns)
{
@@ -1645,119 +1644,3 @@
return is_null($column) ? $comments : $comments[$column];
}
}
-
-/**
- * Iterator applying a so_sql's db2data method on each element retrived
- *
- */
-class so_sql_db2data_iterator implements Iterator
-{
- /**
- * Reference of so_sql class to use it's db2data method
- *
- * @var so_sql
- */
- private $so_sql;
-
- /**
- * Instance of ADOdb record set to iterate
- *
- * @var Iterator
- */
- private $rs;
-
- /**
- * Total count of entries
- *
- * @var int
- */
- public $total;
-
- /**
- * Constructor
- *
- * @param so_sql $so_sql
- * @param Traversable $rs
- */
- public function __construct(so_sql $so_sql,Traversable $rs=null)
- {
- $this->so_sql = $so_sql;
-
- $this->total = $so_sql->total;
-
- if (is_a($rs,'IteratorAggregate'))
- {
- $this->rs = $rs->getIterator();
- }
- else
- {
- $this->rs = $rs;
- }
- }
-
- /**
- * Return the current element
- *
- * @return array
- */
- public function current()
- {
- if (is_a($this->rs,'iterator'))
- {
- $data = $this->rs->current();
-
- return $this->so_sql->data2db($data);
- }
- return null;
- }
-
- /**
- * Return the key of the current element
- *
- * @return int
- */
- public function key()
- {
- if (is_a($this->rs,'iterator'))
- {
- return $this->rs->key();
- }
- return 0;
- }
-
- /**
- * Move forward to next element (called after each foreach loop)
- */
- public function next()
- {
- if (is_a($this->rs,'iterator'))
- {
- return $this->rs->next();
- }
- }
-
- /**
- * Rewind the Iterator to the first element (called at beginning of foreach loop)
- */
- public function rewind()
- {
- if (is_a($this->rs,'iterator'))
- {
- return $this->rs->rewind();
- }
- }
-
- /**
- * Checks if current position is valid
- *
- * @return boolean
- */
- public function valid ()
- {
- if (is_a($this->rs,'iterator'))
- {
- return $this->rs->valid();
- }
- return false;
- }
-}
Copied: trunk/egroupware/api/src/Storage/Base2.php (from r55263, trunk/etemplate/inc/class.so_sql2.inc.php)
URL: http://svn.stylite.de/viewvc/egroupware/trunk/egroupware/api/src/Storage/Base2.php?p2=trunk/egroupware/api/src/Storage/Base2.php&p1=trunk/etemplate/inc/class.so_sql2.inc.php&r1=55263&r2=55277&rev=55277&view=diff
==============================================================================
--- trunk/etemplate/inc/class.so_sql2.inc.php (original)
+++ trunk/egroupware/api/src/Storage/Base2.php Sun Mar 6 10:05:20 2016
@@ -1,14 +1,19 @@
<?php
/**
- * eGroupWare generalized SQL Storage Object Version 2 - requires PHP5.1.2+!!!
+ * EGroupware generalized SQL Storage Object Version 2
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
- * @package etemplate
+ * @package api
+ * @subpackage storage
* @link http://www.egroupware.org
* @author Ralf Becker
- * @copyright 2007/8 by RalfBecker@outdoor-training.de
+ * @copyright 2007-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
+
+namespace EGroupware\Api\Storage;
+
+use EGroupware\Api;
/**
* generalized SQL Storage Object
@@ -18,22 +23,17 @@
* 2) by setting the following documented class-vars in a class derifed from this one
* Of cause can you derife the class and call the constructor with params.
*
- * The so_sql2 class uses a privat $data array and __get and __set methods to set its data.
+ * The Base2 class uses a privat $data array and __get and __set methods to set its data.
* Please note:
* You have to explicitly declare other object-properties of derived classes, which should NOT
* be handled by that mechanism!
- *
- * @package etemplate
- * @subpackage api
- * @author RalfBecker-AT-outdoor-training.de
- * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
-class so_sql2 extends so_sql
+class Base2 extends Base
{
/**
* Private array containing all the object-data
*
- * Colides with the original definition in so_sql and I dont want to change it there at the moment.
+ * Colides with the original definition in Storage\Base and I dont want to change it there at the moment.
*
* @var array
*/
@@ -46,26 +46,16 @@
*
* @param string $app should be set if table-defs to be read from /setup/tables_current.inc.php
* @param string $table should be set if table-defs to be read from /setup/tables_current.inc.php
- * @param object/db $db database object, if not the one in $GLOBALS['egw']->db should be used, eg. for an other database
- * @param string $colum_prefix='' column prefix to automatic remove from the column-name, if the column name starts with it
- * @param boolean $no_clone=false can we avoid to clone the db-object, default no
+ * @param Api\Db $db database object, if not the one in $GLOBALS['egw']->db should be used, eg. for an other database
+ * @param string $column_prefix ='' column prefix to automatic remove from the column-name, if the column name starts with it
+ * @param boolean $no_clone =false can we avoid to clone the db-object, default no
* new code using appnames and foreach(select(...,$app) can set it to avoid an extra instance of the db object
*
* @return so_sql2
*/
- function __construct($app='',$table='',$db=null,$column_prefix='',$no_clone=false)
+ function __construct($app='',$table='',Api\Db $db=null,$column_prefix='',$no_clone=false)
{
parent::__construct($app,$table,$db,$column_prefix,$no_clone);
- }
-
- /**
- * php4 constructor
- *
- * @deprecated use __construct
- */
- function so_sql2($app='',$table='',$db=null,$column_prefix='',$no_clone=false)
- {
- self::__construct($app,$table,$db,$column_prefix,$no_clone);
}
/**
Copied: trunk/egroupware/api/src/Storage/Db2DataIterator.php (from r55263, trunk/etemplate/inc/class.so_sql.inc.php)
URL: http://svn.stylite.de/viewvc/egroupware/trunk/egroupware/api/src/Storage/Db2DataIterator.php?p2=trunk/egroupware/api/src/Storage/Db2DataIterator.php&p1=trunk/etemplate/inc/class.so_sql.inc.php&r1=55263&r2=55277&rev=55277&view=diff
==============================================================================
--- trunk/etemplate/inc/class.so_sql.inc.php (original)
+++ trunk/egroupware/api/src/Storage/Db2DataIterator.php Sun Mar 6 10:05:20 2016
@@ -1,1668 +1,35 @@
<?php
/**
- * eGroupWare generalized SQL Storage Object
+ * EGroupware generalized SQL Storage Object: Iterator applying db2data method
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
- * @package etemplate
+ * @package api
+ * @subpackage storage
* @link http://www.egroupware.org
* @author Ralf Becker
- * @copyright 2002-14 by RalfBecker@outdoor-training.de
+ * @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
-/**
- * generalized SQL Storage Object
- *
- * the class can be used in following ways:
- * 1) by calling the constructor with an app and table-name or
- * 2) by setting the following documented class-vars in a class derived from this one
- * Of cause you can derive from the class and call the constructor with params.
- *
- * @package etemplate
- * @subpackage api
- * @author RalfBecker-AT-outdoor-training.de
- * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
- * @todo modify search() to return an interator instead of an array
- */
-class so_sql
-{
- /**
- * need to be set in the derived class to the db-table-name
- *
- * @var string
- */
- var $table_name;
- /**
- * db-col-name of autoincrement id or ''
- *
- * @var string
- */
- var $autoinc_id = '';
- /**
- * all cols in data which are not (direct)in the db, for data_merge
- *
- * @var array
- */
- var $non_db_cols = array();
- /**
- * 4 turns on the so_sql debug-messages, default 0
- *
- * @var int
- */
- var $debug = 0;
- /**
- * string to be written to db if a col-value is '', eg. "''" or 'NULL' (default)
- *
- * @var string
- */
- var $empty_on_write = 'NULL';
- /**
- * total number of entries of last search with start != false
- *
- * @var int|boolean
- */
- var $total = false;
- /**
- * protected instance or reference (depeding on $no_clone param of constructor) of the db-object
- *
- * @var egw_db
- */
- protected $db;
- /**
- * unique keys/index, set by derived class or via so_sql($app,$table)
- *
- * @var array
- */
- var $db_uni_cols = array();
- /**
- * db-col-name / internal-name pairs, set by derived calls or via so_sql($app,$table)
- *
- * @var array
- */
- var $db_key_cols = array();
- /**
- * db-col-name / internal-name pairs, set by derived calls or via so_sql($app,$table)
- *
- * @var array
- */
- var $db_data_cols = array();
- /**
- * @var array $db_cols all columns = $db_key_cols + $db_data_cols, set in the constructor
- */
- var $db_cols = array();
- /**
- * eGW table definition
- *
- * @var array
- */
- var $table_def = array();
- /**
- * Appname to use in all queries, set via constructor
- *
- * @var string
- */
- var $app;
- /**
- * holds the content of all columns
- *
- * @var array
- */
- var $data = array();
- /**
- * Timestaps that need to be adjusted to user-time on reading or saving
- *
- * @var array
- */
- var $timestamps = array();
- /**
- * Type of timestamps returned by this class (read and search methods), default null means leave them unchanged
- *
- * Possible values:
- * - 'ts'|'integer' convert every timestamp to an integer unix timestamp
- * - 'string' convert every timestamp to a 'Y-m-d H:i:s' string
- * - 'object' convert every timestamp to a egw_time object
- *
- * @var string
- */
- public $timestamp_type;
- /**
- * Offset in secconds between user and server-time, it need to be add to a server-time to get the user-time
- * or substracted from a user-time to get the server-time
- *
- * @var int
- * @deprecated use egw_time methods instead, as the offset between user and server time is only valid for current time
- */
- var $tz_offset_s;
- /**
- * Current time in user timezone
- *
- * @var int|string|DateTime format depends on $this->timestamp_type
- */
- var $now;
- /**
- * Which columns should be searched, if a non-empty string is passed to criteria parameter of search()
- *
- * If not set (by extending class), all data columns will be searched.
- *
- * @var array
- */
- var $columns_to_search;
-
- /**
- * Table has boolean fields, which need automatic conversation, got set automatic by call to setup_table
- *
- * Set it to false, if you dont want automatic conversation
- *
- * @var boolean
- */
- protected $has_bools = false;
-
- /**
- * Should search return an iterator (true) or an array (false = default)
- *
- * @var boolean
- */
- public $search_return_iterator = false;
-
- /**
- * constructor of the class
- *
- * NEED to be called from the constructor of the derived class !!!
- *
- * @param string $app should be set if table-defs to be read from /setup/tables_current.inc.php
- * @param string $table should be set if table-defs to be read from /setup/tables_current.inc.php
- * @param egw_db $db database object, if not the one in $GLOBALS['egw']->db should be used, eg. for an other database
- * @param string $column_prefix ='' column prefix to automatic remove from the column-name, if the column name starts with it
- * @param boolean $no_clone =false can we avoid to clone the db-object, default no
- * new code using appnames and foreach(select(...,$app) can set it to avoid an extra instance of the db object
- * @param string $timestamp_type =null default null=leave them as is, 'ts'|'integer' use integer unix timestamps,
- * 'object' use egw_time objects or 'string' use DB timestamp (Y-m-d H:i:s) string
- *
- * @return so_sql
- */
- function __construct($app='',$table='',$db=null,$column_prefix='',$no_clone=false,$timestamp_type=null)
- {
- if ($no_clone)
- {
- $this->db = is_object($db) ? $db : $GLOBALS['egw']->db;
- }
- else
- {
- $this->db = is_object($db) ? clone($db) : clone($GLOBALS['egw']->db);
- }
- $this->db_cols = $this->db_key_cols + $this->db_data_cols;
-
- if ($app)
- {
- $this->app = $app;
-
- if (!$no_clone) $this->db->set_app($app);
-
- if ($table) $this->setup_table($app,$table,$column_prefix);
- }
- $this->init();
-
- if ((int) $this->debug >= 4)
- {
- echo "
so_sql('$app','$table')
\n";
- _debug_array($this);
- }
- $this->set_times($timestamp_type);
- }
-
- /**
- * Set class vars timestamp_type, now and tz_offset_s
- *
- * @param string|boolean $timestamp_type =false default false do NOT set time_stamptype,
- * null=leave them as is, 'ts'|'integer' use integer unix timestamps, 'object' use egw_time objects,
- * 'string' use DB timestamp (Y-m-d H:i:s) string
- */
- public function set_times($timestamp_type=false)
- {
- if ($timestamp_type !== false) $this->timestamp_type = $timestamp_type;
-
- // set current time
- switch($this->timestamp_type)
- {
- case 'object':
- $this->now = new egw_time('now');
- break;
- case 'string':
- $this->now = egw_time::to('now',egw_time::DATABASE);
- break;
- default:
- $this->now = egw_time::to('now','ts');
- }
- $this->tz_offset_s = egw_time::tz_offset_s();
- }
-
- /**
- * sets up the class for an app and table (by using the table-definition of $app/setup/tables_current.inc.php
- *
- * If you need a more complex conversation then just removing the column_prefix, you have to do so in a derifed class !!!
- *
- * @param string $app app-name $table belongs too
- * @param string $table table-name
- * @param string $colum_prefix ='' column prefix to automatic remove from the column-name, if the column name starts with it
- */
- function setup_table($app,$table,$colum_prefix='')
- {
- $this->table_name = $table;
- $this->table_def = $this->db->get_table_definitions($app,$table);
- if (!$this->table_def || !is_array($this->table_def['fd']))
- {
- throw new egw_exception_wrong_parameter(__METHOD__."('$app','$table'): No table definition for '$table' found !!!");
- }
- $this->db_key_cols = $this->db_data_cols = $this->db_cols = array();
- $this->autoinc_id = '';
- $len_prefix = strlen($colum_prefix);
- foreach($this->table_def['fd'] as $col => $def)
- {
- $name = $col;
- if ($len_prefix && substr($name,0,$len_prefix) == $colum_prefix)
- {
- $name = substr($col,$len_prefix);
- }
- if (in_array($col,$this->table_def['pk']))
- {
- $this->db_key_cols[$col] = $name;
- }
- else
- {
- $this->db_data_cols[$col] = $name;
- }
- $this->db_cols[$col] = $name;
-
- if ($def['type'] == 'auto')
- {
- $this->autoinc_id = $col;
- }
- if ($def['type'] == 'bool') $this->has_bools = true;
-
- foreach($this->table_def['uc'] as $k => $uni_index)
- {
- if (is_array($uni_index) && in_array($name,$uni_index))
- {
- $this->db_uni_cols[$k][$col] = $name;
- }
- elseif($name === $uni_index)
- {
- $this->db_uni_cols[$col] = $name;
- }
- }
- }
- }
-
- /**
- * Add all timestamp fields to $this->timestamps to get automatically converted to usertime
- *
- */
- function convert_all_timestamps()
- {
- $check_already_included = !empty($this->timestamps);
- foreach($this->table_def['fd'] as $name => $data)
- {
- if ($data['type'] == 'timestamp' && (!$check_already_included || !in_array($name,$this->timestamps)))
- {
- $this->timestamps[] = $name;
- }
- }
- }
-
- /**
- * merges in new values from the given new data-array
- *
- * @param $new array in form col => new_value with values to set
- */
- function data_merge($new)
- {
- if ((int) $this->debug >= 4) echo "
so_sql::data_merge(".print_r($new,true).")
\n";
-
- if (!is_array($new) || !count($new))
- {
- return;
- }
- foreach($this->db_cols as $db_col => $col)
- {
- if (array_key_exists($col,$new))
- {
- $this->data[$col] = $new[$col];
- }
- }
- foreach($this->non_db_cols as $db_col => $col)
- {
- if (array_key_exists($col,$new))
- {
- $this->data[$col] = $new[$col];
- }
- }
- if (isset($new[self::USER_TIMEZONE_READ]))
- {
- $this->data[self::USER_TIMEZONE_READ] = $new[self::USER_TIMEZONE_READ];
- }
- if ((int) $this->debug >= 4) _debug_array($this->data);
- }
-
- /**
- * changes the data from the db-format to your work-format
- *
- * It gets called everytime when data is read from the db.
- * This default implementation only converts the timestamps mentioned in $this->timestamps from server to user time.
- * You can reimplement it in a derived class like this:
- *
- * function db2data($data=null)
- * {
- * if (($intern = !is_array($data)))
- * {
- * $data =& $this->data;
- * }
- * // do your own modifications here
- *
- * return parent::db2data($intern ? null : $data); // important to use null, if $intern!
- * }
- *
- * @param array $data =null if given works on that array and returns result, else works on internal data-array
- * @return array
- */
- function db2data($data=null)
- {
- if (!is_array($data))
- {
- $data = &$this->data;
- }
- if ($this->timestamps)
- {
- foreach($this->timestamps as $name)
- {
- if (isset($data[$name]) && $data[$name])
- {
- if ($data[$name] === '0000-00-00 00:00:00')
- {
- $data[$name] = null;
- }
- else
- {
- $data[$name] = egw_time::server2user($data[$name],$this->timestamp_type);
- }
- }
- }
- }
- // automatic convert booleans (eg. PostgreSQL stores 't' or 'f', which both evaluate to true!)
- if ($this->has_bools !== false)
- {
- if (!isset($this->table_def))
- {
- $this->table_def = $this->db->get_table_definitions($this->app, $this->table);
- if (!$this->table_def || !is_array($this->table_def['fd']))
- {
- throw new egw_exception_wrong_parameter(__METHOD__."(): No table definition for '$this->table' found !!!");
- }
- }
- foreach($this->table_def['fd'] as $col => $def)
- {
- if ($def['type'] == 'bool' && isset($data[$col]))
- {
- $data[$col] = $this->db->from_bool($data[$col]);
- }
- }
- }
- return $data;
- }
-
- /**
- * changes the data from your work-format to the db-format
- *
- * It gets called everytime when data gets writen into db or on keys for db-searches.
- * This default implementation only converts the timestamps mentioned in $this->timestampfs from user to server time.
- * You can reimplement it in a derived class like this:
- *
- * function data2db($data=null)
- * {
- * if (($intern = !is_array($data)))
- * {
- * $data =& $this->data;
- * }
- * // do your own modifications here
- *
- * return parent::data2db($intern ? null : $data); // important to use null, if $intern!
- * }
- *
- * @param array $data =null if given works on that array and returns result, else works on internal data-array
- * @return array
- */
- function data2db($data=null)
- {
- if (!is_array($data))
- {
- $data = &$this->data;
- }
- if ($this->timestamps)
- {
- foreach($this->timestamps as $name)
- {
- if (isset($data[$name]) && $data[$name])
- {
- $data[$name] = egw_time::user2server($data[$name],$this->timestamp_type);
- }
- }
- }
- return $data;
- }
-
- /**
- * initializes data with the content of key
- *
- * @param array $keys =array() array with keys in form internalName => value
- * @return array internal data after init
- */
- function init($keys=array())
- {
- $this->data = array();
-
- $this->db2data();
-
- $this->data_merge($keys);
-
- return $this->data;
- }
-
- /**
- * Name of automatically set user timezone field from read
- */
- const USER_TIMEZONE_READ = 'user_timezone_read';
-
- /**
- * reads row matched by key and puts all cols in the data array
- *
- * @param array $keys array with keys in form internalName => value, may be a scalar value if only one key
- * @param string|array $extra_cols ='' string or array of strings to be added to the SELECT, eg. "count(*) as num"
- * @param string $join ='' sql to do a join, added as is after the table-name, eg. ", table2 WHERE x=y" or
- * @return array|boolean data if row could be retrived else False
- */
- function read($keys,$extra_cols='',$join='')
- {
- if (!is_array($keys))
- {
- $pk = array_values($this->db_key_cols);
- if ($pk) $keys = array($pk[0] => $keys);
- }
-
- $this->init($keys);
- $this->data2db();
-
- $query = false;
- foreach ($this->db_key_cols as $db_col => $col)
- {
- if ($this->data[$col] != '')
- {
- $query[$db_col] = $this->data[$col];
- }
- }
- if (!$query) // no primary key in keys, lets try the data_cols for a unique key
- {
- foreach($this->db_uni_cols as $db_col => $col)
- {
- if (!is_array($col) && $this->data[$col] != '')
- {
- $query[$db_col] = $this->data[$col];
- }
- elseif(is_array($col))
- {
- $q = array();
- foreach($col as $db_c => $c)
- {
- if ($this->data[$col] == '')
- {
- $q = null;
- break;
- }
- $q[$db_c] = $this->data[$c];
- }
- if ($q) $query += $q;
- }
- }
- }
- if (!$query) // no unique key in keys, lets try everything else
- {
- foreach($this->db_data_cols as $db_col => $col)
- {
- if ($this->data[$col] != '')
- {
- $query[$db_col] = $this->data[$col];
- }
- }
- }
- if (!$query) // keys has no cols
- {
- $this->db2data();
-
- return False;
- }
- if ($join) // Prefix the columns with the table-name, as they might exist in the join
- {
- foreach($query as $col => $val)
- {
- if (is_int($col) || strpos($join,$col) === false) continue;
- $query[] = $this->db->expression($this->table_name,$this->table_name.'.',array($col=>$val));
- unset($query[$col]);
- }
- }
- foreach($this->db->select($this->table_name,'*'.($extra_cols?','.(is_array($extra_cols)?implode(',',$extra_cols):$extra_cols):''),
- $query,__LINE__,__FILE__,False,'',$this->app,0,$join) as $row)
- {
- $cols = $this->db_cols;
- if ($extra_cols) // extra columns to report
- {
- foreach(is_array($extra_cols) ? $extra_cols : array($extra_cols) as $col)
- {
- if (FALSE!==stripos($col,' as ')) $col = preg_replace('/^.* as *([a-z0-9_]+) *$/i','\\1',$col);
- $cols[$col] = $col;
- }
- }
- foreach ($cols as $db_col => $col)
- {
- $this->data[$col] = $row[$db_col];
- }
- $this->db2data();
-
- // store user timezone used for reading
- $this->data[self::USER_TIMEZONE_READ] = egw_time::$user_timezone->getName();
-
- if ((int) $this->debug >= 4)
- {
- echo "data =\n"; _debug_array($this->data);
- }
- return $this->data;
- }
- if ($this->autoinc_id)
- {
- unset($this->data[$this->db_key_cols[$this->autoinc_id]]);
- }
- if ((int) $this->debug >= 4) echo "nothing found !!!\n";
-
- $this->db2data();
-
- return False;
- }
-
- /**
- * saves the content of data to the db
- *
- * @param array $keys =null if given $keys are copied to data before saveing => allows a save as
- * @param string|array $extra_where =null extra where clause, eg. to check an etag, returns true if no affected rows!
- * @return int|boolean 0 on success, or errno != 0 on error, or true if $extra_where is given and no rows affected
- */
- function save($keys=null,$extra_where=null)
- {
- if (is_array($keys) && count($keys)) $this->data_merge($keys);
-
- // check if data contains user timezone during read AND user changed timezone since then
- // --> load old timezone for the rest of this request
- // this only a grude hack, better handle this situation in app code:
- // history logging eg. depends on old data read before calling save, which is then in new timezone!
- // anyway it's better fixing it here then not fixing it at all ;-)
- if (isset($this->data[self::USER_TIMEZONE_READ]) && $this->data[self::USER_TIMEZONE_READ] != egw_time::$user_timezone->getName())
- {
- //echo "
".__METHOD__."() User change TZ since read! tz-read=".$this->data[self::USER_TIMEZONE_READ].' != current-tz='.egw_time::$user_timezone->getName()." --> fixing
\n";
- error_log(__METHOD__."() User changed TZ since read! tz-read=".$this->data[self::USER_TIMEZONE_READ].' != current-tz='.egw_time::$user_timezone->getName()." --> fixing");
- $GLOBALS['egw_info']['user']['preferences']['common']['tz'] = $this->data[self::USER_TIMEZONE_READ];
- egw_time::setUserPrefs($this->data[self::USER_TIMEZONE_READ]);
- $this->set_times();
- }
- $this->data2db();
-
- if ((int) $this->debug >= 4) { echo "so_sql::save(".print_r($keys,true).") autoinc_id='$this->autoinc_id', data="; _debug_array($this->data); }
-
- if ($this->autoinc_id && !$this->data[$this->db_key_cols[$this->autoinc_id]]) // insert with auto id
- {
- foreach($this->db_cols as $db_col => $col)
- {
- if (!$this->autoinc_id || $db_col != $this->autoinc_id) // not write auto-inc-id
- {
- if (!array_key_exists($col,$this->data) && // handling of unset columns in $this->data
- (isset($this->table_def['fd'][$db_col]['default']) || // we have a default value
- !isset($this->table_def['fd'][$db_col]['nullable']) || $this->table_def['fd'][$db_col]['nullable'])) // column is nullable
- {
- continue; // no need to write that (unset) column
- }
- if ($this->table_def['fd'][$db_col]['type'] == 'varchar' &&
- strlen($this->data[$col]) > $this->table_def['fd'][$db_col]['precision'])
- {
- // truncate the field to mamimum length, if upper layers didn't care
- $data[$db_col] = substr($this->data[$col],0,$this->table_def['fd'][$db_col]['precision']);
- }
- else
- {
- $data[$db_col] = (string) $this->data[$col] === '' && $this->empty_on_write == 'NULL' ? null : $this->data[$col];
- }
- }
- }
- $this->db->insert($this->table_name,$data,false,__LINE__,__FILE__,$this->app);
-
- if ($this->autoinc_id)
- {
- $this->data[$this->db_key_cols[$this->autoinc_id]] = $this->db->get_last_insert_id($this->table_name,$this->autoinc_id);
- }
- }
- else // insert in table without auto id or update of existing row, dont write colums unset in $this->data
- {
- foreach($this->db_data_cols as $db_col => $col)
- {
- // we need to update columns set to null: after a $this->data[$col]=null:
- // - array_key_exits($col,$this->data) === true
- // - isset($this->data[$col]) === false
- if (!array_key_exists($col,$this->data) && // handling of unset columns in $this->data
- ($this->autoinc_id || // update of table with auto id or
- isset($this->table_def['fd'][$db_col]['default']) || // we have a default value or
- !isset($this->table_def['fd'][$db_col]['nullable']) || $this->table_def['fd'][$db_col]['nullable'])) // column is nullable
- {
- continue; // no need to write that (unset) column
- }
- $data[$db_col] = !is_object($this->data[$col]) && (string) $this->data[$col] === '' && $this->empty_on_write == 'NULL' ? null : $this->data[$col];
- }
- // allow to add direct sql updates, eg. "etag=etag+1" with int keys
- if (is_array($keys) && isset($keys[0]))
- {
- for($n=0; isset($keys[$n]); ++$n)
- {
- $data[] = $keys[$n];
- }
- }
- $keys = $extra_where;
- foreach($this->db_key_cols as $db_col => $col)
- {
- $keys[$db_col] = $this->data[$col];
- }
- if (!$data && !$this->autoinc_id) // happens if all columns are in the primary key
- {
- $data = $keys;
- $keys = False;
- }
- if ($this->autoinc_id)
- {
- $this->db->update($this->table_name,$data,$keys,__LINE__,__FILE__,$this->app);
- if (($nothing_affected = !$this->db->Errno && !$this->db->affected_rows()) && $extra_where)
- {
- return true; // extra_where not met, eg. etag wrong
- }
- }
- // always try an insert if we have no autoinc_id, as we