diff --git a/calendar/inc/class.calendar_timezones.inc.php b/calendar/inc/class.calendar_timezones.inc.php index dd19243d24..71a8c0ae6c 100644 --- a/calendar/inc/class.calendar_timezones.inc.php +++ b/calendar/inc/class.calendar_timezones.inc.php @@ -84,7 +84,7 @@ class calendar_timezones * - calendar_timezone::tz2id('Europe/Berlin','component') returns VTIMEZONE component for given TZID * * @param string $tzid TZID - * @param string $what='id' what to return, default id, null for whole array + * @param string $what ='id' what to return, default id, null for whole array * @return int tz_id or null if not found */ public static function tz2id($tzid,$what='id') @@ -105,7 +105,7 @@ class calendar_timezones if (!isset($id) && stripos($tzid, 'America/') === 0 && count($parts = explode('/', $tzid)) == 2) { if (($data = $GLOBALS['egw']->db->select(self::TABLE,'*',array( - 'tz_tzid LIKE '.$GLOBALS['egw']->db->quote($parts[0].'/%/'.$part[1]), + 'tz_tzid LIKE '.$GLOBALS['egw']->db->quote($parts[0].'/%/'.$parts[1]), ),__LINE__,__FILE__,false,'','calendar')->fetch())) { $id = $data['tz_id']; @@ -129,7 +129,7 @@ class calendar_timezones * - calendar_timezone::id2tz($id,'component') returns VTIMEZONE component for the given id * * @param int $id - * @param string $what='tzid' what data to return or null for whole data array, with keys 'id', 'tzid', 'component', 'alias', 'latitude', 'longitude' + * @param string $what ='tzid' what data to return or null for whole data array, with keys 'id', 'tzid', 'component', 'alias', 'latitude', 'longitude' * @return mixed false: if not found */ public static function id2tz($id,$what='tzid') @@ -177,17 +177,21 @@ class calendar_timezones // check for updated timezones once per session if (!egw_cache::getSession(__CLASS__, 'tzs_checked')) { + $updated = false; try { $msg = self::import_sqlite($updated); if ($updated) error_log($msg); // log that timezones have been updated - $msg = self::import_tz_aliases($updated); - if ($updated) error_log($msg); // log that timezone aliases have been updated } - catch (Exception $e) + catch (egw_exception_wrong_userinput $e) { - _egw_log_exception($e); // log the exception to error_log, but do not stall program execution + unset($e); + $msg = self::import_db_backup($updated); + if ($updated) error_log($msg); // log that timezones have been updated } + $alias_msg = self::import_tz_aliases($updated); + if ($updated) error_log($alias_msg); // log that timezone aliases have been updated + egw_cache::setSession(__CLASS__, 'tzs_checked', true); } } @@ -196,10 +200,10 @@ class calendar_timezones * Import timezones from sqlite file * * @param boolean &$updated=null on return true if update was neccessary, false if tz's were already up to date - * @param string $file='calendar/setup/timezones.sqlite' filename relative to EGW_SERVER_ROOT + * @param string $file ='calendar/setup/timezones.sqlite' filename relative to EGW_SERVER_ROOT * @return string message about update * @throws egw_exception_wrong_parameter if $file is not readable or wrong format/version - * @throws egw_exception_assertion_failed if no PDO sqlite support + * @throws egw_exception_wrong_userinput if no PDO sqlite support * @throws egw_exception_wrong_userinput for broken sqlite extension */ public static function import_sqlite(&$updated=null, $file='calendar/setup/timezones.sqlite') @@ -271,15 +275,46 @@ class calendar_timezones } /** - * Import timezone aliases + * Import timezone via db_backup of egw_cal_timezones * * @param boolean &$updated=null on return true if update was neccessary, false if tz's were already up to date - * @param string $file='calendar/setup/tz_aliases.inc.php' filename relative to EGW_SERVER_ROOT - * @param boolean $check_mtime=true true: check version and only act, if it's different + * @param string $file ='calendar/setup/tz_aliases.inc.php' filename relative to EGW_SERVER_ROOT * @return string message about update * @throws egw_exception_wrong_parameter if $file is not readable or wrong format/version */ - public static function import_tz_aliases(&$updated=null,$file='calendar/setup/tz_aliases.inc.php',$check_mtime=true) + public static function import_db_backup(&$updated=null,$file='calendar/setup/timezones.db_backup') + { + $path = EGW_SERVER_ROOT.'/'.$file; + + if (!file_exists($path) || !is_readable($path)) + { + throw new egw_exception_wrong_parameter(__METHOD__."('$file') not found or readable!"); + } + $config = config::read('phpgwapi'); + $tz_version = date('Y-m-d H:i:s', filemtime($path)); + if ($tz_version === $config['tz_version']) + { + $updated = false; + return lang('Nothing to update, version is already %1.',$tz_version); + } + $db_backup = new db_backup(); + $rows = $db_backup->db_restore($db_backup->fopen_backup($path, true), 'tz_tzid'); + + config::save_value('tz_version', $tz_version, 'phpgwapi'); + + $updated = true; + return lang('Timezones updated to version %1 (%2 records updated).', $tz_version, $rows-8); // -8 because of header-lines + } + + /** + * Import timezone aliases + * + * @param boolean &$updated=null on return true if update was neccessary, false if tz's were already up to date + * @param string $file ='calendar/setup/tz_aliases.inc.php' filename relative to EGW_SERVER_ROOT + * @return string message about update + * @throws egw_exception_wrong_parameter if $file is not readable or wrong format/version + */ + public static function import_tz_aliases(&$updated=null,$file='calendar/setup/tz_aliases.inc.php') { $path = EGW_SERVER_ROOT.'/'.$file; @@ -294,6 +329,7 @@ class calendar_timezones $updated = false; return lang('Nothing to update, version is already %1.',$tz_aliases_mtime); } + $tz_aliases = array(); include($path); // sets $tz_aliases $updates = 0; @@ -328,10 +364,17 @@ class calendar_timezones { throw new egw_exception_no_permission_admin(); } - $GLOBALS['egw']->framework->render( - '

'.self::import_sqlite()."

\n". - '

'.self::import_tz_aliases()."

\n", - lang('Update timezones'),true); + try { + $output = '

'.self::import_sqlite()."

\n"; + } + catch (egw_exception_wrong_userinput $e) + { + unset($e); + $output = '

'.self::import_db_backup()."

\n"; + } + $output .= '

'.self::import_tz_aliases()."

\n"; + + $GLOBALS['egw']->framework->render($output, lang('Update timezones'), true); } /** @@ -347,7 +390,7 @@ class calendar_timezones // checking type of $val, now we included the object definition (no need to always include it!) if (!$vcal instanceof Horde_iCalendar) { - throw new egw_exception_wrong_parameter(__METHOD__.'('.array2string($val).", '$tzid') no Horde_iCalendar!"); + throw new egw_exception_wrong_parameter(__METHOD__.'('.array2string($vcal).", '$tzid') no Horde_iCalendar!"); } // check if we have vtimezone component data for $tzid if (!($vtimezone = calendar_timezones::tz2id($tzid, 'component'))) @@ -362,16 +405,16 @@ class calendar_timezones $standard = $horde_vtimezone->findComponent('STANDARD'); if (is_a($standard, 'Horde_iCalendar')) { - $dtstart = $standard->getAttribute('DTSTART'); - $dtstart = new egw_time($dtstart, egw_time::$server_timezone); + $time = $standard->getAttribute('DTSTART'); + $dtstart = new egw_time($time, egw_time::$server_timezone); $dtstart->setTimezone(egw_time::$server_timezone); $standard->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false); } $daylight = $horde_vtimezone->findComponent('DAYLIGHT'); if (is_a($daylight, 'Horde_iCalendar')) { - $dtstart = $daylight->getAttribute('DTSTART'); - $dtstart = new egw_time($dtstart, egw_time::$server_timezone); + $time = $daylight->getAttribute('DTSTART'); + $dtstart = new egw_time($time, egw_time::$server_timezone); $dtstart->setTimezone(egw_time::$server_timezone); $daylight->setAttribute('DTSTART', $dtstart->format('Ymd\THis'), array(), false); } @@ -384,8 +427,8 @@ class calendar_timezones /** * Query timezone of a given user, returns 'tzid' or VTIMEZONE 'component' * - * @param int $user=null - * @param string $type='vcalendar' 'tzid' or everything tz2id supports, default 'vcalendar' = full vcalendar component + * @param int $user =null + * @param string $type ='vcalendar' 'tzid' or everything tz2id supports, default 'vcalendar' = full vcalendar component * @return string */ public static function user_timezone($user=null, $type='vcalendar') diff --git a/calendar/setup/default_records.inc.php b/calendar/setup/default_records.inc.php index 5c9177ee86..ffb493d5d1 100644 --- a/calendar/setup/default_records.inc.php +++ b/calendar/setup/default_records.inc.php @@ -27,11 +27,10 @@ foreach(array( try { calendar_timezones::import_sqlite(); - calendar_timezones::import_tz_aliases(); } // catch missing or broken sqlite support and use timezones.db_backup to install timezones catch (egw_exception_wrong_userinput $e) // all other exceptions are fatal { - $db_backup = new db_backup(); - $db_backup->restore($db_backup->fopen_backup(EGW_SERVER_ROOT.'/calendar/setup/timezones.db_backup', true), true, '', false); -} \ No newline at end of file + calendar_timezones::import_db_backup(); +} +calendar_timezones::import_tz_aliases(); diff --git a/calendar/setup/timezones.db_backup b/calendar/setup/timezones.db_backup index 0c9a24a783..7c51673a01 100644 --- a/calendar/setup/timezones.db_backup +++ b/calendar/setup/timezones.db_backup @@ -439,179 +439,3 @@ tz_id,tz_tzid,tz_alias,tz_latitude,tz_longitude,tz_component 431,"Europe/Belfast",338,NULL,NULL,NULL 432,"Atlantic/Jan_Mayen",347,NULL,NULL,NULL 433,"Pacific/Yap",421,NULL,NULL,NULL -434,"AUS Central Standard Time",307,NULL,NULL,NULL -435,"AUS Eastern Standard Time",314,NULL,NULL,NULL -436,"Afghanistan Standard Time",246,NULL,NULL,NULL -437,"Alaskan Standard Time",54,NULL,NULL,NULL -438,"Arab Standard Time",273,NULL,NULL,NULL -439,"Arabian Standard Time",233,NULL,NULL,NULL -440,"Arabic Standard Time",220,NULL,NULL,NULL -441,"Argentina Standard Time",58,NULL,NULL,NULL -442,"Atlantic Standard Time",113,NULL,NULL,NULL -443,"Azerbaijan Standard Time",222,NULL,NULL,NULL -444,"Azores Standard Time",293,NULL,NULL,NULL -445,"Bahia Standard Time",73,NULL,NULL,NULL -446,"Bangladesh Standard Time",231,NULL,NULL,NULL -447,"Canada Central Standard Time",172,NULL,NULL,NULL -448,"Cape Verde Standard Time",296,NULL,NULL,NULL -449,"Caucasus Standard Time",292,NULL,NULL,NULL -450,"Cen. Australia Standard Time",303,NULL,NULL,NULL -451,"Central America Standard Time",110,NULL,NULL,NULL -452,"Central Asia Standard Time",214,NULL,NULL,NULL -453,"Central Brazilian Standard Time",92,NULL,NULL,NULL -454,"Central Europe Standard Time",323,NULL,NULL,NULL -455,"Central European Standard Time",368,NULL,NULL,NULL -456,"Central Pacific Standard Time",395,NULL,NULL,NULL -457,"Central Standard Time",88,NULL,NULL,NULL -458,"Central Standard Time (Mexico)",145,NULL,NULL,NULL -459,"China Standard Time",277,NULL,NULL,NULL -460,"E. Africa Standard Time",43,NULL,NULL,NULL -461,"E. Australia Standard Time",304,NULL,NULL,NULL -462,"E. Europe Standard Time",262,NULL,NULL,NULL -463,"E. South America Standard Time",179,NULL,NULL,NULL -464,"Eastern Standard Time",153,NULL,NULL,NULL -465,"Egypt Standard Time",13,NULL,NULL,NULL -466,"Ekaterinburg Standard Time",291,NULL,NULL,NULL -467,"FLE Standard Time",335,NULL,NULL,NULL -468,"Fiji Standard Time",391,NULL,NULL,NULL -469,"GMT Standard Time",338,NULL,NULL,NULL -470,"GTB Standard Time",322,NULL,NULL,NULL -471,"Georgian Standard Time",281,NULL,NULL,NULL -472,"Greenland Standard Time",105,NULL,NULL,NULL -473,"Greenwich Standard Time",299,NULL,NULL,NULL -474,"Hawaiian Standard Time",397,NULL,NULL,NULL -475,"India Standard Time",252,NULL,NULL,NULL -476,"Iran Standard Time",282,NULL,NULL,NULL -477,"Israel Standard Time",245,NULL,NULL,NULL -478,"Jordan Standard Time",215,NULL,NULL,NULL -479,"Kaliningrad Standard Time",334,NULL,NULL,NULL -480,"Korea Standard Time",276,NULL,NULL,NULL -481,"Magadan Standard Time",258,NULL,NULL,NULL -482,"Mauritius Standard Time",380,NULL,NULL,NULL -483,"Middle East Standard Time",224,NULL,NULL,NULL -484,"Montevideo Standard Time",149,NULL,NULL,NULL -485,"Morocco Standard Time",14,NULL,NULL,NULL -486,"Mountain Standard Time",97,NULL,NULL,NULL -487,"Mountain Standard Time (Mexico)",89,NULL,NULL,NULL -488,"Myanmar Standard Time",272,NULL,NULL,NULL -489,"N. Central Asia Standard Time",264,NULL,NULL,NULL -490,"Namibia Standard Time",52,NULL,NULL,NULL -491,"Nepal Standard Time",250,NULL,NULL,NULL -492,"New Zealand Standard Time",384,NULL,NULL,NULL -493,"Newfoundland Standard Time",184,NULL,NULL,NULL -494,"North Asia East Standard Time",241,NULL,NULL,NULL -495,"North Asia Standard Time",253,NULL,NULL,NULL -496,"Pacific SA Standard Time",177,NULL,NULL,NULL -497,"Pacific Standard Time",133,NULL,NULL,NULL -498,"Pacific Standard Time (Mexico)",175,NULL,NULL,NULL -499,"Pakistan Standard Time",248,NULL,NULL,NULL -500,"Paraguay Standard Time",71,NULL,NULL,NULL -501,"Romance Standard Time",348,NULL,NULL,NULL -502,"Russian Standard Time",345,NULL,NULL,NULL -503,"SA Eastern Standard Time",86,NULL,NULL,NULL -504,"SA Pacific Standard Time",80,NULL,NULL,NULL -505,"SA Western Standard Time",131,NULL,NULL,NULL -506,"SE Asia Standard Time",223,NULL,NULL,NULL -507,"Samoa Standard Time",383,NULL,NULL,NULL -508,"Singapore Standard Time",278,NULL,NULL,NULL -509,"South Africa Standard Time",25,NULL,NULL,NULL -510,"Sri Lanka Standard Time",229,NULL,NULL,NULL -511,"Syria Standard Time",230,NULL,NULL,NULL -512,"Taipei Standard Time",279,NULL,NULL,NULL -513,"Tasmania Standard Time",309,NULL,NULL,NULL -514,"Tokyo Standard Time",284,NULL,NULL,NULL -515,"Tonga Standard Time",418,NULL,NULL,NULL -516,"Turkey Standard Time",332,NULL,NULL,NULL -517,"US Mountain Standard Time",164,NULL,NULL,NULL -518,"Ulaanbaatar Standard Time",285,NULL,NULL,NULL -519,"Venezuela Standard Time",85,NULL,NULL,NULL -520,"Vladivostok Standard Time",289,NULL,NULL,NULL -521,"W. Australia Standard Time",313,NULL,NULL,NULL -522,"W. Central Africa Standard Time",31,NULL,NULL,NULL -523,"W. Europe Standard Time",319,NULL,NULL,NULL -524,"West Asia Standard Time",280,NULL,NULL,NULL -525,"West Pacific Standard Time",413,NULL,NULL,NULL -526,"Yakutsk Standard Time",290,NULL,NULL,NULL -527,"Universal Coordinated Time",-1,NULL,NULL,NULL -528,"Casablanca, Monrovia",14,NULL,NULL,NULL -529,"Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London",336,NULL,NULL,NULL -530,"Greenwich Mean Time; Dublin, Edinburgh, London",338,NULL,NULL,NULL -531,"Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna",319,NULL,NULL,NULL -532,"Belgrade, Pozsony, Budapest, Ljubljana, Prague",350,NULL,NULL,NULL -533,"Brussels, Copenhagen, Madrid, Paris",348,NULL,NULL,NULL -534,"Paris, Madrid, Brussels, Copenhagen",348,NULL,NULL,NULL -535,"Prague, Central Europe",350,NULL,NULL,NULL -536,"Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb",355,NULL,NULL,NULL -537,"West Central Africa",34,NULL,NULL,NULL -538,"Athens, Istanbul, Minsk",317,NULL,NULL,NULL -539,"Bucharest",322,NULL,NULL,NULL -540,"Cairo",13,NULL,NULL,NULL -541,"Harare, Pretoria",24,NULL,NULL,NULL -542,"Helsinki, Riga, Tallinn",330,NULL,NULL,NULL -543,"Israel, Jerusalem Standard Time",245,NULL,NULL,NULL -544,"Baghdad",220,NULL,NULL,NULL -545,"Arab, Kuwait, Riyadh",256,NULL,NULL,NULL -546,"Moscow, St. Petersburg, Volgograd",345,NULL,NULL,NULL -547,"East Africa, Nairobi",43,NULL,NULL,NULL -548,"Tehran",282,NULL,NULL,NULL -549,"Abu Dhabi, Muscat",261,NULL,NULL,NULL -550,"Baku, Tbilisi, Yerevan",222,NULL,NULL,NULL -551,"Kabul",246,NULL,NULL,NULL -552,"Ekaterinburg",291,NULL,NULL,NULL -553,"Islamabad, Karachi, Tashkent",248,NULL,NULL,NULL -554,"Kolkata, Chennai, Mumbai, New Delhi, India Standard Time",429,NULL,NULL,NULL -555,"Kathmandu, Nepal",250,NULL,NULL,NULL -556,"Almaty, Novosibirsk, North Central Asia",214,NULL,NULL,NULL -557,"Astana, Dhaka",231,NULL,NULL,NULL -558,"Sri Jayawardenepura, Sri Lanka",229,NULL,NULL,NULL -559,"Rangoon",272,NULL,NULL,NULL -560,"Bangkok, Hanoi, Jakarta",223,NULL,NULL,NULL -561,"Krasnoyarsk",253,NULL,NULL,NULL -562,"Beijing, Chongqing, Hong Kong SAR, Urumqi",277,NULL,NULL,NULL -563,"Irkutsk, Ulaan Bataar",241,NULL,NULL,NULL -564,"Kuala Lumpur, Singapore",278,NULL,NULL,NULL -565,"Perth, Western Australia",313,NULL,NULL,NULL -566,"Taipei",279,NULL,NULL,NULL -567,"Osaka, Sapporo, Tokyo",284,NULL,NULL,NULL -568,"Seoul, Korea Standard time",276,NULL,NULL,NULL -569,"Yakutsk",290,NULL,NULL,NULL -570,"Adelaide, Central Australia",303,NULL,NULL,NULL -571,"Darwin",307,NULL,NULL,NULL -572,"Brisbane, East Australia",304,NULL,NULL,NULL -573,"Canberra, Melbourne, Sydney, Hobart (year 2000 only)",314,NULL,NULL,NULL -574,"Guam, Port Moresby",396,NULL,NULL,NULL -575,"Hobart, Tasmania",309,NULL,NULL,NULL -576,"Vladivostok",289,NULL,NULL,NULL -577,"Magadan, Solomon Is., New Caledonia",258,NULL,NULL,NULL -578,"Auckland, Wellington",384,NULL,NULL,NULL -579,"Fiji Islands, Kamchatka, Marshall Is.",391,NULL,NULL,NULL -580,"Nuku'alofa, Tonga",418,NULL,NULL,NULL -581,"Azores",293,NULL,NULL,NULL -582,"Cape Verde Is.",296,NULL,NULL,NULL -583,"Mid-Atlantic",156,NULL,NULL,NULL -584,"Brasilia",179,NULL,NULL,NULL -585,"Buenos Aires",58,NULL,NULL,NULL -586,"Greenland",105,NULL,NULL,NULL -587,"Newfoundland",184,NULL,NULL,NULL -588,"Atlantic Time (Canada)",113,NULL,NULL,NULL -589,"Caracas, La Paz",85,NULL,NULL,NULL -590,"Santiago",177,NULL,NULL,NULL -591,"Bogota, Lima, Quito",80,NULL,NULL,NULL -592,"Eastern Time (US & Canada)",153,NULL,NULL,NULL -593,"Indiana (East)",116,NULL,NULL,NULL -594,"Central America",110,NULL,NULL,NULL -595,"Central Time (US & Canada)",88,NULL,NULL,NULL -596,"Mexico City, Tegucigalpa",145,NULL,NULL,NULL -597,"Saskatchewan",100,NULL,NULL,NULL -598,"Arizona",164,NULL,NULL,NULL -599,"Mountain Time (US & Canada)",97,NULL,NULL,NULL -600,"Pacific Time (US & Canada); Tijuana",133,NULL,NULL,NULL -601,"Alaska",54,NULL,NULL,NULL -602,"Hawaii",397,NULL,NULL,NULL -603,"Midway Island, Samoa",404,NULL,NULL,NULL -604,"Eniwetok, Kwajalein, Dateline Time",401,NULL,NULL,NULL -605,"Armenian Standard Time",292,NULL,NULL,NULL -606,"Mexico Standard Time",145,NULL,NULL,NULL -607,"Mexico Standard Time 2",89,NULL,NULL,NULL -608,"Mid-Atlantic Standard Time",300,NULL,NULL,NULL -609,"US/Eastern",153,NULL,NULL,NULL diff --git a/phpgwapi/inc/class.db_backup.inc.php b/phpgwapi/inc/class.db_backup.inc.php index dc69ac860c..d349ce4dbd 100644 --- a/phpgwapi/inc/class.db_backup.inc.php +++ b/phpgwapi/inc/class.db_backup.inc.php @@ -416,155 +416,7 @@ class db_backup $backup_db_halt_on_error = $this->db->Halt_On_Error; $this->db->Halt_On_Error = 'no'; } - $table = False; - $n = 0; - $rows = array(); - while(!feof($f)) - { - $line = trim(fgets($f)); ++$n; - - if (empty($line)) continue; - - if (substr($line,0,9) == 'version: ') - { - $api_version = trim(substr($line,9)); - continue; - } - if (substr($line,0,9) == 'charset: ') - { - $charset = trim(substr($line,9)); - // needed if mbstring.func_overload > 0, else eg. substr does not work with non ascii chars - @ini_set('mbstring.internal_encoding',$charset); - - // check if we really need to convert the charset, as it's not perfect and can do some damage - if ($convert_to_system_charset && !strcasecmp($this->schema_proc->system_charset, $charset)) - { - $convert_to_system_charset = false; // no conversation necessary - } - // set the DB's client encoding (for mysql only if api_version >= 1.0.1.019) - if ((!$convert_to_system_charset || $this->db->capabilities['client_encoding']) && - (substr($this->db->Type,0,5) != 'mysql' || !is_object($GLOBALS['egw_setup']) || - $api_version && !$GLOBALS['egw_setup']->alessthanb($api_version,'1.0.1.019'))) - { - $this->db->Link_ID->SetCharSet($charset); - if (!$convert_to_system_charset) - { - $this->schema_proc->system_charset = $charset; // so schema_proc uses it for the creation of the tables - } - } - continue; - } - if (substr($line,0,8) == 'schema: ') - { - // create the tables in the backup set - $this->schemas = json_php_unserialize(trim(substr($line,8))); - foreach($this->schemas as $table_name => $schema) - { - // if column is longtext in current schema, convert text to longtext, in case user already updated column - foreach($schema['fd'] as $col => &$def) - { - if ($def['type'] == 'text' && $this->db->get_column_attribute($col, $table_name, true, 'type') == 'longtext') - { - $def['type'] = 'longtext'; - } - } - //echo "
$table_name => ".self::write_array($schema,1)."
\n"; - $this->schema_proc->CreateTable($table_name, $schema); - } - continue; - } - if (substr($line,0,7) == 'table: ') - { - if ($rows) // flush pending rows of last table - { - $this->db->insert($table,$rows,False,__LINE__,__FILE__,false,false,$this->schemas[$table]); - } - $rows = array(); - $table = substr($line,7); - if (!isset($this->schemas[$table])) $this->schemas[$table] = $this->db->get_table_definitions(true, $table); - - $cols = self::csv_split($line=fgets($f)); ++$n; - $blobs = array(); - foreach($this->schemas[$table]['fd'] as $col => $data) - { - if ($data['type'] == 'blob') $blobs[] = $col; - } - - if (feof($f)) break; - continue; - } - if ($convert_to_system_charset && !$this->db->capabilities['client_encoding']) - { - if ($GLOBALS['egw_setup']) - { - if (!is_object($GLOBALS['egw_setup']->translation->sql)) - { - $GLOBALS['egw_setup']->translation->setup_translation_sql(); - } - } - } - if ($table) // do we already reached the data part - { - $import = true; - $data = self::csv_split($line, $cols, $blobs); - - if ($table == 'egw_async' && in_array('##last-check-run##',$data)) - { - //echo '

'.lang("Line %1: '%2'
csv data does contain ##last-check-run## of table %3 ==> ignored",$n,$line,$table)."

\n"; - //echo 'data=
'.print_r($data,true)."
\n"; - $import = false; - } - if (in_array($table,$this->exclude_tables)) - { - echo '

'.lang("Table %1 is excluded from backup and restore. Data will not be restored.",$table)."

\n"; - $import = false; // dont restore data of excluded tables - } - if ($import) - { - if (count($data) == count($cols)) - { - if ($convert_to_system_charset && !$this->db->capabilities['client_encoding']) - { - $data = translation::convert($data,$charset); - } - if ($insert_n_rows > 1) - { - $rows[] = $data; - if (count($rows) == $insert_n_rows) - { - $this->db->insert($table,$rows,False,__LINE__,__FILE__,false,false,$this->schemas[$table]); - $rows = array(); - } - } - else - { - $this->db->insert($table,$data,False,__LINE__,__FILE__,false,false,$this->schemas[$table]); - } - } - else - { - echo '

'.lang("Line %1: '%2'
csv data does not match column-count of table %3 ==> ignored",$n,$line,$table)."

\n"; - echo 'data=
'.print_r($data,true)."
\n"; - } - } - } - } - if ($rows) // flush pending rows - { - $this->db->insert($table,$rows,False,__LINE__,__FILE__,false,false,$this->schemas[$table]); - } - // updated the sequences, if the DB uses them - foreach($this->schemas as $table => $schema) - { - foreach($schema['fd'] as $column => $definition) - { - if ($definition['type'] == 'auto') - { - $this->schema_proc->UpdateSequence($table,$column); - break; // max. one per table - } - } - } + $this->db_restore($f, $insert_n_rows); if ($convert_to_system_charset) // store the changed charset { @@ -652,6 +504,177 @@ class db_backup return ''; } + /** + * Restore data from a (compressed) csv file + * + * @param resource $f file opened with fopen for reading + * @param int|string $insert_n_rows =10 how many rows to insert in one sql statement, or string with column-name used as unique key for insert + * @returns int number of rows read from csv file + */ + function db_restore($f, $insert_n_rows=10) + { + $convert_to_system_charset = true; + $table = False; + $n = 0; + $rows = array(); + while(!feof($f)) + { + $line = trim(fgets($f)); ++$n; + + if (empty($line)) continue; + + if (substr($line,0,9) == 'version: ') + { + $api_version = trim(substr($line,9)); + continue; + } + if (substr($line,0,9) == 'charset: ') + { + $charset = trim(substr($line,9)); + // needed if mbstring.func_overload > 0, else eg. substr does not work with non ascii chars + @ini_set('mbstring.internal_encoding',$charset); + + // check if we really need to convert the charset, as it's not perfect and can do some damage + if ($convert_to_system_charset && !strcasecmp($this->schema_proc->system_charset, $charset)) + { + $convert_to_system_charset = false; // no conversation necessary + } + // set the DB's client encoding (for mysql only if api_version >= 1.0.1.019) + if ((!$convert_to_system_charset || $this->db->capabilities['client_encoding']) && + (substr($this->db->Type,0,5) != 'mysql' || !is_object($GLOBALS['egw_setup']) || + $api_version && !$GLOBALS['egw_setup']->alessthanb($api_version,'1.0.1.019'))) + { + $this->db->Link_ID->SetCharSet($charset); + if (!$convert_to_system_charset) + { + $this->schema_proc->system_charset = $charset; // so schema_proc uses it for the creation of the tables + } + } + continue; + } + if (substr($line,0,8) == 'schema: ') + { + // create the tables in the backup set + $this->schemas = json_php_unserialize(trim(substr($line,8))); + foreach($this->schemas as $table_name => $schema) + { + // if column is longtext in current schema, convert text to longtext, in case user already updated column + foreach($schema['fd'] as $col => &$def) + { + if ($def['type'] == 'text' && $this->db->get_column_attribute($col, $table_name, true, 'type') == 'longtext') + { + $def['type'] = 'longtext'; + } + } + //echo "
$table_name => ".self::write_array($schema,1)."
\n"; + $this->schema_proc->CreateTable($table_name, $schema); + } + continue; + } + if (substr($line,0,7) == 'table: ') + { + if ($rows) // flush pending rows of last table + { + $this->db->insert($table,$rows,False,__LINE__,__FILE__,false,false,$this->schemas[$table]); + } + $rows = array(); + $table = substr($line,7); + if (!isset($this->schemas[$table])) $this->schemas[$table] = $this->db->get_table_definitions(true, $table); + $auto_id = count($this->schemas[$table]['pk']) == 1 ? $this->schemas[$table]['pk'][0] : null; + + $cols = self::csv_split($line=fgets($f)); ++$n; + $blobs = array(); + foreach($this->schemas[$table]['fd'] as $col => $data) + { + if ($data['type'] == 'blob') $blobs[] = $col; + } + + if (feof($f)) break; + continue; + } + if ($convert_to_system_charset && !$this->db->capabilities['client_encoding']) + { + if ($GLOBALS['egw_setup']) + { + if (!is_object($GLOBALS['egw_setup']->translation->sql)) + { + $GLOBALS['egw_setup']->translation->setup_translation_sql(); + } + } + } + if ($table) // do we already reached the data part + { + $import = true; + $data = self::csv_split($line, $cols, $blobs); + + if ($table == 'egw_async' && in_array('##last-check-run##',$data)) + { + //echo '

'.lang("Line %1: '%2'
csv data does contain ##last-check-run## of table %3 ==> ignored",$n,$line,$table)."

\n"; + //echo 'data=
'.print_r($data,true)."
\n"; + $import = false; + } + if (in_array($table,$this->exclude_tables)) + { + echo '

'.lang("Table %1 is excluded from backup and restore. Data will not be restored.",$table)."

\n"; + $import = false; // dont restore data of excluded tables + } + if ($import) + { + if (count($data) == count($cols)) + { + if ($convert_to_system_charset && !$this->db->capabilities['client_encoding']) + { + $data = translation::convert($data,$charset); + } + if ($insert_n_rows > 1) + { + $rows[] = $data; + if (count($rows) == $insert_n_rows) + { + $this->db->insert($table,$rows,False,__LINE__,__FILE__,false,false,$this->schemas[$table]); + $rows = array(); + } + } + // update existing table using given unique key in $insert_n_rows (also removing auto-id/sequence) + elseif(!is_numeric($insert_n_rows)) + { + $where = array($insert_n_rows => $data[$insert_n_rows]); + unset($data[$insert_n_rows]); + if ($auto_id) unset($data[$auto_id]); + $this->db->insert($table,$data,$where,__LINE__,__FILE__,false,false,$this->schemas[$table]); + } + else + { + $this->db->insert($table,$data,False,__LINE__,__FILE__,false,false,$this->schemas[$table]); + } + } + else + { + echo '

'.lang("Line %1: '%2'
csv data does not match column-count of table %3 ==> ignored",$n,$line,$table)."

\n"; + echo 'data=
'.print_r($data,true)."
\n"; + } + } + } + } + if ($rows) // flush pending rows + { + $this->db->insert($table,$rows,False,__LINE__,__FILE__,false,false,$this->schemas[$table]); + } + // updated the sequences, if the DB uses them + foreach($this->schemas as $table => $schema) + { + foreach($schema['fd'] as $column => $definition) + { + if ($definition['type'] == 'auto') + { + $this->schema_proc->UpdateSequence($table,$column); + break; // max. one per table + } + } + } + return $n; + } + /** * Removes a dir, no matter whether it is empty or full * diff --git a/phpgwapi/inc/class.egw_db.inc.php b/phpgwapi/inc/class.egw_db.inc.php index aca8154484..8cb611a875 100644 --- a/phpgwapi/inc/class.egw_db.inc.php +++ b/phpgwapi/inc/class.egw_db.inc.php @@ -1750,7 +1750,7 @@ class egw_db $this->select($table,'count(*)',$where,$line,$file); if ($this->next_record() && $this->f(0)) { - return !!$this->update($table,$data,$where,$line,$file,$app); + return !!$this->update($table,$data,$where,$line,$file,$app,$use_prepared_statement,$table_def); } break; }