From ef700b00618e5041acb5a02e91582828d193b61b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sat, 7 Jun 2008 17:45:33 +0000 Subject: [PATCH] new class naming schema --- addressbook/inc/class.addressbook_bo.inc.php | 5 +- calendar/csv_import.php | 8 +- calendar/freebusy.php | 132 +- .../class.import_events_csv.inc.php | 94 +- calendar/inc/class.bocalendar.inc.php | 22 +- calendar/inc/class.boical.inc.php | 1552 --------- ...ar.inc.php => class.calendar_ajax.inc.php} | 30 +- ...ocal.inc.php => class.calendar_bo.inc.php} | 56 +- ...nc.php => class.calendar_boupdate.inc.php} | 10 +- ....php => class.calendar_datasource.inc.php} | 62 +- calendar/inc/class.calendar_groupdav.inc.php | 6 +- calendar/inc/class.calendar_ical.inc.php | 1546 +++++++++ calendar/inc/class.calendar_sif.inc.php | 503 +++ ...ocal.inc.php => class.calendar_so.inc.php} | 17 +- ...ical.inc.php => class.calendar_ui.inc.php} | 75 +- ...inc.php => class.calendar_uiforms.inc.php} | 32 +- ....inc.php => class.calendar_uilist.inc.php} | 42 +- ...inc.php => class.calendar_uiviews.inc.php} | 206 +- calendar/inc/class.sifcalendar.inc.php | 502 --- calendar/inc/hook_admin.inc.php | 25 - calendar/inc/hook_deleteaccount.inc.php | 15 - calendar/inc/hook_home.inc.php | 77 - calendar/inc/hook_preferences.inc.php | 23 - calendar/inc/hook_settings.inc.php | 374 -- calendar/index.php | 34 +- calendar/js/dragDropFunctions.js | 23 +- calendar/setup/etemplates.inc.php | 59 +- calendar/setup/setup.inc.php | 129 +- calendar/setup/tables_current.inc.php | 192 +- calendar/setup/tables_update.inc.php | 3081 +++++++++-------- calendar/templates/default/edit.xet | 12 +- calendar/templates/default/list.xet | 32 +- 32 files changed, 4170 insertions(+), 4806 deletions(-) delete mode 100644 calendar/inc/class.boical.inc.php rename calendar/inc/{class.ajaxcalendar.inc.php => class.calendar_ajax.inc.php} (76%) rename calendar/inc/{class.bocal.inc.php => class.calendar_bo.inc.php} (97%) rename calendar/inc/{class.bocalupdate.inc.php => class.calendar_boupdate.inc.php} (99%) rename calendar/inc/{class.datasource_calendar.inc.php => class.calendar_datasource.inc.php} (63%) create mode 100644 calendar/inc/class.calendar_ical.inc.php create mode 100644 calendar/inc/class.calendar_sif.inc.php rename calendar/inc/{class.socal.inc.php => class.calendar_so.inc.php} (98%) rename calendar/inc/{class.uical.inc.php => class.calendar_ui.inc.php} (93%) rename calendar/inc/{class.uiforms.inc.php => class.calendar_uiforms.inc.php} (98%) rename calendar/inc/{class.uilist.inc.php => class.calendar_uilist.inc.php} (95%) rename calendar/inc/{class.uiviews.inc.php => class.calendar_uiviews.inc.php} (97%) delete mode 100644 calendar/inc/class.sifcalendar.inc.php delete mode 100755 calendar/inc/hook_admin.inc.php delete mode 100755 calendar/inc/hook_deleteaccount.inc.php delete mode 100755 calendar/inc/hook_home.inc.php delete mode 100644 calendar/inc/hook_preferences.inc.php delete mode 100644 calendar/inc/hook_settings.inc.php diff --git a/addressbook/inc/class.addressbook_bo.inc.php b/addressbook/inc/class.addressbook_bo.inc.php index 1a56b740e5..7079e7e7a2 100755 --- a/addressbook/inc/class.addressbook_bo.inc.php +++ b/addressbook/inc/class.addressbook_bo.inc.php @@ -366,7 +366,7 @@ class addressbook_bo extends addressbook_so if (!$data['freebusy_uri'] && !$data['owner'] && $data['account_id'] && !is_object($GLOBALS['egw_setup'])) { static $fb_url; - if (!$fb_url && @is_dir(EGW_SERVER_ROOT.'/calendar/inc')) $fb_url = ExecMethod('calendar.bocal.freebusy_url',''); + if (!$fb_url && @is_dir(EGW_SERVER_ROOT.'/calendar/inc')) $fb_url = calendar_bo::freebusy_url(''); if ($fb_url) $data['freebusy_uri'] = $fb_url.urlencode($GLOBALS['egw']->accounts->id2name($data['account_id'])); } return $data; @@ -944,8 +944,7 @@ class addressbook_bo extends addressbook_so } if (!$uids) return array(); - include_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.bocal.inc.php'); - $bocal = new bocal; + $bocal = new calendar_bo(); $events = $bocal->search(array( 'users' => $uids, 'enum_recuring' => true, diff --git a/calendar/csv_import.php b/calendar/csv_import.php index 3d81db889b..b5e1692927 100644 --- a/calendar/csv_import.php +++ b/calendar/csv_import.php @@ -37,7 +37,7 @@ $GLOBALS['egw']->redirect_link('/admin/index.php'); } $GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps']['calendar']['title'].' - '.lang('Import CSV-File'); - $cal =& CreateObject('calendar.uical',true); + $cal = new calendar_ui(true); $GLOBALS['egw']->common->egw_header(); $GLOBALS['egw']->template->set_file(array('import_t' => 'csv_import.tpl')); @@ -62,7 +62,7 @@ function addr_id( $n_family,$n_given,$org_name ) { // find in Addressbook, at least n_family AND (n_given OR org_name) have to match - $contacts =& CreateObject('phpgwapi.contacts'); + $contacts = new contacts(); $addrs = $contacts->read(0,0,array('id'),'',"n_family=$n_family,n_given=$n_given,org_name=$org_name"); if(!count($addrs)) @@ -102,7 +102,7 @@ if (is_numeric($cat) && $GLOBALS['egw']->categories->id2name($cat) != '--') { $cat2id[$cat] = $ids[$cat] = $cat; - } + } elseif ($id = $GLOBALS['egw']->categories->name2id( addslashes($cat) )) { // cat exists $cat2id[$cat] = $ids[$cat] = $id; @@ -472,7 +472,7 @@ $action = $values['id'] ? 'updating' : 'adding'; //echo $action.'
'.print_r($values,True)."
\n"; $cal_id = $cal->bo->update($values,true,!$values['modified'],$is_admin); // ignoring conflicts and ACL (for admins) on import - + $log .= "\t\t".''.($cal_id ? $action." cal_id=$cal_id" : 'Error '.$action)."\n\t\n"; } else diff --git a/calendar/freebusy.php b/calendar/freebusy.php index bb54ec710c..389b0258c7 100644 --- a/calendar/freebusy.php +++ b/calendar/freebusy.php @@ -1,76 +1,74 @@ + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package calendar + * @subpackage export + * @version $Id$ + */ - /* $Id$ */ +$GLOBALS['egw_info'] = array( + 'flags' => array( + 'currentapp' => 'calendar', + 'noheader' => True, + 'nofooter' => True, + ), +); +// check if we are loged in, by checking sessionid and kp3, as the sessionid get set automaticaly by php for php4-sessions +if (!($loged_in = @$_REQUEST['sessionid'] && @$_REQUEST['kp3'])) +{ + $GLOBALS['egw_info']['flags']['currentapp'] = 'login'; + $GLOBALS['egw_info']['flags']['noapi'] = True; +} +include ('../header.inc.php'); - $GLOBALS['egw_info'] = array( - 'flags' => array( - 'currentapp' => 'calendar', - 'noheader' => True, - 'nofooter' => True, - ), - ); - // check if we are loged in, by checking sessionid and kp3, as the sessionid get set automaticaly by php for php4-sessions - if (!($loged_in = @$_REQUEST['sessionid'] && @$_REQUEST['kp3'])) - { - $GLOBALS['egw_info']['flags']['currentapp'] = 'login'; - $GLOBALS['egw_info']['flags']['noapi'] = True; - } - include ('../header.inc.php'); +function fail_exit($msg) +{ + echo "\n\n$msg\n\n\n

$msg

\n\n\n"; - function fail_exit($msg) - { - echo "\n\n$msg\n\n\n

$msg

\n\n\n"; + $GLOBALS['egw']->common->egw_exit(); +} - $GLOBALS['egw']->common->egw_exit(); - } +if (!$loged_in) +{ + include ('../phpgwapi/inc/functions.inc.php'); + $GLOBALS['egw_info']['flags']['currentapp'] = 'calendar'; +} +// fix for SOGo connector, which does not decode the = in our f/b url +if (strpos($_SERVER['QUERY_STRING'],'=3D') !== false && substr($_GET['user'],0,2) == '3D') +{ + $_GET['user'] = substr($_GET['user'],2); + if (isset($_GET['password'])) $_GET['password'] = substr($_GET['password'],2); +} +$user = is_numeric($_GET['user']) ? (int) $_GET['user'] : $GLOBALS['egw']->accounts->name2id($_GET['user'],'account_lid','u'); - if (!$loged_in) - { - include ('../phpgwapi/inc/functions.inc.php'); - $GLOBALS['egw_info']['flags']['currentapp'] = 'calendar'; - } - // fix for SOGo connector, which does not decode the = in our f/b url - if (strpos($_SERVER['QUERY_STRING'],'=3D') !== false && substr($_GET['user'],0,2) == '3D') - { - $_GET['user'] = substr($_GET['user'],2); - if (isset($_GET['password'])) $_GET['password'] = substr($_GET['password'],2); - } - $user = is_numeric($_GET['user']) ? (int) $_GET['user'] : $GLOBALS['egw']->accounts->name2id($_GET['user'],'account_lid','u'); +if (!($username = $GLOBALS['egw']->accounts->id2name($user))) +{ + fail_exit(lang("freebusy: Unknow user '%1', wrong password or not availible to not loged in users !!!"." $username($user)",$_GET['user'])); +} +if (!$loged_in) +{ + $GLOBALS['egw']->preferences->account_id = $user; + $GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); + $GLOBALS['egw_info']['user']['account_id'] = $user; + $GLOBALS['egw_info']['user']['account_lid'] = $username; - if (!($username = $GLOBALS['egw']->accounts->id2name($user))) + $cal_prefs = &$GLOBALS['egw_info']['user']['preferences']['calendar']; + if (!$cal_prefs['freebusy'] || !empty($cal_prefs['freebusy_pw']) && $cal_prefs['freebusy_pw'] != $_GET['password']) { - fail_exit(lang("freebusy: Unknow user '%1', wrong password or not availible to not loged in users !!!"." $username($user)",$_GET['user'])); + fail_exit(lang("freebusy: Unknow user '%1', wrong password or not availible to not loged in users !!!",$_GET['user'])); } - if (!$loged_in) - { - $GLOBALS['egw']->preferences->account_id = $user; - $GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); - $GLOBALS['egw_info']['user']['account_id'] = $user; - $GLOBALS['egw_info']['user']['account_lid'] = $username; - - $cal_prefs = &$GLOBALS['egw_info']['user']['preferences']['calendar']; - if (!$cal_prefs['freebusy'] || !empty($cal_prefs['freebusy_pw']) && $cal_prefs['freebusy_pw'] != $_GET['password']) - { - fail_exit(lang("freebusy: Unknow user '%1', wrong password or not availible to not loged in users !!!",$_GET['user'])); - } - } - if ($_GET['debug']) - { - echo "
";
-	}
-	else
-	{
-		ExecMethod2('phpgwapi.browser.content_header','freebusy.ifb','text/calendar');
-	}
-	echo ExecMethod2('calendar.boical.freebusy',$user,$_GET['end']);
+}
+if ($_GET['debug'])
+{
+	echo "
";
+}
+else
+{
+	ExecMethod2('phpgwapi.browser.content_header','freebusy.ifb','text/calendar');
+}
+echo ExecMethod2('calendar.calendar_ical.freebusy',$user,$_GET['end']);
diff --git a/calendar/importexport/class.import_events_csv.inc.php b/calendar/importexport/class.import_events_csv.inc.php
index add2d94894..1c659791fb 100644
--- a/calendar/importexport/class.import_events_csv.inc.php
+++ b/calendar/importexport/class.import_events_csv.inc.php
@@ -18,69 +18,69 @@ require_once(EGW_INCLUDE_ROOT.'/importexport/inc/class.import_csv.inc.php');
  * class import_csv for addressbook
  */
 class import_events_csv implements iface_import_plugin  {
-	
+
 	private static $plugin_options = array(
 		'fieldsep', 			// char
 		'charset', 				// string
 		'event_owner', 			// int account_id or -1 for leave untuched
 		'owner_joins_event',	// bool
-		'update_cats', 			// string {override|add} overides record 
+		'update_cats', 			// string {override|add} overides record
 								// with cat(s) from csv OR add the cat from
 								// csv file to exeisting cat(s) of record
 		'num_header_lines',		// int number of header lines
 		'trash_users_records',	// trashes all events of events owner before import
 		'field_conversion', 	// array( $csv_col_num => conversion)
 		'field_mapping',		// array( $csv_col_num => adb_filed)
-		'conditions',			/* => array containing condition arrays: 
+		'conditions',			/* => array containing condition arrays:
 				'type' => exists, // record['uid'] exists
 				'true' => array(
 					'action' => update,
 					'last' => true,
 				),
 				'false' => array(
-					'action' => insert, 
+					'action' => insert,
 					'last' => true,
 				),*/
-				
+
 	);
-	
+
 	/**
 	 * actions wich could be done to data entries
 	 */
 	private static $actions = array( 'none', 'update', 'insert', 'delete', );
-	
+
 	/**
 	 * conditions for actions
 	 *
 	 * @var array
 	 */
 	private static $conditions = array( 'exists', 'empty', );
-	
+
 	/**
 	 * @var definition
 	 */
 	private $definition;
-	
+
 	/**
 	 * @var bocalupdate
 	 */
 	private $bocalupdate;
-	
+
 	/**
 	 * @var bool
 	 */
 	private $dry_run = false;
-	
+
 	/**
 	 * @var bool is current user admin?
 	 */
 	private $is_admin = false;
-	
+
 	/**
 	 * @var int
 	 */
 	private $user = null;
-	
+
 	/**
 	 * imports entries according to given definition object.
 	 * @param resource $_stream
@@ -92,7 +92,7 @@ class import_events_csv implements iface_import_plugin  {
 			'fieldsep' => $_definition->plugin_options['fieldsep'],
 			'charset' => $_definition->plugin_options['charset'],
 		));
-		
+
 		$this->definition = $_definition;
 
 		// user, is admin ?
@@ -100,79 +100,79 @@ class import_events_csv implements iface_import_plugin  {
 		$this->user = $GLOBALS['egw_info']['user']['account_id'];
 
 		// dry run?
-		$this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] :  false; 
-		
+		$this->dry_run = isset( $_definition->plugin_options['dry_run'] ) ? $_definition->plugin_options['dry_run'] :  false;
+
 		// fetch the calendar bo
-		$this->bocalupdate = CreateObject('calendar.bocalupdate');
-		
+		$this->bocalupdate = new calendar_boupdate();
+
 		// set FieldMapping.
 		$import_csv->mapping = $_definition->plugin_options['field_mapping'];
-		
+
 		// set FieldConversion
 		$import_csv->conversion = $_definition->plugin_options['field_conversion'];
-		
+
 		//check if file has a header lines
 		if ( isset( $_definition->plugin_options['num_header_lines'] ) ) {
 			$import_csv->skip_records( $_definition->plugin_options['num_header_lines'] );
 		}
-		
+
 		// set eventOwner
-		$_definition->plugin_options['events_owner'] = isset( $_definition->plugin_options['events_owner'] ) ? 
+		$_definition->plugin_options['events_owner'] = isset( $_definition->plugin_options['events_owner'] ) ?
 			$_definition->plugin_options['events_owner'] : $this->user;
-		
+
 		// trash_users_records ?
 		if ( $_definition->plugin_options['trash_users_records'] === true ) {
 			if ( !$_definition->plugin_options['dry_run'] ) {
-				$socal = CreateObject( 'calendar.socal' );
-				$socal->change_delete_user( $_definition->plugin_options['events_owner'], false );
+				$socal = new calendar_socal();
+				$this->bocalupdate->so->deleteaccount( $_definition->plugin_options['events_owner']);
 				unset( $socal );
 			} else {
 				$lid = $GLOBALS['egw']->accounts->id2name( $_definition->plugin_options['events_owner'] );
 				echo "Attension: All Events of '$lid' would be deleted!\n";
 			}
 		}
-		
+
 		while ( $record = $import_csv->get_record() ) {
 
 			// don't import empty events
 			if( count( array_unique( $record ) ) < 2 ) continue;
-			
+
 			if ( $_definition->plugin_options['events_owner'] != -1 ) {
 				$record['owner'] = $_definition->plugin_options['events_owner'];
 			} else unset( $record['owner'] );
-			
+
 			if ( $_definition->plugin_options['conditions'] ) {
 				foreach ( $_definition->plugin_options['conditions'] as $condition ) {
 					switch ( $condition['type'] ) {
 						// exists
 						case 'exists' :
-						
+
 							if ( is_array( $event = $this->bocalupdate->read( $record['uid'], null, $this->is_admin ) ) ) {
 								// apply action to event matching this exists condition
 								$record['id'] = $event['id'];
-								
+
 								if ( $_definition->plugin_options['update_cats'] == 'add' ) {
 									if ( !is_array( $event['cat_id'] ) ) $event['cat_id'] = explode( ',', $event['cat_id'] );
 									if ( !is_array( $record['cat_id'] ) ) $record['cat_id'] = explode( ',', $record['cat_id'] );
 									$record['cat_id'] = implode( ',', array_unique( array_merge( $record['cat_id'], $event['cat_id'] ) ) );
 								}
-								
+
 								// check if entry is modiefied
 								$event = array_intersect_key( $event, $record );
 								$diff = array_diff( $event, $record );
 								if( !empty( $diff ) ) $record['modified'] = time();
-								
+
 								$action = $condition['true'];
 							} else $action = $condition['false'];
-							
+
 							$this->action( $action['action'], $record );
 							break;
 						case 'empty' :
 							$action = empty( $record[$condition['string']] ) ? $condition['true'] : $condition['false'];
 							$this->action( $action['action'], $record );
 							break;
-							
-						// not supported action	
+
+						// not supported action
 						default :
 							throw new Exception('condition not supported!!!');
 							break;
@@ -185,7 +185,7 @@ class import_events_csv implements iface_import_plugin  {
 			}
 		}
 	}
-	
+
 	/**
 	 * perform the required action
 	 *
@@ -197,10 +197,10 @@ class import_events_csv implements iface_import_plugin  {
 		switch ( $_action ) {
 			case 'none' :
 				return true;
-				
+
 			case 'update' :
 			case 'insert' :
-				
+
 				// paticipants handling
 				$participants = $_data['participants'] ? split( '[,;]', $_data['participants'] ) : array();
 				$_data['participants'] = array();
@@ -219,18 +219,18 @@ class import_events_csv implements iface_import_plugin  {
 				if ( empty( $_data['participants'] ) ) {
 					$_data['participants'][$this->user] = 'A';
 				}
-				
+
 				// are we serious?
 				if ( $this->dry_run ) {
 					print_r($_data);
 				} else {
-					return $this->bocalupdate->update( $_data, true, !$_data['modified'], $this->is_admin);	
+					return $this->bocalupdate->update( $_data, true, !$_data['modified'], $this->is_admin);
 				}
-				
+
 			case 'delete' :
 		}
 	}
-	
+
 	/**
 	 * returns translated name of plugin
 	 *
@@ -239,7 +239,7 @@ class import_events_csv implements iface_import_plugin  {
 	public static function get_name() {
 		return lang('Calendar CSV export');
 	}
-	
+
 	/**
 	 * returns translated (user) description of plugin
 	 *
@@ -248,7 +248,7 @@ class import_events_csv implements iface_import_plugin  {
 	public static function get_description() {
 		return lang("Imports events into your Calendar from a CSV File. CSV means 'Comma Seperated Values'. However in the options Tab you can also choose other seperators.");
 	}
-	
+
 	/**
 	 * retruns file suffix(s) plugin can handle (e.g. csv)
 	 *
@@ -257,12 +257,12 @@ class import_events_csv implements iface_import_plugin  {
 	public static function get_filesuffix() {
 		return 'csv';
 	}
-	
+
 	/**
 	 * return etemplate components for options.
 	 * @abstract We can't deal with etemplate objects here, as an uietemplate
 	 * objects itself are scipt orientated and not "dialog objects"
-	 * 
+	 *
 	 * @return array (
 	 * 		name 		=> string,
 	 * 		content		=> array,
@@ -273,7 +273,7 @@ class import_events_csv implements iface_import_plugin  {
 	public function get_options_etpl() {
 		// lets do it!
 	}
-	
+
 	/**
 	 * returns etemplate name for slectors of this plugin
 	 *
diff --git a/calendar/inc/class.bocalendar.inc.php b/calendar/inc/class.bocalendar.inc.php
index 588faf625c..66236cba0a 100755
--- a/calendar/inc/class.bocalendar.inc.php
+++ b/calendar/inc/class.bocalendar.inc.php
@@ -2,16 +2,16 @@
 /**
  * eGroupWare - Calendar's XMLRPC or SOAP access
  *
+ * Please note: dont use addressbook_... naming convention, as it would break the existing xmlrpc clients
+ *
  * @link http://www.egroupware.org
  * @package calendar
  * @author Ralf Becker 
- * @copyright (c) 2005-7 by RalfBecker-At-outdoor-training.de
+ * @copyright (c) 2005-8 by RalfBecker-At-outdoor-training.de
  * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
  * @version $Id$
  */
 
-require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.bocalupdate.inc.php');
-
 /**
  * Class to access AND manipulate calendar data via XMLRPC or SOAP
  *
@@ -19,7 +19,7 @@ require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.bocalupdate.inc.php');
  *
  * @link http://egroupware.org/wiki/xmlrpc
  */
-class bocalendar 
+class bocalendar
 {
 	var $xmlrpc_date_format = 'Y-m-d\\TH:i:s';
 	var $debug = false;	// log function call to the apache error_log
@@ -33,9 +33,9 @@ class bocalendar
 		'list_methods' => True,
 	);
 
-	function bocalendar()
+	function __construct()
 	{
-		$this->cal =& new bocalupdate();
+		$this->cal =& new calendar_boupdate();
 
 		if (is_object($GLOBALS['server']) && $GLOBALS['server']->simpledate)
 		{
@@ -112,7 +112,7 @@ class bocalendar
 						'out' => array('array')
 					),
 				);
-		}		
+		}
 		return array();
 	}
 
@@ -127,7 +127,7 @@ class bocalendar
 		if ($this->debug) error_log('bocalendar::read('.print_r($id,true).')');
 
 		$events =& $this->cal->read($id,null,true,$this->xmlrpc_date_format);	// true = ignore acl!!!
-	
+
 		if (!$events)	// only id not found, as ignore_acl=true
 		{
 			// xmlrpc_error does NOT return
@@ -217,7 +217,7 @@ class bocalendar
 					$user = $GLOBALS['egw']->accounts->name2id($data['email'],'account_email');
 				}
 				if (!$user) continue;
-				
+
 				$event['participants'][$user] = in_array($data['status'],array('U','A','R','T')) ? $data['status'] : 'U';
 			}
 		}
@@ -252,7 +252,7 @@ class bocalendar
 		unset($params['ignore_acl']);
 
 		$events =& $this->cal->search($params);
-		
+
 		foreach($events as $key => $event)
 		{
 			$events[$key] = $this->xmlrpc_prepare($event);
@@ -311,7 +311,7 @@ class bocalendar
 		}
 		// using access={public|private} in all modules via xmlrpc
 		$event['access'] = $event['public'] ? 'public' : 'private';
-		
+
 		// unset everything not known in version 1.0
 		foreach(array('public','participant_types') as $key)
 		{
diff --git a/calendar/inc/class.boical.inc.php b/calendar/inc/class.boical.inc.php
deleted file mode 100644
index 31a3c20d4b..0000000000
--- a/calendar/inc/class.boical.inc.php
+++ /dev/null
@@ -1,1552 +0,0 @@
-                      *
-	* --------------------------------------------                             *
-	*  This program is free software; you can redistribute it and/or modify it *
-	*  under the terms of the GNU General Public License as published by the   *
-	*  Free Software Foundation; either version 2 of the License.              *
-	\**************************************************************************/
-
-	/* $Id$ */
-
-	require_once EGW_SERVER_ROOT.'/calendar/inc/class.bocalupdate.inc.php';
-	require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/Horde/iCalendar.php';
-
-	/**
-	 * iCal import and export via Horde iCalendar classes
-	 *
-	 * @package calendar
-	 * @author Lars Kneschke 
-	 * @author Ralf Becker 
-	 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
-	 */
-	class boical extends bocalupdate
-	{
-		/**
-		 * @var array $supportedFields array containing the supported fields of the importing device
-		 */
-		var $supportedFields;
-
-		var $recur_days_1_0 = array(
-			MCAL_M_MONDAY    => 'MO',
-			MCAL_M_TUESDAY   => 'TU',
-			MCAL_M_WEDNESDAY => 'WE',
-			MCAL_M_THURSDAY  => 'TH',
-			MCAL_M_FRIDAY    => 'FR',
-			MCAL_M_SATURDAY  => 'SA',
-			MCAL_M_SUNDAY    => 'SU',
-		);
-
-		/**
-		 * @var array $status_egw2ical conversation of the participant status egw => ical
-		 */
-		var $status_egw2ical = array(
-			'U' => 'NEEDS-ACTION',
-			'A' => 'ACCEPTED',
-			'R' => 'DECLINED',
-			'T' => 'TENTATIVE',
-		);
-		/**
-		 * @var array conversation of the participant status ical => egw
-		 */
-		var $status_ical2egw = array(
-			'NEEDS-ACTION' => 'U',
-			'ACCEPTED'     => 'A',
-			'DECLINED'     => 'R',
-			'TENTATIVE'    => 'T',
-		);
-
-		/**
-		 * @var array $status_ical2egw conversation of the priority egw => ical
-		 */
-		var $priority_egw2ical = array(
-			0 => 0,		// undefined
-			1 => 9,		// low
-			2 => 5,		// normal
-			3 => 1,		// high
-		);
-		/**
-		 * @var array $status_ical2egw conversation of the priority ical => egw
-		 */
-		var $priority_ical2egw = array(
-			0 => 0,		// undefined
-			9 => 1,	8 => 1, 7 => 1, 6 => 1,	// low
-			5 => 2,		// normal
-			4 => 3, 2 => 3, 3 => 3, 1 => 3,	// high
-		);
-
-		/**
-		 * @var array $recur_egw2ical_2_0 converstaion of egw recur-type => ical FREQ
-		 */
-		var $recur_egw2ical_2_0 = array(
-			MCAL_RECUR_DAILY        => 'DAILY',
-			MCAL_RECUR_WEEKLY       => 'WEEKLY',
-			MCAL_RECUR_MONTHLY_MDAY => 'MONTHLY',	// BYMONHTDAY={1..31}
-			MCAL_RECUR_MONTHLY_WDAY => 'MONTHLY',	// BYDAY={1..5}{MO..SO}
-			MCAL_RECUR_YEARLY       => 'YEARLY',
-		);
-
-		/**
-		 * @var array $recur_egw2ical_1_0 converstaion of egw recur-type => ical FREQ
-		 */
-		var $recur_egw2ical_1_0 = array(
-			MCAL_RECUR_DAILY        => 'D',
-			MCAL_RECUR_WEEKLY       => 'W',
-			MCAL_RECUR_MONTHLY_MDAY => 'MD',	// BYMONHTDAY={1..31}
-			MCAL_RECUR_MONTHLY_WDAY => 'MP',	// BYDAY={1..5}{MO..SO}
-			MCAL_RECUR_YEARLY       => 'YM',
-		);
-
-		/**
-		 * manufacturer and name of the sync-client
-		 *
-		 * @var string
-		 */
-		var $productManufacturer = 'file';
-		var $productName = '';
-
-		/**
-		 * Exports one calendar event to an iCalendar item
-		 *
-		 * @param int/array $events (array of) cal_id or array of the events
-		 * @param string $version='1.0' could be '2.0' too
-		 * @param string $method='PUBLISH'
-		 * @param boolean $force_own_uid=true ignore the stored and maybe from the client transfered uid and generate a new one
-		 * RalfBecker: GroupDAV/CalDAV requires to switch that non RFC conform behavior off, dont know if SyncML still needs it
-		 * @return string/boolean string with vCal or false on error (eg. no permission to read the event)
-		 */
-		function &exportVCal($events,$version='1.0', $method='PUBLISH',$force_own_uid=true)
-		{
-			$egwSupportedFields = array(
-				'CLASS'			=> array('dbName' => 'public'),
-				'SUMMARY'		=> array('dbName' => 'title'),
-				'DESCRIPTION'	=> array('dbName' => 'description'),
-				'LOCATION'		=> array('dbName' => 'location'),
-				'DTSTART'		=> array('dbName' => 'start'),
-				'DTEND'			=> array('dbName' => 'end'),
-				'ORGANIZER'		=> array('dbName' => 'owner'),
-				'ATTENDEE'		=> array('dbName' => 'participants'),
-				'RRULE'			=> array('dbName' => 'recur_type'),
-				'EXDATE'		=> array('dbName' => 'recur_exception'),
- 				'PRIORITY'		=> array('dbName' => 'priority'),
- 				'TRANSP'		=> array('dbName' => 'non_blocking'),
-				'CATEGORIES'	=> array('dbName' => 'category'),
-			);
-			if(!is_array($this->supportedFields))
-			{
-				$this->setSupportedFields();
-			}
-
-			if($this->productManufacturer == '' )
-			{	// syncevolution is broken
-				$version = "2.0";
-			}
-
-			$palm_enddate_workaround=False;
-			if($this->productManufacturer == 'Synthesis AG'
-				&& strpos($this->productName, "PalmOS") )
-			{
-				// This workaround adds 1 day to the recur_enddate if it exists, to fix a palm bug
-				$palm_enddate_workaround=True;
-			}
-
-			$vcal = &new Horde_iCalendar;
-			$vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
-				strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
-			$vcal->setAttribute('VERSION',$version);
-			$vcal->setAttribute('METHOD',$method);
-
-			if (!is_array($events)) $events = array($events);
-
-			foreach($events as $event)
-			{
-				if (!is_array($event) && !($event = $this->read($event,null,false,'server')))	// server = timestamp in server-time(!)
-				{
-					return false;	// no permission to read $cal_id
-				}
-				//_debug_array($event);
-
-				// correct daylight saving time
-				/* causes times wrong by one hour, if exporting events with DST different from the current date,
-				which this fix is suppost to fix. Maybe the problem has been fixed in the horde code too.
-				$currentDST = date('I', mktime());
-				$eventDST = date('I', $event['start']);
-				$DSTCorrection = ($currentDST - $eventDST) * 3600;
-				$event['start']	= $event['start'] + $DSTCorrection;
-				$event['end']	= $event['end'] + $DSTCorrection;
-				*/
-				$eventGUID = $GLOBALS['egw']->common->generate_uid('calendar',$event['id']);
-
-				$vevent = Horde_iCalendar::newComponent('VEVENT',$vcal);
-				$parameters = $attributes = array();
-
-				foreach($egwSupportedFields as $icalFieldName => $egwFieldInfo)
-				{
-					if($this->supportedFields[$egwFieldInfo['dbName']])
-					{
-						switch($icalFieldName)
-						{
-							case 'ATTENDEE':
-								//if (count($event['participants']) == 1 && isset($event['participants'][$this->user])) break;
-								foreach((array)$event['participants'] as $uid => $status)
-								{
-									if (!($info = $this->resource_info($uid))) continue;
-									// RB: MAILTO href contains only the email-address, NO cn!
-									$attributes['ATTENDEE'][]	= $info['email'] ? 'MAILTO:'.$info['email'] : '';
-									// ROLE={CHAIR|REQ-PARTICIPANT|OPT-PARTICIPANT|NON-PARTICIPANT} NOT used by eGW atm.
-									$role = $uid == $event['owner'] ? 'CHAIR' : 'REQ-PARTICIPANT';
-									// RSVP={TRUE|FALSE}	// resonse expected, not set in eGW => status=U
-									$rsvp = $status == 'U' ? 'TRUE' : 'FALSE';
-									// PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED|COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm.
-									$status = $this->status_egw2ical[$status];
-									// CUTYPE={INDIVIDUAL|GROUP|RESOURCE|ROOM|UNKNOWN}
-									switch ($info['type'])
-									{
-										case 'g':
-											$cutype = 'GROUP';
-											break;
-										case 'r':
-											$cutype = 'RESOURCE';
-											break;
-										case 'u':	// account
-										case 'c':	// contact
-										case 'e':	// email address
-											$cutype = 'INDIVIDUAL';
-											break;
-										default:
-											$cutype = 'UNKNOWN';
-											break;
-									};
-									$parameters['ATTENDEE'][] = array(
-										'CN'       => $info['cn'] ? $info['cn'] : $info['name'],
-										'ROLE'     => $role,
-										'PARTSTAT' => $status,
-										'CUTYPE'   => $cutype,
-										'RSVP'     => $rsvp,
-									)+($info['type'] != 'e' ? array('X-EGROUPWARE-UID' => $uid) : array());
-								}
-								break;
-
-	            			case 'CLASS':
-	            				$attributes['CLASS'] = $event['public'] ? 'PUBLIC' : 'PRIVATE';
-        	    				break;
-
-            				case 'ORGANIZER':	// according to iCalendar standard, ORGANIZER not used for events in the own calendar
-            					if ($event['owner'] != $this->user)
-            					//if (!isset($event['participants'][$event['owner']]) || count($event['participants']) > 1)
-            					{
-									$mailtoOrganizer = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email');
-									$attributes['ORGANIZER'] = $mailtoOrganizer ? 'MAILTO:'.$mailtoOrganizer : '';
-									$parameters['ORGANIZER']['CN'] = trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname').' '.
-										$GLOBALS['egw']->accounts->id2name($event['owner'],'account_lastname'));
-            					}
-								break;
-
-							case 'DTEND':
-								if(date('H:i:s',$event['end']) == '23:59:59') $event['end']++;
-								if(date('H:i:s',$event['end']) == '23:59:00') $event['end']+=60; // needed by old eGW whole-day events
-								$attributes[$icalFieldName]	= $event['end'];
-								break;
-
-							case 'RRULE':
-								if ($event['recur_type'] == MCAL_RECUR_NONE) break;		// no recuring event
-								if ($version == '1.0') {
-									$interval = ($event['recur_interval'] > 1) ? $event['recur_interval'] : 1;
-									$rrule = array('FREQ' => $this->recur_egw2ical_1_0[$event['recur_type']].$interval);
-									switch ($event['recur_type'])
-									{
-	            								case MCAL_RECUR_WEEKLY:
-	            									$days = array();
-	            									foreach($this->recur_days_1_0 as $id => $day)
-	            									{
-	            										if ($event['recur_data'] & $id) $days[] = strtoupper(substr($day,0,2));
-													}
-		            								$rrule['BYDAY'] = implode(' ',$days);
-		            								$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
-		            								break;
-
-	        	    							case MCAL_RECUR_MONTHLY_MDAY:	// date of the month: BYMONTDAY={1..31}
-	            									break;
-
-	             								case MCAL_RECUR_MONTHLY_WDAY:	// weekday of the month: BDAY={1..5}{MO..SO}
-	             									$rrule['BYDAY'] = (1 + (int) ((date('d',$event['start'])-1) / 7)).'+ '.
-		             									strtoupper(substr(date('l',$event['start']),0,2));
-		            								$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
-											break;
-									}
-
-									if ($event['recur_enddate'])
-									{
-										$recur_enddate = (int)$event['recur_enddate'];
-										if ($palm_enddate_workaround)
-										{
-											$recur_enddate += 86400;
-										}
-										# append T and the Endtime, since the RRULE seems not to be understood by the client without it
-										$rrule['UNTIL'] = date('Ymd',$recur_enddate).'T'.date('His',($event['end']?$event['end']:$event['start'])) ;
-									}
-									else
-									{
-										$rrule['UNTIL'] = '#0';
-									}
-
-									$attributes['RRULE'] = $rrule['FREQ'].' '.$rrule['UNTIL'];
-								} else {
-									$rrule = array('FREQ' => $this->recur_egw2ical_2_0[$event['recur_type']]);
-									switch ($event['recur_type'])
-									{
-	            								case MCAL_RECUR_WEEKLY:
-	            									$days = array();
-	            									foreach($this->recur_days as $id => $day)
-	            									{
-	            										if ($event['recur_data'] & $id) $days[] = strtoupper(substr($day,0,2));
-											}
-		            								$rrule['BYDAY'] = implode(',',$days);
-		            								break;
-
-	        	    							case MCAL_RECUR_MONTHLY_MDAY:	// date of the month: BYMONTDAY={1..31}
-	            									$rrule['BYMONTHDAY'] = (int) date('d',$event['start']);
-	            									break;
-
-	             								case MCAL_RECUR_MONTHLY_WDAY:	// weekday of the month: BDAY={1..5}{MO..SO}
-	             									$rrule['BYDAY'] = (1 + (int) ((date('d',$event['start'])-1) / 7)).
-		             									strtoupper(substr(date('l',$event['start']),0,2));
-											break;
-									}
-									if ($event['recur_interval'] > 1) $rrule['INTERVAL'] = $event['recur_interval'];
-									if ($event['recur_enddate']) $rrule['UNTIL'] = date('Ymd',$event['recur_enddate']);	// only day is set in eGW
-
-									// no idea how to get the Horde parser to produce a standard conformant
-									// RRULE:FREQ=... (note the double colon after RRULE, we cant use the $parameter array)
-									// so we create one value manual ;-)
-									foreach($rrule as $name => $value)
-									{
-										$attributes['RRULE'][] = $name . '=' . $value;
-									}
-									$attributes['RRULE'] = implode(';',$attributes['RRULE']);
-								}
-								break;
-
-							case 'EXDATE':
-								if ($event['recur_exception'])
-								{
-									$days = array();
-									foreach($event['recur_exception'] as $day)
-									{
-										$days[] = date('Ymd',$day);
-									}
-									$attributes['EXDATE'] = implode(',',$days);
-									$parameters['EXDATE']['VALUE'] = 'DATE';
-								}
-								break;
-
-							case 'PRIORITY':
-	 							$attributes['PRIORITY'] = (int) $this->priority_egw2ical[$event['priority']];
-	 							break;
-
-	 						case 'TRANSP':
-								if ($version == '1.0') {
-									$attributes['TRANSP'] = $event['non_blocking'] ? 1 : 0;
-								} else {
-									$attributes['TRANSP'] = $event['non_blocking'] ? 'TRANSPARENT' : 'OPAQUE';
-								}
-								break;
-
-							case 'CATEGORIES':
-								if ($event['category'])
-								{
-									$attributes['CATEGORIES'] = implode(',',$this->get_categories($event['category']));
-								}
-								break;
-
-							default:
-								if ($event[$egwFieldInfo['dbName']])	// dont write empty fields
-								{
-									$attributes[$icalFieldName]	= $event[$egwFieldInfo['dbName']];
-								}
-								break;
-						}
-					}
-				}
-
-				if(strtolower($this->productManufacturer) == 'nokia') {
-					if($event['special'] == '1') {
-						$attributes['X-EPOCAGENDAENTRYTYPE'] = 'ANNIVERSARY';
-						$attributes['DTEND'] = $attributes['DTSTART'];
-					} else {
-						$attributes['X-EPOCAGENDAENTRYTYPE'] = 'APPOINTMENT';
-					}
-				}
-
-				$modified = $GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'modify');
-				$created = $GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'add');
-				if (!$created && !$modified) $created = $event['modified'];
-				if ($created) $attributes['CREATED'] = $created;
-				if (!$modified) $modified = $event['modified'];
-				if ($modified) $attributes['LAST-MODIFIED'] = $modified;
-
-				foreach($event['alarm'] as $alarmID => $alarmData)
-				{
-					if ($version == '1.0')
-					{
-						$attributes['DALARM'] = $vcal->_exportDateTime($alarmData['time']);
-						$attributes['AALARM'] = $vcal->_exportDateTime($alarmData['time']);
-						// lets take only the first alarm
-						break;
-					}
-					else
-					{
-						// VCalendar 2.0 / RFC 2445
-
-						// skip over alarms that don't have the minimum required info
-						if (!$alarmData['offset'] && !$alarmData['time'])
-						{
-							error_log("Couldn't add VALARM (no alarm time info)");
-							continue;
-						}
-
-						// RFC requires DESCRIPTION for DISPLAY
-						if (!$event['title'] && !$event['description'])
-						{
-							error_log("Couldn't add VALARM (no description)");
-							continue;
-						}
-
-						$valarm = Horde_iCalendar::newComponent('VALARM',$vevent);
-						if ($alarmData['offset'])
-						{
-							$valarm->setAttribute('TRIGGER', -$alarmData['offset'],
-									array('VALUE' => 'DURATION', 'RELATED' => 'START'));
-						}
-						else
-						{
-							$valarm->setAttribute('TRIGGER', $alarmData['time'],
-									array('VALUE' => 'DATE-TIME'));
-						}
-
-						$valarm->setAttribute('ACTION','DISPLAY');
-						$valarm->setAttribute('DESCRIPTION',$event['title'] ? $event['title'] : $event['description']);
-						$vevent->addComponent($valarm);
-					}
-				}
-
-				$attributes['UID'] = $force_own_uid ? $eventGUID : $event['uid'];
-
-				foreach($attributes as $key => $value)
-				{
-					foreach(is_array($value) ? $value : array($value) as $valueID => $valueData)
-					{
-						$valueData = $GLOBALS['egw']->translation->convert($valueData,$GLOBALS['egw']->translation->charset(),'UTF-8');
-						$paramData = (array) $GLOBALS['egw']->translation->convert(is_array($value) ? $parameters[$key][$valueID] : $parameters[$key],
-							$GLOBALS['egw']->translation->charset(),'UTF-8');
-						//echo "$key:$valueID: value=$valueData, param=".print_r($paramDate,true)."\n";
-						$vevent->setAttribute($key, $valueData, $paramData);
-						$options = array();
-						if($key != 'RRULE' && preg_match('/([\000-\012\015\016\020-\037\075])/',$valueData))
-						{
-							$options['ENCODING'] = 'QUOTED-PRINTABLE';
-						}
-						if(preg_match('/([\177-\377])/',$valueData))
-						{
-							$options['CHARSET'] = 'UTF-8';
-						}
-						$vevent->setParameter($key, $options);
-					}
-				}
-				$vcal->addComponent($vevent);
-			}
-			//_debug_array($vcal->exportvCalendar());
-
-			return $vcal->exportvCalendar();
-		}
-
-		/**
-		 * Import an iCal
-		 *
-		 * @param string $_vcalData
-		 * @param int $cal_id=-1 must be -1 for new entrys!
-		 * @param string $etag=null if an etag is given, it has to match the current etag or the import will fail
-		 * @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag
-		 */
-		function importVCal($_vcalData, $cal_id=-1,$etag=null)
-		{
-			// our (patched) horde classes, do NOT unfold folded lines, which causes a lot trouble in the import
-			$_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData);
-
-			$vcal = &new Horde_iCalendar;
-			if(!$vcal->parsevCalendar($_vcalData))
-			{
-				return FALSE;
-			}
-
-			$version = $vcal->getAttribute('VERSION');
-
-			if(!is_array($this->supportedFields))
-			{
-				$this->setSupportedFields();
-			}
-			//echo "supportedFields="; _debug_array($this->supportedFields);
-
-			$syncevo_enddate_fix = False;
-			if( $this->productManufacturer == '' && $this->productName == '' )
-			{
-				// syncevolution needs an adjusted recur_enddate
-				$syncevo_enddate_fix = True;
-			}
-
-			$Ok = false;	// returning false, if file contains no components
-			foreach($vcal->getComponents() as $component)
-			{
-				if(is_a($component, 'Horde_iCalendar_vevent'))
-				{
-					$supportedFields = $this->supportedFields;
-					#$event = array('participants' => array());
-					$event		= array();
-					$alarms		= array();
-					$vcardData	= array(
-						'recur_type'		=> MCAL_RECUR_NONE,
-						'recur_exception'	=> array(),
-					);
-
-					// lets see what we can get from the vcard
-					foreach($component->_attributes as $attributes)
-					{
-						switch($attributes['name'])
-						{
-							case 'AALARM':
-							case 'DALARM':
-								if (preg_match('/.*Z$/',$attributes['value'],$matches)) {
-									$alarmTime = $vcal->_parseDateTime($attributes['value']);
-									$alarms[$alarmTime] = array(
-										'time' => $alarmTime
-									);
-								} elseif (preg_match('/(........T......);;(\d*);$/',$attributes['value'],$matches)) {
-									//error_log(print_r($matches,true));
-									$alarmTime = $vcal->_parseDateTime($matches[1]);
-									$alarms[$alarmTime] = array(
-										'time' => $alarmTime
-									);
-								} elseif (preg_match('/(........T......Z);;(\d*);$/',$attributes['value'],$matches)) {
-									//error_log(print_r($matches,true));
-									$alarmTime = $vcal->_parseDateTime($matches[1]);
-									$alarms[$alarmTime] = array(
-										'time' => $alarmTime
-									);
-								} elseif (preg_match('/(........T......)$/',$attributes['value'],$matches)) {
-									$alarmTime = $vcal->_parseDateTime($attributes['value']);
-									$alarms[$alarmTime] = array(
-										'time' => $alarmTime
-									);
-								}
-								break;
-							case 'CLASS':
-								$vcardData['public']		= (int)(strtolower($attributes['value']) == 'public');
-								break;
-							case 'DESCRIPTION':
-								$vcardData['description']	= $attributes['value'];
-								break;
-							case 'DTEND':
-								$dtend_ts = is_numeric($attributes['value']) ? $attributes['value'] : $this->date2ts($attributes['value']);
-								if(date('H:i:s',$dtend_ts) == '00:00:00') {
-									$dtend_ts -= 60;
-								}
-								$vcardData['end']		= $dtend_ts;
-								break;
-							case 'DTSTART':
-								$vcardData['start']		= $attributes['value'];
-								break;
-							case 'LOCATION':
-								$vcardData['location']	= $attributes['value'];
-								break;
-							case 'RRULE':
-								$recurence = $attributes['value'];
-								$type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence[0];
-								// vCard 2.0 values for all types
-								if (preg_match('/UNTIL=([0-9T]+)/',$recurence,$matches))
-								{
-									$vcardData['recur_enddate'] = $vcal->_parseDateTime($matches[1]);
-								}
-								elseif (preg_match('/COUNT=([0-9]+)/',$recurence,$matches))
-								{
-									$vcardData['recur_count'] = (int)$matches[1];
-								}
-								if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches))
-								{
-									// 1 is invalid,, egw uses 0 for interval
-									$vcardData['recur_interval'] = (int) $matches[1] != 0 ? (int) $matches[1] : 0;
-								}
-								if (!isset($vcardData['start']))	// it might not yet be set, because the RRULE is before it
-								{
-									$vcardData['start'] = self::_get_attribute($component->_attributes,'DTSTART');
-									$vcardData['end'] = self::_get_attribute($component->_attributes,'DTEND');
-								}
-								$vcardData['recur_data'] = 0;
-								switch($type)
-								{
-									case 'W':
-									case 'WEEKLY':
-										$days = array();
-										if(preg_match('/W(\d+) (.*) (.*)/',$recurence, $recurenceMatches))		// 1.0
-										{
-											$vcardData['recur_interval'] = $recurenceMatches[1];
-											$days = explode(' ',trim($recurenceMatches[2]));
-											if($recurenceMatches[3] != '#0')
-												$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[3]);
-											$recur_days = $this->recur_days_1_0;
-										}
-										elseif (preg_match('/BYDAY=([^;: ]+)/',$recurence,$recurenceMatches))	// 2.0
-										{
-											$days = explode(',',$recurenceMatches[1]);
-											$recur_days = $this->recur_days;
-										}
-										else	// no day given, use the day of dtstart
-										{
-											$vcardData['recur_data'] |= 1 << (int)date('w',$vcardData['start']);
-											$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
-										}
-										if ($days)
-										{
-											foreach($recur_days as $id => $day)
-		            						{
-		            							if (in_array(strtoupper(substr($day,0,2)),$days))
-	        									{
-	        										$vcardData['recur_data'] |= $id;
-	        									}
-	        								}
-											$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
-										}
-
-										if (!empty($vcardData['recur_count']))
-										{
-											$vcardData['recur_enddate'] = mktime(0,0,0,
-												date('m',$vcardData['start']),
-												date('d',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)*7),
-												date('Y',$vcardData['start']));
-										}
-										break;
-
-									case 'D':	// 1.0
-										if(preg_match('/D(\d+) #(.\d)/', $recurence, $recurenceMatches)) {
-											$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[2] > 0 && $vcardData['end']) {
-												$vcardData['recur_enddate'] = mktime(
-													date('H', $vcardData['end']),
-													date('i', $vcardData['end']),
-													date('s', $vcardData['end']),
-													date('m', $vcardData['end']),
-													date('d', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval']),
-													date('Y', $vcardData['end'])
-												);
-											}
-										} elseif(preg_match('/D(\d+) (.*)/', $recurence, $recurenceMatches)) {
-											$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[2] != '#0') {
-												$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
-											}
-										} else {
-											break;
-										}
-										// fall-through
-									case 'DAILY':	// 2.0
-										$vcardData['recur_type'] = MCAL_RECUR_DAILY;
-
-										if (!empty($vcardData['recur_count']))
-										{
-											$vcardData['recur_enddate'] = mktime(0,0,0,
-												date('m',$vcardData['start']),
-												date('d',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)),
-												date('Y',$vcardData['start']));
-										}
-										break;
-
-									case 'M':
-										if(preg_match('/MD(\d+) #(.\d)/', $recurence, $recurenceMatches)) {
-											$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY;
-											$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[2] > 0 && $vcardData['end']) {
-												$vcardData['recur_enddate'] = mktime(
-													date('H', $vcardData['end']),
-													date('i', $vcardData['end']),
-													date('s', $vcardData['end']),
-													date('m', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval']),
-													date('d', $vcardData['end']),
-													date('Y', $vcardData['end'])
-												);
-											}
-										} elseif(preg_match('/MD(\d+) (.*)/',$recurence, $recurenceMatches)) {
-											$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY;
-											if($recurenceMatches[1] > 1)
-												$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[2] != '#0')
-												$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
-										} elseif(preg_match('/MP(\d+) (.*) (.*) (.*)/',$recurence, $recurenceMatches)) {
-											$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_WDAY;
-											if($recurenceMatches[1] > 1)
-												$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[4] != '#0')
-												$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[4]);
-										}
-										break;
-									case 'MONTHLY':
-										$vcardData['recur_type'] = strpos($recurence,'BYDAY') !== false ?
-											MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY;
-
-										if (!empty($vcardData['recur_count']))
-										{
-											$vcardData['recur_enddate'] = mktime(0,0,0,
-												date('m',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)),
-												date('d',$vcardData['start']),
-												date('Y',$vcardData['start']));
-										}
-										break;
-
-									case 'Y':		// 1.0
-										if(preg_match('/YM(\d+) #(.\d)/', $recurence, $recurenceMatches)) {
-											$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[2] > 0 && $vcardData['end']) {
-												$vcardData['recur_enddate'] = mktime(
-													date('H', $vcardData['end']),
-													date('i', $vcardData['end']),
-													date('s', $vcardData['end']),
-													date('m', $vcardData['end']),
-													date('d', $vcardData['end']),
-													date('Y', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval'])
-												);
-											}
-										} elseif(preg_match('/YM(\d+) (.*)/',$recurence, $recurenceMatches)) {
-											$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[2] != '#0') {
-												$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
-											}
-										} else {
-											break;
-										}
-										// fall-through
-									case 'YEARLY':	// 2.0
-										$vcardData['recur_type'] = MCAL_RECUR_YEARLY;
-
-										if (!empty($vcardData['recur_count']))
-										{
-											$vcardData['recur_enddate'] = mktime(0,0,0,
-												date('m',$vcardData['start']),
-												date('d',$vcardData['start']),
-												date('Y',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)));
-										}
-										break;
-								}
-								if( $syncevo_enddate_fix && $vcardData['recur_enddate'] )
-								{
-									// Does syncevolution need to adjust recur_enddate
-									$vcardData['recur_enddate'] = (int)$vcardData['recur_enddate'] + 86400;
-								}
-								break;
-							case 'EXDATE':
-								$vcardData['recur_exception'] = array_merge($vcardData['recur_exception'],$attributes['value']);
-								break;
-							case 'SUMMARY':
-								$vcardData['title']		= $attributes['value'];
-								break;
-							case 'UID':
-								$event['uid'] = $vcardData['uid'] = $attributes['value'];
-								if ($cal_id <= 0 && !empty($vcardData['uid']) && ($uid_event = $this->read($vcardData['uid'])))
-								{
-									$cal_id = $event['id'] = $uid_event['id'];
-									unset($uid_event);
-								}
-								break;
-	 						case 'TRANSP':
-	 							if($version == '1.0') {
-	 								$vcardData['non_blocking'] = $attributes['value'] == 1;
-	 							} else {
-									$vcardData['non_blocking'] = $attributes['value'] == 'TRANSPARENT';
-								}
-								break;
-							case 'PRIORITY':
-	 							$vcardData['priority'] = (int) $this->priority_ical2egw[$attributes['value']];
-	 							break;
-	 						case 'CATEGORIES':
-	 							if ($attributes['value'])
-	 							{
-	 								$vcardData['category'] = $this->find_or_add_categories(explode(',',$attributes['value']));
-	 							}
-								else
-								{
-	 								$vcardData['category'] = array();
-								}
-	 							break;
-	 						case 'ATTENDEE':
-	 							if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) ||
-	 								preg_match('/<([@.a-z0-9_-]+)>/i',$attributes['value'],$matches))
- 								{
- 									$email = $matches[1];
- 								}
- 								elseif(strpos($attributes['value'],'@') !== false)
- 								{
- 									$email = $attributes['value'];
- 								}
-	 							if (($uid = $attributes['params']['X-EGROUPWARE-UID']) &&
-	 								($info = $this->resource_info($uid)) && $info['email'] == $email)
-	 							{
-	 								// we use the (checked) X-EGROUPWARE-UID
-	 							}
-	 							/*elseif($attributes['params']['CUTYPE'] == 'RESOURCE')
-	 							{
-
-	 							}*/
-		 						elseif($attributes['value'] == 'Unknown')
-	 							{
-	 								$uid = $GLOBALS['egw_info']['user']['account_id'];
-	 							}
-	 							elseif (($uid = $GLOBALS['egw']->accounts->name2id($email,'account_email')))
-	 							{
-	 								// we use the account we found
-	 							}
-								elseif ((list($data) = ExecMethod2('addressbook.addressbook_bo.search',array(
-									'email' => $email,
-									'email_home' => $email,
-								),true,'','','',false,'OR')))
-								{
-									$uid = 'c'.$data['id'];
-								}
-	 							else
-	 							{
-	 								$uid = 'e'.($attributes['params']['CN'] ? $attributes['params']['CN'].' <'.$email.'>' : $email);
-	 							}
-	 							$event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ?
-	 									$this->status_ical2egw[strtoupper($attributes['params']['PARTSTAT'])] :
-	 									($uid == $event['owner'] ? 'A' : 'U');
-	 							break;
-	 						case 'ORGANIZER':	// will be written direct to the event
-	 							if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
-	 								($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email')))
-	 							{
-	 								$event['owner'] = $uid;
-	 							}
-	 							break;
-	 						case 'CREATED':		// will be written direct to the event
-	 							if ($event['modified']) break;
-	 							// fall through
-	 						case 'LAST-MODIFIED':	// will be written direct to the event
-								$event['modified'] = $attributes['value'];
-								break;
-						}
-					}
-
-					// check if the entry is a birthday
-					// this field is only set from NOKIA clients
-					$agendaEntryType = $component->getAttribute('X-EPOCAGENDAENTRYTYPE');
-					if (!is_a($agendaEntryType, 'PEAR_Error')) {
-						if(strtolower($agendaEntryType) == 'anniversary') {
-							$event['special'] = '1';
-							// make it a whole day event for eGW
-							$vcardData['end'] = $vcardData['start'] + 86399;
-						}
-					}
-
-					if(!empty($vcardData['recur_enddate']))
-					{
-						// reset recure_enddate to 00:00:00 on the last day
-						$vcardData['recur_enddate'] = mktime(0, 0, 0,
-							date('m',$vcardData['recur_enddate']),
-							date('d',$vcardData['recur_enddate']),
-							date('Y',$vcardData['recur_enddate'])
-						);
-					}
-					//echo "event=";_debug_array($vcardData);
-
-					// now that we know what the vard provides, we merge that data with the information we have about the device
-					$event['priority']		= 2;
-					if($cal_id > 0)
-					{
-						$event['id'] = $cal_id;
-					}
-					while(($fieldName = array_shift($supportedFields)))
-					{
-						switch($fieldName)
-						{
-							case 'alarms':
-								// not handled here
-								break;
-							case 'recur_type':
-								$event['recur_type'] = $vcardData['recur_type'];
-								if ($event['recur_type'] != MCAL_RECUR_NONE)
-								{
-									foreach(array('recur_interval','recur_enddate','recur_data','recur_exception') as $r)
-									{
-										if(isset($vcardData[$r]))
-										{
-											$event[$r] = $vcardData[$r];
-										}
-									}
-								}
-								unset($supportedFields['recur_type']);
-								unset($supportedFields['recur_interval']);
-								unset($supportedFields['recur_enddate']);
-								unset($supportedFields['recur_data']);
-								break;
-							default:
-								if (isset($vcardData[$fieldName]))
-								{
-									$event[$fieldName] = $vcardData[$fieldName];
-								}
-								unset($supportedFields[$fieldName]);
-								break;
-						}
-					}
-
-					// add ourself to new events as participant
-	 				if($cal_id == -1 && !isset($this->supportedFields['participants']))
-	 				{
-						$event['participants'] = array($GLOBALS['egw_info']['user']['account_id'] => 'A');
-	 				}
-
-					// If this is an updated meeting, and the client doesn't support
-					// participants, add them back
-					if( $cal_id > 0 && !isset($this->supportedFields['participants']))
-					{
-						if (($egw_event = $this->read($cal_id)))
-						{
-							$event['participants'] = $egw_event['participants'];
-							$event['participant_types'] = $egw_event['participant_types'];
-						}
-					}
-
-					// Check for resources, and don't remove them
-					if( $cal_id > 0 )
-					{
-						// for each existing participant:
-						if (($egw_event = $this->read($cal_id)))
-						{
-							foreach( $egw_event['participants'] as $uid => $status )
-							{
-								// Is it a resource and not longer present in the event?
-								if ( $uid[0] == 'r' && !isset($event['participants'][$uid]) )
-								{
-									// Add it back in
-									$event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status;
-								}
-							}
-						}
-					}
-
-					// check if iCal changes the organizer, which is not allowed
-					if ($cal_id > 0 && ($egw_event = $this->read($cal_id)) && $event['owner'] != $egw_event['owner'])
-					{
-						$event['owner'] = $egw_event['owner'];	// set it back to the original owner
-					}
-
-					#error_log('ALARMS');
-					#error_log(print_r($event, true));
-
-					// if an etag is given, include it in the update
-					if (!is_null($etag))
-					{
-						$event['etag'] = $etag;
-					}
-					if (!($Ok = $this->update($event, TRUE)))
-					{
-						// check if current user is an attendee and tried to change his status
-						if ($Ok === false && $cal_id && ($egw_event = $this->read($cal_id)) && isset($egw_event['participants'][$this->user]) &&
-							$egw_event['participants'][$this->user] !== $event['participants'][$this->user])
-						{
-							$this->set_status($egw_event,$this->user,
-								$status = $event['participants'][$this->user] ? $event['participants'][$this->user] : 'R');
-
-							$Ok = $cal_id;
-							continue;
-						}
-						break;	// stop with the first error
-					}
-					else
-					{
-						$eventID =& $Ok;
-
-						// handle the alarms
-						foreach ($component->getComponents() as $valarm)
-						{
-							if (is_a($valarm, 'Horde_iCalendar_valarm'))
-							{
-								$this->valarm2egw($alarms,$valarm);
-							}
-						}
-
-						if(count($alarms) > 0 || (isset($this->supportedFields['alarms'])  && count($alarms) == 0))
-						{
-							// delete the old alarms
-							$updatedEvent = $this->read($eventID);
-							foreach($updatedEvent['alarm'] as $alarmID => $alarmData)
-							{
-								$this->delete_alarm($alarmID);
-							}
-						}
-
-						foreach($alarms as $alarm)
-						{
-							$alarm['offset'] = $event['start'] - $alarm['time'];
-							$alarm['owner'] = $GLOBALS['egw_info']['user']['account_id'];
-							$this->save_alarm($eventID, $alarm);
-						}
-					}
-				}
-			}
-			return $Ok;
-		}
-
-		/**
-		 * get the value of an attribute by its name
-		 *
-		 * @param array $attributes
-		 * @param string $name eg. 'DTSTART'
-		 * @param string $what='value'
-		 * @return mixed
-		 */
-		static function _get_attribute($components,$name,$what='value')
-		{
-			foreach($components as $attribute)
-			{
-				if ($attribute['name'] == $name)
-				{
-					return !$what ? $attribute : $attribute[$what];
-				}
-			}
-			return false;
-		}
-
-		static function valarm2egw(&$alarms, &$valarm)
-		{
-			$count = 0;
-			foreach($valarm->_attributes as $vattr)
-			{
-				switch($vattr['name'])
-				{
-					case 'TRIGGER':
-						$vtype = (isset($vattr['params']['VALUE']))
-							? $vattr['params']['VALUE'] : 'DURATION'; //default type
-						switch ($vtype)
-						{
-							case 'DURATION':
-								if (isset($vattr['params']['RELATED'])
-									&& $vattr['params']['RELATED'] != 'START')
-								{
-									error_log("Unsupported VALARM offset anchor ".$vattr['params']['RELATED']);
-								}
-								else
-								{
-									$alarms[] = array('offset' => -$vattr['value']);
-									$count++;
-								}
-								break;
-							case 'DATE-TIME':
-								$alarms[] = array('time' => $vattr['value']);
-								$count++;
-								break;
-							default:
-								// we should also do ;RELATED=START|END
-								error_log('VALARM/TRIGGER: unsupported value type:' . $vtype);
-						}
-						break;
-					// case 'ACTION':
-					// 	break;
-					// case 'DISPLAY':
-					// 	break;
-
-					default:
-						error_log('VALARM field:' .$vattr['name'] .':' . print_r($vattrval,true) . ' HAS NO CONVERSION YET');
-				}
-			}
-			return $count;
-		}
-
-		function setSupportedFields($_productManufacturer='file', $_productName='')
-		{
-			// save them vor later use
-			$this->productManufacturer = $_productManufacturer;
-			$this->productName = $_productName;
-
-			$defaultFields['minimal'] = array(
-				'public'			=> 'public',
-				'description'		=> 'description',
-				'end'				=> 'end',
-				'start'				=> 'start',
-				'location'			=> 'location',
-				'recur_type'		=> 'recur_type',
-				'recur_interval'	=> 'recur_interval',
-				'recur_data'		=> 'recur_data',
-				'recur_enddate'		=> 'recur_enddate',
-				'title'				=> 'title',
-				'alarms'			=> 'alarms',
-			);
-
-			$defaultFields['basic'] = $defaultFields['minimal'] + array(
-				'recur_exception'	=> 'recur_exception',
-				'priority'			=> 'priority',
-			);
-
-			$defaultFields['nexthaus'] = $defaultFields['basic'] + array(
-				'participants'		=> 'participants',
-			);
-
-			$defaultFields['synthesis'] = $defaultFields['basic'] + array(
-				'non_blocking'		=> 'non_blocking',
-				'category'			=> 'category',
-			);
-
-			$defaultFields['evolution'] = $defaultFields['basic'] + array(
-				'participants'		=> 'participants',
-				'owner'				=> 'owner',
-				'category'			=> 'category',
-			);
-
-			$defaultFields['full'] = $defaultFields['basic'] + array(
-				'participants'		=> 'participants',
-				'owner'				=> 'owner',
-				'category'			=> 'category',
-				'non_blocking'		=> 'non_blocking',
-			);
-
-
-			switch(strtolower($_productManufacturer))
-			{
-				case 'nexthaus corporation':
-				case 'nexthaus corp':
-					switch(strtolower($_productName))
-					{
-						default:
-							$this->supportedFields = $defaultFields['nexthaus'];
-							break;
-					}
-					break;
-
-				// multisync does not provide anymore information then the manufacturer
-				// we suppose multisync with evolution
-				case 'the multisync project':
-					switch(strtolower($_productName))
-					{
-						default:
-							$this->supportedFields = $defaultFields['basic'];
-							break;
-					}
-					break;
-
-				case 'nokia':
-					switch(strtolower($_productName))
-					{
-						case 'e61':
-							$this->supportedFields = $defaultFields['minimal'];
-							break;
-						default:
-							error_log("Unknown Nokia phone '$_productName', assuming E61");
-							$this->supportedFields = $defaultFields['minimal'];
-							break;
-					}
-					break;
-
-				case 'sonyericsson':
-				case 'sony ericsson':
-					switch(strtolower($_productName))
-					{
-						case 'd750i':
-						case 'p910i':
-							$this->supportedFields = $defaultFields['basic'];
-							break;
-						default:
-							error_log("Unknown Sony Ericsson phone '$_productName' assuming d750i");
-							$this->supportedFields = $defaultFields['basic'];
-							break;
-					}
-					break;
-
-				case 'synthesis ag':
-					switch(strtolower($_productName))
-					{
-						case 'sysync client pocketpc std':
-						case 'sysync client pocketpc pro':
-							$this->supportedFields = $defaultFields['full'];
-							break;
-						default:
-							$this->supportedFields = $defaultFields['synthesis'];
-							break;
-					}
-					break;
-
-				//Syncevolution compatibility
-				case 'patrick ohly':
-					$this->supportedFields = $defaultFields['evolution'];
-					break;
-
-				case '': // seems syncevolution 0.5 doesn't send a manufacturer
-					error_log("No vendor name, assuming syncevolution 0.5");
-					$this->supportedFields = $defaultFields['evolution'];
-					break;
-
-				case 'file':	// used outside of SyncML, eg. by the calendar itself ==> all possible fields
-					$this->supportedFields = $defaultFields['full'];
-					break;
-
-				// the fallback for SyncML
-				default:
-					error_log("Unknown calendar SyncML client: manufacturer='$_productManufacturer'  product='$_productName'");
-					$this->supportedFields = $defaultFields['full'];
-					break;
-			}
-		}
-
-		function icaltoegw($_vcalData)
-		{
-			// our (patched) horde classes, do NOT unfold folded lines, which causes a lot trouble in the import
-			$_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData);
-
-			$vcal = &new Horde_iCalendar;
-			if(!$vcal->parsevCalendar($_vcalData))
-			{
-				return FALSE;
-			}
-
-			if(!is_array($this->supportedFields))
-			{
-				$this->setSupportedFields();
-			}
-			//echo "supportedFields="; _debug_array($this->supportedFields);
-
-			$Ok = false;	// returning false, if file contains no components
-			foreach($vcal->getComponents() as $component)
-			{
-				if(is_a($component, 'Horde_iCalendar_vevent'))
-				{
-					$supportedFields = $this->supportedFields;
-					#$event = array('participants' => array());
-					$event		= array();
-					$alarms		= array();
-					$vcardData	= array('recur_type' => 0);
-
-					// lets see what we can get from the vcard
-					foreach($component->_attributes as $attributes)
-					{
-						switch($attributes['name'])
-						{
-							case 'AALARM':
-							case 'DALARM':
-								if (preg_match('/.*Z$/',$attributes['value'],$matches))
-								{
-									$alarmTime = $vcal->_parseDateTime($attributes['value']);
-									$alarms[$alarmTime] = array(
-										'time' => $alarmTime
-									);
-								}
-								break;
-							case 'CLASS':
-								$vcardData['public']		= (int)(strtolower($attributes['value']) == 'public');
-								break;
-							case 'DESCRIPTION':
-								$vcardData['description']	= $attributes['value'];
-								break;
-							case 'DTEND':
-								if(date('H:i:s',$attributes['value']) == '00:00:00')
-									$attributes['value']--;
-								$vcardData['end']		= $attributes['value'];
-								break;
-							case 'DTSTART':
-								$vcardData['start']		= $attributes['value'];
-								break;
-							case 'LOCATION':
-								$vcardData['location']	= $attributes['value'];
-								break;
-							case 'RRULE':
-								$recurence = $attributes['value'];
-								$type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence[0];
-								// vCard 2.0 values for all types
-								if (preg_match('/UNTIL=([0-9T]+)/',$recurence,$matches))
-								{
-									$vcardData['recur_enddate'] = $vcal->_parseDateTime($matches[1]);
-								}
-								if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches))
-								{
-									$vcardData['recur_interval'] = (int) $matches[1];
-								}
-								$vcardData['recur_data'] = 0;
-								switch($type)
-								{
-									case 'W':
-									case 'WEEKLY':
-										$days = array();
-										if(preg_match('/W(\d+) (.*) (.*)/',$recurence, $recurenceMatches))		// 1.0
-										{
-											$vcardData['recur_interval'] = $recurenceMatches[1];
-											$days = explode(' ',trim($recurenceMatches[2]));
-											if($recurenceMatches[3] != '#0')
-												$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[3]);
-											$recur_days = $this->recur_days_1_0;
-										}
-										elseif (preg_match('/BYDAY=([^;: ]+)/',$recurence,$recurenceMatches))	// 2.0
-										{
-											$days = explode(',',$recurenceMatches[1]);
-											$recur_days = $this->recur_days;
-										}
-										if ($days)
-										{
-											foreach($recur_days as $id => $day)
-				            						{
-				            							if (in_array(strtoupper(substr($day,0,2)),$days))
-		            									{
-		            										$vcardData['recur_data'] |= $id;
-		            									}
-		            								}
-											$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
-										}
-										break;
-
-									case 'D':		// 1.0
-										if(!preg_match('/D(\d+) (.*)/',$recurence, $recurenceMatches)) break;
-										$vcardData['recur_interval'] = $recurenceMatches[1];
-										if($recurenceMatches[2] != '#0')
-											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
-										// fall-through
-									case 'DAILY':	// 2.0
-										$vcardData['recur_type'] = MCAL_RECUR_DAILY;
-										break;
-
-									case 'M':
-										if(preg_match('/MD(\d+) (.*)/',$recurence, $recurenceMatches))
-										{
-											$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY;
-											if($recurenceMatches[1] > 1)
-												$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[2] != '#0')
-												$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
-										}
-										elseif(preg_match('/MP(\d+) (.*) (.*) (.*)/',$recurence, $recurenceMatches))
-										{
-											$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_WDAY;
-											if($recurenceMatches[1] > 1)
-												$vcardData['recur_interval'] = $recurenceMatches[1];
-											if($recurenceMatches[4] != '#0')
-												$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[4]);
-										}
-										break;
-									case 'MONTHLY':
-										$vcardData['recur_type'] = strpos($recurence,'BYDAY') !== false ?
-											MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY;
-										break;
-
-									case 'Y':		// 1.0
-										if(!preg_match('/YM(\d+) (.*)/',$recurence, $recurenceMatches)) break;
-										$vcardData['recur_interval'] = $recurenceMatches[1];
-										if($recurenceMatches[2] != '#0')
-											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
-										// fall-through
-									case 'YEARLY':	// 2.0
-										$vcardData['recur_type'] = MCAL_RECUR_YEARLY;
-										break;
-								}
-								break;
-							case 'EXDATE':
-								$vcardData['recur_exception'] = $attributes['value'];
-								break;
-							case 'SUMMARY':
-								$vcardData['title']		= $attributes['value'];
-								break;
-							case 'UID':
-								$event['uid'] = $vcardData['uid'] = $attributes['value'];
-								if ($cal_id <= 0 && !empty($vcardData['uid']) && ($uid_event = $this->read($vcardData['uid'])))
-								{
-									$event['id'] = $uid_event['id'];
-									unset($uid_event);
-								}
-								break;
-	 						case 'TRANSP':
-								$vcardData['non_blocking'] = $attributes['value'] == 'TRANSPARENT';
-								break;
-							case 'PRIORITY':
-								if ($this->productManufacturer == 'nexthaus corporation'
-									|| $this->productManufacturer == 'nexthaus corp')
-								{
-									$vcardData['priority'] = $attributes['value'] == 1 ? 3 : 2; // 1=high, 2=normal
-								}
-								else
-								{
-	 								$vcardData['priority'] = (int) $this->priority_ical2egw[$attributes['value']];
-								}
-	 							break;
-	 						case 'CATEGORIES':
-	 							if ($attributes['value'])
-	 							{
-									$vcardData['category'] = $this->find_or_add_categories(explode(',',$attributes['value']));
-								}
-								else
-								{
-	 								$vcardData['category'] = array();
-								}
-	 							break;
-	 						case 'ATTENDEE':
-	 							if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
-	 								($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email')))
-	 							{
-	 								$event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ?
-	 									$this->status_ical2egw[strtoupper($attributes['params']['PARTSTAT'])] :
-	 									($uid == $event['owner'] ? 'A' : 'U');
-	 							}
-	 							break;
-	 						case 'ORGANIZER':	// will be written direct to the event
-	 							if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
-	 								($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email')))
-	 							{
-	 								$event['owner'] = $uid;
-	 							}
-	 							break;
-	 						case 'CREATED':		// will be written direct to the event
-	 							if ($event['modified']) break;
-	 							// fall through
-	 						case 'LAST-MODIFIED':	// will be written direct to the event
-								$event['modified'] = $attributes['value'];
-								break;
-						}
-					}
-
-					// check if the entry is a birthday
-					// this field is only set from NOKIA clients
-					$agendaEntryType = $component->getAttribute('X-EPOCAGENDAENTRYTYPE');
-					if (!is_a($agendaEntryType, 'PEAR_Error')) {
-						if(strtolower($agendaEntryType) == 'anniversary') {
-							$event['special'] = '1';
-							$vcardData['end'] = $vcardData['start'] + 86399;
-						}
-					}
-
-					if(!empty($vcardData['recur_enddate']))
-					{
-						// reset recure_enddate to 00:00:00 on the last day
-						$vcardData['recur_enddate'] = mktime(0, 0, 0,
-							date('m',$vcardData['recur_enddate']),
-							date('d',$vcardData['recur_enddate']),
-							date('Y',$vcardData['recur_enddate'])
-						);
-					}
-					//echo "event=";_debug_array($vcardData);
-
-					while(($fieldName = array_shift($supportedFields)))
-					{
-						switch($fieldName)
-						{
-							case 'recur_interval':
-							case 'recur_enddate':
-							case 'recur_data':
-							case 'recur_exception':
-							case 'alarms':
-								// not handled here
-								break;
-							case 'recur_type':
-								$event['recur_type'] = $vcardData['recur_type'];
-								if ($event['recur_type'] != MCAL_RECUR_NONE)
-								{
-									foreach(array('recur_interval','recur_enddate','recur_data','recur_exception') as $r)
-									{
-										if(isset($vcardData[$r]))
-										{
-											$event[$r] = $vcardData[$r];
-										}
-									}
-								}
-								break;
-							default:
-								if (isset($vcardData[$fieldName]))
-								{
-									$event[$fieldName] = $vcardData[$fieldName];
-								}
-								break;
-						}
-					}
-
-					return $event;
-				}
-			}
-
-			return false;
-		}
-
-		function search($_vcalData)
-		{
-			if(!$event = $this->icaltoegw($_vcalData)) {
-				return false;
-			}
-
-			$query = array(
-				'cal_start='.$this->date2ts($event['start'],true),	// true = Server-time
-				'cal_end='.$this->date2ts($event['end'],true),
-			);
-
-			#foreach(array('title','location','priority','public','non_blocking') as $name) {
-			foreach(array('title','location','public','non_blocking') as $name) {
-				if (isset($event[$name])) $query['cal_'.$name] = $event[$name];
-			}
-
-			if($foundEvents = parent::search(array(
-				'user'  => $this->user,
-				'query' => $query,
-			))) {
-				if(is_array($foundEvents)) {
-					$event = array_shift($foundEvents);
-					return $event['id'];
-				}
-			}
-			return false;
-		}
-
-		/**
-		 * Create a freebusy vCal for the given user(s)
-		 *
-		 * @param int $user account_id
-		 * @param mixed $end=null end-date, default now+1 month
-		 * @return string
-		 */
-		function freebusy($user,$end=null)
-		{
-			if (!$end) $end = $this->now_su + 100*DAY_s;	// default next 100 days
-
-			$vcal = &new Horde_iCalendar;
-			$vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
-				strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
-			$vcal->setAttribute('VERSION','2.0');
-
-			$vfreebusy = Horde_iCalendar::newComponent('VFREEBUSY',$vcal);
-			$parameters = array(
-				'ORGANIZER' => $GLOBALS['egw']->translation->convert(
-					$GLOBALS['egw']->accounts->id2name($user,'account_firstname').' '.
-					$GLOBALS['egw']->accounts->id2name($user,'account_lastname'),
-					$GLOBALS['egw']->translation->charset(),'utf-8'),
-			);
-			foreach(array(
-				'URL' => $this->freebusy_url($user),
-				'DTSTART' => $this->date2ts($this->now_su,true),	// true = server-time
-				'DTEND' => $this->date2ts($end,true),	// true = server-time
-			  	'ORGANIZER' => $GLOBALS['egw']->accounts->id2name($user,'account_email'),
-				'DTSTAMP' => time(),
-			) as $attr => $value)
-			{
-				$vfreebusy->setAttribute($attr, $value, $parameters[$name]);
-			}
-			$fbdata = parent::search(array(
-						'start' => $this->now_su,
-						'end'   => $end,
-						'users' => $user,
-						'date_format' => 'server',
-						'show_rejected' => false,
-					));
-			if (is_array($fbdata))
-			{
-				foreach ($fbdata as $event)
-				{
-					if ($event['non_blocking']) continue;
-
-					$vfreebusy->setAttribute('FREEBUSY',array(array(
-						'start' => $event['start'],
-						'end' => $event['end'],
-					)));
-				}
-			}
-			$vcal->addComponent($vfreebusy);
-
-			return $vcal->exportvCalendar();
-		}
-	}
diff --git a/calendar/inc/class.ajaxcalendar.inc.php b/calendar/inc/class.calendar_ajax.inc.php
similarity index 76%
rename from calendar/inc/class.ajaxcalendar.inc.php
rename to calendar/inc/class.calendar_ajax.inc.php
index a640d64646..2206f95da2 100644
--- a/calendar/inc/class.ajaxcalendar.inc.php
+++ b/calendar/inc/class.calendar_ajax.inc.php
@@ -7,34 +7,26 @@
  * @package calendar
  * @copyright (c) 2006 by Christian Binder 
  * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
- * @version $Id: class.ajaxcalendar.inc.php 22777 2006-11-26 20:55:00Z jaytraxx $ 
+ * @version $Id: class.ajaxcalendar.inc.php 22777 2006-11-26 20:55:00Z jaytraxx $
  */
 
-require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.bocalupdate.inc.php');
-
 /**
  * General object of the calendar ajax class
- *
- * @package calendar
- * @author Christian Binder 
- * @copyright (c) 2006 by Christian Binder 
- * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
  */
+class calendar_ajax {
 
-class ajaxcalendar {
-	
 	/**
 	 * calendar object to handle events
-	 * 
-	 * @var bocalupdate
+	 *
+	 * @var calendar_boupdate
 	 */
 	var $calendar;
 
-	function ajaxcalendar() 
+	function __construct()
 	{
-		$this->calendar = new bocalupdate;
+		$this->calendar = new calendar_boupdate();
 	}
-	
+
 	/**
 	 * moves an event to another date/time
 	 *
@@ -54,22 +46,22 @@ class ajaxcalendar {
 
 		$event=$this->calendar->read($eventId);
 		$duration=$event['end']-$event['start'];
-		
+
 		$event['start'] = $this->calendar->date2ts($targetDateTime);
 		$event['end'] = $event['start']+$duration;
-		
+
 		$conflicts=$this->calendar->update($event);
 
 		$response =& new xajaxResponse();
 		if(!is_array($conflicts))
 		{
-			$response->addRedirect($PHP_SELF);
+			$response->addRedirect('');
 		}
 		else
 		{
 			$response->addScriptCall(
 				'egw_openWindowCentered2',
-				$GLOBALS['egw_info']['server']['webserver_url'].'/index.php?menuaction=calendar.uiforms.edit
+				$GLOBALS['egw_info']['server']['webserver_url'].'/index.php?menuaction=calendar.calendar_uiforms.edit
 					&cal_id='.$event['id']
 					.'&start='.$event['start']
 					.'&end='.$event['end']
diff --git a/calendar/inc/class.bocal.inc.php b/calendar/inc/class.calendar_bo.inc.php
similarity index 97%
rename from calendar/inc/class.bocal.inc.php
rename to calendar/inc/class.calendar_bo.inc.php
index 19713ebb63..1f1153ddb0 100644
--- a/calendar/inc/class.bocal.inc.php
+++ b/calendar/inc/class.calendar_bo.inc.php
@@ -5,13 +5,11 @@
  * @link http://www.egroupware.org
  * @package calendar
  * @author Ralf Becker 
- * @copyright (c) 2004-7 by RalfBecker-At-outdoor-training.de
+ * @copyright (c) 2004-8 by RalfBecker-At-outdoor-training.de
  * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
  * @version $Id$
  */
 
-require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.socal.inc.php');
-
 if (!defined('ACL_TYPE_IDENTIFER'))	// used to mark ACL-values for the debug_message methode
 {
 	define('ACL_TYPE_IDENTIFER','***ACL***');
@@ -27,8 +25,6 @@ define('WEEK_s',7*DAY_s);
  */
 define('EGW_ACL_READ_FOR_PARTICIPANTS',EGW_ACL_CUSTOM_1);
 
-require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.socal.inc.php');
-
 /**
  * Class to access all calendar data
  *
@@ -47,7 +43,7 @@ require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.socal.inc.php');
  *
  * All permanent debug messages of the calendar-code should done via the debug-message method of this class !!!
  */
-class bocal
+class calendar_bo
 {
 	/**
 	 * @var int $debug name of method to debug or level of debug-messages:
@@ -152,11 +148,11 @@ class bocal
 	/**
 	 * Constructor
 	 */
-	function bocal()
+	function __construct()
 	{
 		if ($this->debug > 0) $this->debug_message('bocal::bocal() started',True,$param);
 
-		$this->so = new socal();
+		$this->so = new calendar_so();
 		$this->datetime = $GLOBALS['egw']->datetime;
 
 		$this->common_prefs =& $GLOBALS['egw_info']['user']['preferences']['common'];
@@ -1737,31 +1733,6 @@ class bocal
 		return $result;
 	}
 
-	/**
-	 * Hook called by link-class to include calendar in the appregistry of the linkage
-	 *
-	 * @param array/string $location location and other parameters (not used)
-	 * @return array with method-names
-	 */
-	function search_link($location)
-	{
-		return array(
-			'query' => 'calendar.bocal.link_query',
-			'title' => 'calendar.bocal.link_title',
-			'view'  => array(
-				'menuaction' => 'calendar.uiforms.edit',
-			),
-			'view_id'    => 'cal_id',
-			'view_popup' => '750x400',
-			'add'        => array(
-				'menuaction' => 'calendar.uiforms.edit',
-			),
-			'add_app'    => 'link_app',
-			'add_id'     => 'link_id',
-			'add_popup'  => '750x400',
-		);
-	}
-
 	/**
 	 * sets the default prefs, if they are not already set (on a per pref. basis)
 	 *
@@ -1826,7 +1797,7 @@ class bocal
 	 * @param int/string $user account_id or account_lid
 	 * @param string $pw=null password
 	 */
-	function freebusy_url($user,$pw=null)
+	static function freebusy_url($user,$pw=null)
 	{
 		if (is_numeric($user)) $user = $GLOBALS['egw']->accounts->id2name($user);
 
@@ -1836,20 +1807,3 @@ class bocal
 			($pw ? '&password='.urlencode($pw) : '');
 	}
 }
-
-if (!function_exists('array_intersect_key'))	// php5.1 function
-{
-	function array_intersect_key($array1,$array2)
-	{
-		$intersection = $keys = array();
-		foreach(func_get_args() as $arr)
-		{
-			$keys[] = array_keys((array)$arr);
-		}
-		foreach(call_user_func_array('array_intersect',$keys) as $key)
-		{
-			$intersection[$key] = $array1[$key];
-		}
-		return $intersection;
-	}
-}
diff --git a/calendar/inc/class.bocalupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php
similarity index 99%
rename from calendar/inc/class.bocalupdate.inc.php
rename to calendar/inc/class.calendar_boupdate.inc.php
index a6af70dcd6..c05ed330d3 100644
--- a/calendar/inc/class.bocalupdate.inc.php
+++ b/calendar/inc/class.calendar_boupdate.inc.php
@@ -5,13 +5,11 @@
  * @link http://www.egroupware.org
  * @package calendar
  * @author Ralf Becker 
- * @copyright (c) 2005-7 by RalfBecker-At-outdoor-training.de
+ * @copyright (c) 2005-8 by RalfBecker-At-outdoor-training.de
  * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
  * @version $Id$
  */
 
-require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.bocal.inc.php');
-
 // types of messsages send by bocalupdate::send_update
 define('MSG_DELETED',0);
 define('MSG_MODIFIED',1);
@@ -39,7 +37,7 @@ define('MSG_DISINVITE',7);
  * All permanent debug messages of the calendar-code should done via the debug-message method of the bocal class !!!
  */
 
-class bocalupdate extends bocal
+class calendar_boupdate extends calendar_bo
 {
 	/**
 	 * name of method to debug or level of debug-messages:
@@ -60,11 +58,11 @@ class bocalupdate extends bocal
 	/**
 	 * Constructor
 	 */
-	function bocalupdate()
+	function __construct()
 	{
 		if ($this->debug > 0) $this->debug_message('bocalupdate::bocalupdate() started',True);
 
-		$this->bocal();	// calling the parent constructor
+		parent::__construct();	// calling the parent constructor
 
 		if ($this->debug > 0) $this->debug_message('bocalupdate::bocalupdate() finished',True);
 	}
diff --git a/calendar/inc/class.datasource_calendar.inc.php b/calendar/inc/class.calendar_datasource.inc.php
similarity index 63%
rename from calendar/inc/class.datasource_calendar.inc.php
rename to calendar/inc/class.calendar_datasource.inc.php
index 46428c68b8..9dc0352668 100644
--- a/calendar/inc/class.datasource_calendar.inc.php
+++ b/calendar/inc/class.calendar_datasource.inc.php
@@ -1,56 +1,50 @@
      *
-* --------------------------------------------                             *
-*  This program is free software; you can redistribute it and/or modify it *
-*  under the terms of the GNU General Public License as published by the   *
-*  Free Software Foundation; either version 2 of the License, or (at your  *
-*  option) any later version.                                              *
-\**************************************************************************/
-
-/* $Id$ */
+/**
+ * DataSource for the Calendar
+ *
+ * @link http://www.egroupware.org
+ * @package calendar
+ * @author RalfBecker-AT-outdoor-training.de
+ * @copyright (c) 2005-8 by RalfBecker-AT-outdoor-training.de
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @version $Id$
+ */
 
 include_once(EGW_INCLUDE_ROOT.'/projectmanager/inc/class.datasource.inc.php');
 
 /**
  * DataSource for the Calendar
- *
- * @package calendar
- * @author RalfBecker-AT-outdoor-training.de
- * @copyright (c) 2005 by RalfBecker-AT-outdoor-training.de
- * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
  */
-class datasource_calendar extends datasource
+class calendar_datasource extends datasource
 {
 	/**
 	 * Constructor
 	 */
-	function datasource_calendar()
+	function __construct()
 	{
 		$this->datasource('calendar');
-		
+
 		$this->valid = PM_PLANNED_START|PM_PLANNED_END|PM_PLANNED_TIME|PM_RESOURCES;
 	}
-	
+
 	/**
 	 * get an entry from the underlaying app (if not given) and convert it into a datasource array
-	 * 
+	 *
 	 * @param mixed $data_id id as used in the link-class for that app, or complete entry as array
 	 * @return array/boolean array with the data supported by that source or false on error (eg. not found, not availible)
 	 */
 	function get($data_id)
 	{
-		// we use $GLOBALS['bocal'] as an already running instance is availible there
-		if (!is_object($GLOBALS['bocal']))
+		// we use $cal as an already running instance is availible there
+		if (!is_object($GLOBALS['calendar_bo']))
 		{
-			include_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.bocal.inc.php');
-			$GLOBALS['bocal'] =& new bocal();
+			$GLOBALS['calendar_bo'] = new calendar_bo();
 		}
+		$cal = $GLOBALS['calendar_bo'];
+
 		if (!is_array($data_id))
 		{
-			if (!(int) $data_id || !($data = $GLOBALS['bocal']->read((int) $data_id)))
+			if (!(int) $data_id || !($data = $cal->read((int) $data_id)))
 			{
 				return false;
 			}
@@ -60,9 +54,9 @@ class datasource_calendar extends datasource
 			$data =& $data_id;
 		}
 		$ds = array(
-			'pe_title' => $GLOBALS['bocal']->link_title($data),
-			'pe_planned_start' => $GLOBALS['bocal']->date2ts($data['start']),
-			'pe_planned_end'   => $GLOBALS['bocal']->date2ts($data['end']),
+			'pe_title' => $cal->link_title($data),
+			'pe_planned_start' => $cal->date2ts($data['start']),
+			'pe_planned_end'   => $cal->date2ts($data['end']),
 			'pe_resources'     => array(),
 			'pe_details'       => $data['description'] ? nl2br($data['description']) : '',
 		);
@@ -74,14 +68,14 @@ class datasource_calendar extends datasource
 		{
 			foreach(array('start','end') as $name)
 			{
-				$$name = $GLOBALS['bocal']->date2array($ds['pe_planned_'.$name]);
+				$$name = $cal->date2array($ds['pe_planned_'.$name]);
 				${$name}['hour'] = 12;
 				${$name}['minute'] = ${$name}['second'] = 0;
 				unset(${$name}['raw']);
-				$$name = $GLOBALS['bocal']->date2ts($$name);
+				$$name = $cal->date2ts($$name);
 			}
 			$nights = round(($end - $start) / DAY_s);
-			
+
 			if (!is_array($this->pm_config))
 			{
 				$c =& CreateObject('phpgwapi.config','projectmanager');
@@ -103,7 +97,7 @@ class datasource_calendar extends datasource
 		$ds['pe_planned_time'] *= count($ds['pe_resources']);
 
 /*
-		// ToDO: this does not change automatically after the event is over, 
+		// ToDO: this does not change automatically after the event is over,
 		// maybe we need a flag for that in egw_pm_elements
 		if ($data['end']['raw'] <= time()+$GLOBALS['egw']->datetime->tz_offset)
 		{
diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php
index 1f679935d5..7e589fc421 100644
--- a/calendar/inc/class.calendar_groupdav.inc.php
+++ b/calendar/inc/class.calendar_groupdav.inc.php
@@ -11,8 +11,6 @@
  * @version $Id$
  */
 
-require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.bocalupdate.inc.php');
-
 /**
  * eGroupWare: GroupDAV access: calendar handler
  */
@@ -21,7 +19,7 @@ class calendar_groupdav extends groupdav_handler
 	/**
 	 * bo class of the application
 	 *
-	 * @var bocalupdate
+	 * @var calendar_boupdate
 	 */
 	var $bo;
 
@@ -49,7 +47,7 @@ class calendar_groupdav extends groupdav_handler
 	{
 		parent::__construct($app,$debug,$base_uri);
 
-		$this->bo =& new bocalupdate();
+		$this->bo =& new calendar_boupdate();
 	}
 
 	/**
diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php
new file mode 100644
index 0000000000..ba2703570d
--- /dev/null
+++ b/calendar/inc/class.calendar_ical.inc.php
@@ -0,0 +1,1546 @@
+
+ * @author Ralf Becker 
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package calendar
+ * @subpackage export
+ * @version $Id$
+ */
+
+require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/Horde/iCalendar.php';
+
+/**
+ * iCal import and export via Horde iCalendar classes
+ */
+class boical extends calendar_boupdate
+{
+	/**
+	 * @var array $supportedFields array containing the supported fields of the importing device
+	 */
+	var $supportedFields;
+
+	var $recur_days_1_0 = array(
+		MCAL_M_MONDAY    => 'MO',
+		MCAL_M_TUESDAY   => 'TU',
+		MCAL_M_WEDNESDAY => 'WE',
+		MCAL_M_THURSDAY  => 'TH',
+		MCAL_M_FRIDAY    => 'FR',
+		MCAL_M_SATURDAY  => 'SA',
+		MCAL_M_SUNDAY    => 'SU',
+	);
+
+	/**
+	 * @var array $status_egw2ical conversation of the participant status egw => ical
+	 */
+	var $status_egw2ical = array(
+		'U' => 'NEEDS-ACTION',
+		'A' => 'ACCEPTED',
+		'R' => 'DECLINED',
+		'T' => 'TENTATIVE',
+	);
+	/**
+	 * @var array conversation of the participant status ical => egw
+	 */
+	var $status_ical2egw = array(
+		'NEEDS-ACTION' => 'U',
+		'ACCEPTED'     => 'A',
+		'DECLINED'     => 'R',
+		'TENTATIVE'    => 'T',
+	);
+
+	/**
+	 * @var array $status_ical2egw conversation of the priority egw => ical
+	 */
+	var $priority_egw2ical = array(
+		0 => 0,		// undefined
+		1 => 9,		// low
+		2 => 5,		// normal
+		3 => 1,		// high
+	);
+	/**
+	 * @var array $status_ical2egw conversation of the priority ical => egw
+	 */
+	var $priority_ical2egw = array(
+		0 => 0,		// undefined
+		9 => 1,	8 => 1, 7 => 1, 6 => 1,	// low
+		5 => 2,		// normal
+		4 => 3, 2 => 3, 3 => 3, 1 => 3,	// high
+	);
+
+	/**
+	 * @var array $recur_egw2ical_2_0 converstaion of egw recur-type => ical FREQ
+	 */
+	var $recur_egw2ical_2_0 = array(
+		MCAL_RECUR_DAILY        => 'DAILY',
+		MCAL_RECUR_WEEKLY       => 'WEEKLY',
+		MCAL_RECUR_MONTHLY_MDAY => 'MONTHLY',	// BYMONHTDAY={1..31}
+		MCAL_RECUR_MONTHLY_WDAY => 'MONTHLY',	// BYDAY={1..5}{MO..SO}
+		MCAL_RECUR_YEARLY       => 'YEARLY',
+	);
+
+	/**
+	 * @var array $recur_egw2ical_1_0 converstaion of egw recur-type => ical FREQ
+	 */
+	var $recur_egw2ical_1_0 = array(
+		MCAL_RECUR_DAILY        => 'D',
+		MCAL_RECUR_WEEKLY       => 'W',
+		MCAL_RECUR_MONTHLY_MDAY => 'MD',	// BYMONHTDAY={1..31}
+		MCAL_RECUR_MONTHLY_WDAY => 'MP',	// BYDAY={1..5}{MO..SO}
+		MCAL_RECUR_YEARLY       => 'YM',
+	);
+
+	/**
+	 * manufacturer and name of the sync-client
+	 *
+	 * @var string
+	 */
+	var $productManufacturer = 'file';
+	var $productName = '';
+
+	/**
+	 * Exports one calendar event to an iCalendar item
+	 *
+	 * @param int/array $events (array of) cal_id or array of the events
+	 * @param string $version='1.0' could be '2.0' too
+	 * @param string $method='PUBLISH'
+	 * @param boolean $force_own_uid=true ignore the stored and maybe from the client transfered uid and generate a new one
+	 * RalfBecker: GroupDAV/CalDAV requires to switch that non RFC conform behavior off, dont know if SyncML still needs it
+	 * @return string/boolean string with vCal or false on error (eg. no permission to read the event)
+	 */
+	function &exportVCal($events,$version='1.0', $method='PUBLISH',$force_own_uid=true)
+	{
+		$egwSupportedFields = array(
+			'CLASS'			=> array('dbName' => 'public'),
+			'SUMMARY'		=> array('dbName' => 'title'),
+			'DESCRIPTION'	=> array('dbName' => 'description'),
+			'LOCATION'		=> array('dbName' => 'location'),
+			'DTSTART'		=> array('dbName' => 'start'),
+			'DTEND'			=> array('dbName' => 'end'),
+			'ORGANIZER'		=> array('dbName' => 'owner'),
+			'ATTENDEE'		=> array('dbName' => 'participants'),
+			'RRULE'			=> array('dbName' => 'recur_type'),
+			'EXDATE'		=> array('dbName' => 'recur_exception'),
+				'PRIORITY'		=> array('dbName' => 'priority'),
+				'TRANSP'		=> array('dbName' => 'non_blocking'),
+			'CATEGORIES'	=> array('dbName' => 'category'),
+		);
+		if(!is_array($this->supportedFields))
+		{
+			$this->setSupportedFields();
+		}
+
+		if($this->productManufacturer == '' )
+		{	// syncevolution is broken
+			$version = "2.0";
+		}
+
+		$palm_enddate_workaround=False;
+		if($this->productManufacturer == 'Synthesis AG'
+			&& strpos($this->productName, "PalmOS") )
+		{
+			// This workaround adds 1 day to the recur_enddate if it exists, to fix a palm bug
+			$palm_enddate_workaround=True;
+		}
+
+		$vcal = &new Horde_iCalendar;
+		$vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
+			strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
+		$vcal->setAttribute('VERSION',$version);
+		$vcal->setAttribute('METHOD',$method);
+
+		if (!is_array($events)) $events = array($events);
+
+		foreach($events as $event)
+		{
+			if (!is_array($event) && !($event = $this->read($event,null,false,'server')))	// server = timestamp in server-time(!)
+			{
+				return false;	// no permission to read $cal_id
+			}
+			//_debug_array($event);
+
+			// correct daylight saving time
+			/* causes times wrong by one hour, if exporting events with DST different from the current date,
+			which this fix is suppost to fix. Maybe the problem has been fixed in the horde code too.
+			$currentDST = date('I', mktime());
+			$eventDST = date('I', $event['start']);
+			$DSTCorrection = ($currentDST - $eventDST) * 3600;
+			$event['start']	= $event['start'] + $DSTCorrection;
+			$event['end']	= $event['end'] + $DSTCorrection;
+			*/
+			$eventGUID = $GLOBALS['egw']->common->generate_uid('calendar',$event['id']);
+
+			$vevent = Horde_iCalendar::newComponent('VEVENT',$vcal);
+			$parameters = $attributes = array();
+
+			foreach($egwSupportedFields as $icalFieldName => $egwFieldInfo)
+			{
+				if($this->supportedFields[$egwFieldInfo['dbName']])
+				{
+					switch($icalFieldName)
+					{
+						case 'ATTENDEE':
+							//if (count($event['participants']) == 1 && isset($event['participants'][$this->user])) break;
+							foreach((array)$event['participants'] as $uid => $status)
+							{
+								if (!($info = $this->resource_info($uid))) continue;
+								// RB: MAILTO href contains only the email-address, NO cn!
+								$attributes['ATTENDEE'][]	= $info['email'] ? 'MAILTO:'.$info['email'] : '';
+								// ROLE={CHAIR|REQ-PARTICIPANT|OPT-PARTICIPANT|NON-PARTICIPANT} NOT used by eGW atm.
+								$role = $uid == $event['owner'] ? 'CHAIR' : 'REQ-PARTICIPANT';
+								// RSVP={TRUE|FALSE}	// resonse expected, not set in eGW => status=U
+								$rsvp = $status == 'U' ? 'TRUE' : 'FALSE';
+								// PARTSTAT={NEEDS-ACTION|ACCEPTED|DECLINED|TENTATIVE|DELEGATED|COMPLETED|IN-PROGRESS} everything from delegated is NOT used by eGW atm.
+								$status = $this->status_egw2ical[$status];
+								// CUTYPE={INDIVIDUAL|GROUP|RESOURCE|ROOM|UNKNOWN}
+								switch ($info['type'])
+								{
+									case 'g':
+										$cutype = 'GROUP';
+										break;
+									case 'r':
+										$cutype = 'RESOURCE';
+										break;
+									case 'u':	// account
+									case 'c':	// contact
+									case 'e':	// email address
+										$cutype = 'INDIVIDUAL';
+										break;
+									default:
+										$cutype = 'UNKNOWN';
+										break;
+								};
+								$parameters['ATTENDEE'][] = array(
+									'CN'       => $info['cn'] ? $info['cn'] : $info['name'],
+									'ROLE'     => $role,
+									'PARTSTAT' => $status,
+									'CUTYPE'   => $cutype,
+									'RSVP'     => $rsvp,
+								)+($info['type'] != 'e' ? array('X-EGROUPWARE-UID' => $uid) : array());
+							}
+							break;
+
+            			case 'CLASS':
+            				$attributes['CLASS'] = $event['public'] ? 'PUBLIC' : 'PRIVATE';
+    	    				break;
+
+        				case 'ORGANIZER':	// according to iCalendar standard, ORGANIZER not used for events in the own calendar
+        					if ($event['owner'] != $this->user)
+        					//if (!isset($event['participants'][$event['owner']]) || count($event['participants']) > 1)
+        					{
+								$mailtoOrganizer = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email');
+								$attributes['ORGANIZER'] = $mailtoOrganizer ? 'MAILTO:'.$mailtoOrganizer : '';
+								$parameters['ORGANIZER']['CN'] = trim($GLOBALS['egw']->accounts->id2name($event['owner'],'account_firstname').' '.
+									$GLOBALS['egw']->accounts->id2name($event['owner'],'account_lastname'));
+        					}
+							break;
+
+						case 'DTEND':
+							if(date('H:i:s',$event['end']) == '23:59:59') $event['end']++;
+							if(date('H:i:s',$event['end']) == '23:59:00') $event['end']+=60; // needed by old eGW whole-day events
+							$attributes[$icalFieldName]	= $event['end'];
+							break;
+
+						case 'RRULE':
+							if ($event['recur_type'] == MCAL_RECUR_NONE) break;		// no recuring event
+							if ($version == '1.0') {
+								$interval = ($event['recur_interval'] > 1) ? $event['recur_interval'] : 1;
+								$rrule = array('FREQ' => $this->recur_egw2ical_1_0[$event['recur_type']].$interval);
+								switch ($event['recur_type'])
+								{
+            								case MCAL_RECUR_WEEKLY:
+            									$days = array();
+            									foreach($this->recur_days_1_0 as $id => $day)
+            									{
+            										if ($event['recur_data'] & $id) $days[] = strtoupper(substr($day,0,2));
+												}
+	            								$rrule['BYDAY'] = implode(' ',$days);
+	            								$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
+	            								break;
+
+        	    							case MCAL_RECUR_MONTHLY_MDAY:	// date of the month: BYMONTDAY={1..31}
+            									break;
+
+             								case MCAL_RECUR_MONTHLY_WDAY:	// weekday of the month: BDAY={1..5}{MO..SO}
+             									$rrule['BYDAY'] = (1 + (int) ((date('d',$event['start'])-1) / 7)).'+ '.
+	             									strtoupper(substr(date('l',$event['start']),0,2));
+	            								$rrule['FREQ'] = $rrule['FREQ'].' '.$rrule['BYDAY'];
+										break;
+								}
+
+								if ($event['recur_enddate'])
+								{
+									$recur_enddate = (int)$event['recur_enddate'];
+									if ($palm_enddate_workaround)
+									{
+										$recur_enddate += 86400;
+									}
+									# append T and the Endtime, since the RRULE seems not to be understood by the client without it
+									$rrule['UNTIL'] = date('Ymd',$recur_enddate).'T'.date('His',($event['end']?$event['end']:$event['start'])) ;
+								}
+								else
+								{
+									$rrule['UNTIL'] = '#0';
+								}
+
+								$attributes['RRULE'] = $rrule['FREQ'].' '.$rrule['UNTIL'];
+							} else {
+								$rrule = array('FREQ' => $this->recur_egw2ical_2_0[$event['recur_type']]);
+								switch ($event['recur_type'])
+								{
+            								case MCAL_RECUR_WEEKLY:
+            									$days = array();
+            									foreach($this->recur_days as $id => $day)
+            									{
+            										if ($event['recur_data'] & $id) $days[] = strtoupper(substr($day,0,2));
+										}
+	            								$rrule['BYDAY'] = implode(',',$days);
+	            								break;
+
+        	    							case MCAL_RECUR_MONTHLY_MDAY:	// date of the month: BYMONTDAY={1..31}
+            									$rrule['BYMONTHDAY'] = (int) date('d',$event['start']);
+            									break;
+
+             								case MCAL_RECUR_MONTHLY_WDAY:	// weekday of the month: BDAY={1..5}{MO..SO}
+             									$rrule['BYDAY'] = (1 + (int) ((date('d',$event['start'])-1) / 7)).
+	             									strtoupper(substr(date('l',$event['start']),0,2));
+										break;
+								}
+								if ($event['recur_interval'] > 1) $rrule['INTERVAL'] = $event['recur_interval'];
+								if ($event['recur_enddate']) $rrule['UNTIL'] = date('Ymd',$event['recur_enddate']);	// only day is set in eGW
+
+								// no idea how to get the Horde parser to produce a standard conformant
+								// RRULE:FREQ=... (note the double colon after RRULE, we cant use the $parameter array)
+								// so we create one value manual ;-)
+								foreach($rrule as $name => $value)
+								{
+									$attributes['RRULE'][] = $name . '=' . $value;
+								}
+								$attributes['RRULE'] = implode(';',$attributes['RRULE']);
+							}
+							break;
+
+						case 'EXDATE':
+							if ($event['recur_exception'])
+							{
+								$days = array();
+								foreach($event['recur_exception'] as $day)
+								{
+									$days[] = date('Ymd',$day);
+								}
+								$attributes['EXDATE'] = implode(',',$days);
+								$parameters['EXDATE']['VALUE'] = 'DATE';
+							}
+							break;
+
+						case 'PRIORITY':
+ 							$attributes['PRIORITY'] = (int) $this->priority_egw2ical[$event['priority']];
+ 							break;
+
+ 						case 'TRANSP':
+							if ($version == '1.0') {
+								$attributes['TRANSP'] = $event['non_blocking'] ? 1 : 0;
+							} else {
+								$attributes['TRANSP'] = $event['non_blocking'] ? 'TRANSPARENT' : 'OPAQUE';
+							}
+							break;
+
+						case 'CATEGORIES':
+							if ($event['category'])
+							{
+								$attributes['CATEGORIES'] = implode(',',$this->get_categories($event['category']));
+							}
+							break;
+
+						default:
+							if ($event[$egwFieldInfo['dbName']])	// dont write empty fields
+							{
+								$attributes[$icalFieldName]	= $event[$egwFieldInfo['dbName']];
+							}
+							break;
+					}
+				}
+			}
+
+			if(strtolower($this->productManufacturer) == 'nokia') {
+				if($event['special'] == '1') {
+					$attributes['X-EPOCAGENDAENTRYTYPE'] = 'ANNIVERSARY';
+					$attributes['DTEND'] = $attributes['DTSTART'];
+				} else {
+					$attributes['X-EPOCAGENDAENTRYTYPE'] = 'APPOINTMENT';
+				}
+			}
+
+			$modified = $GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'modify');
+			$created = $GLOBALS['egw']->contenthistory->getTSforAction($eventGUID,'add');
+			if (!$created && !$modified) $created = $event['modified'];
+			if ($created) $attributes['CREATED'] = $created;
+			if (!$modified) $modified = $event['modified'];
+			if ($modified) $attributes['LAST-MODIFIED'] = $modified;
+
+			foreach($event['alarm'] as $alarmID => $alarmData)
+			{
+				if ($version == '1.0')
+				{
+					$attributes['DALARM'] = $vcal->_exportDateTime($alarmData['time']);
+					$attributes['AALARM'] = $vcal->_exportDateTime($alarmData['time']);
+					// lets take only the first alarm
+					break;
+				}
+				else
+				{
+					// VCalendar 2.0 / RFC 2445
+
+					// skip over alarms that don't have the minimum required info
+					if (!$alarmData['offset'] && !$alarmData['time'])
+					{
+						error_log("Couldn't add VALARM (no alarm time info)");
+						continue;
+					}
+
+					// RFC requires DESCRIPTION for DISPLAY
+					if (!$event['title'] && !$event['description'])
+					{
+						error_log("Couldn't add VALARM (no description)");
+						continue;
+					}
+
+					$valarm = Horde_iCalendar::newComponent('VALARM',$vevent);
+					if ($alarmData['offset'])
+					{
+						$valarm->setAttribute('TRIGGER', -$alarmData['offset'],
+								array('VALUE' => 'DURATION', 'RELATED' => 'START'));
+					}
+					else
+					{
+						$valarm->setAttribute('TRIGGER', $alarmData['time'],
+								array('VALUE' => 'DATE-TIME'));
+					}
+
+					$valarm->setAttribute('ACTION','DISPLAY');
+					$valarm->setAttribute('DESCRIPTION',$event['title'] ? $event['title'] : $event['description']);
+					$vevent->addComponent($valarm);
+				}
+			}
+
+			$attributes['UID'] = $force_own_uid ? $eventGUID : $event['uid'];
+
+			foreach($attributes as $key => $value)
+			{
+				foreach(is_array($value) ? $value : array($value) as $valueID => $valueData)
+				{
+					$valueData = $GLOBALS['egw']->translation->convert($valueData,$GLOBALS['egw']->translation->charset(),'UTF-8');
+					$paramData = (array) $GLOBALS['egw']->translation->convert(is_array($value) ? $parameters[$key][$valueID] : $parameters[$key],
+						$GLOBALS['egw']->translation->charset(),'UTF-8');
+					//echo "$key:$valueID: value=$valueData, param=".print_r($paramDate,true)."\n";
+					$vevent->setAttribute($key, $valueData, $paramData);
+					$options = array();
+					if($key != 'RRULE' && preg_match('/([\000-\012\015\016\020-\037\075])/',$valueData))
+					{
+						$options['ENCODING'] = 'QUOTED-PRINTABLE';
+					}
+					if(preg_match('/([\177-\377])/',$valueData))
+					{
+						$options['CHARSET'] = 'UTF-8';
+					}
+					$vevent->setParameter($key, $options);
+				}
+			}
+			$vcal->addComponent($vevent);
+		}
+		//_debug_array($vcal->exportvCalendar());
+
+		return $vcal->exportvCalendar();
+	}
+
+	/**
+	 * Import an iCal
+	 *
+	 * @param string $_vcalData
+	 * @param int $cal_id=-1 must be -1 for new entrys!
+	 * @param string $etag=null if an etag is given, it has to match the current etag or the import will fail
+	 * @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag
+	 */
+	function importVCal($_vcalData, $cal_id=-1,$etag=null)
+	{
+		// our (patched) horde classes, do NOT unfold folded lines, which causes a lot trouble in the import
+		$_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData);
+
+		$vcal = &new Horde_iCalendar;
+		if(!$vcal->parsevCalendar($_vcalData))
+		{
+			return FALSE;
+		}
+
+		$version = $vcal->getAttribute('VERSION');
+
+		if(!is_array($this->supportedFields))
+		{
+			$this->setSupportedFields();
+		}
+		//echo "supportedFields="; _debug_array($this->supportedFields);
+
+		$syncevo_enddate_fix = False;
+		if( $this->productManufacturer == '' && $this->productName == '' )
+		{
+			// syncevolution needs an adjusted recur_enddate
+			$syncevo_enddate_fix = True;
+		}
+
+		$Ok = false;	// returning false, if file contains no components
+		foreach($vcal->getComponents() as $component)
+		{
+			if(is_a($component, 'Horde_iCalendar_vevent'))
+			{
+				$supportedFields = $this->supportedFields;
+				#$event = array('participants' => array());
+				$event		= array();
+				$alarms		= array();
+				$vcardData	= array(
+					'recur_type'		=> MCAL_RECUR_NONE,
+					'recur_exception'	=> array(),
+				);
+
+				// lets see what we can get from the vcard
+				foreach($component->_attributes as $attributes)
+				{
+					switch($attributes['name'])
+					{
+						case 'AALARM':
+						case 'DALARM':
+							if (preg_match('/.*Z$/',$attributes['value'],$matches)) {
+								$alarmTime = $vcal->_parseDateTime($attributes['value']);
+								$alarms[$alarmTime] = array(
+									'time' => $alarmTime
+								);
+							} elseif (preg_match('/(........T......);;(\d*);$/',$attributes['value'],$matches)) {
+								//error_log(print_r($matches,true));
+								$alarmTime = $vcal->_parseDateTime($matches[1]);
+								$alarms[$alarmTime] = array(
+									'time' => $alarmTime
+								);
+							} elseif (preg_match('/(........T......Z);;(\d*);$/',$attributes['value'],$matches)) {
+								//error_log(print_r($matches,true));
+								$alarmTime = $vcal->_parseDateTime($matches[1]);
+								$alarms[$alarmTime] = array(
+									'time' => $alarmTime
+								);
+							} elseif (preg_match('/(........T......)$/',$attributes['value'],$matches)) {
+								$alarmTime = $vcal->_parseDateTime($attributes['value']);
+								$alarms[$alarmTime] = array(
+									'time' => $alarmTime
+								);
+							}
+							break;
+						case 'CLASS':
+							$vcardData['public']		= (int)(strtolower($attributes['value']) == 'public');
+							break;
+						case 'DESCRIPTION':
+							$vcardData['description']	= $attributes['value'];
+							break;
+						case 'DTEND':
+							$dtend_ts = is_numeric($attributes['value']) ? $attributes['value'] : $this->date2ts($attributes['value']);
+							if(date('H:i:s',$dtend_ts) == '00:00:00') {
+								$dtend_ts -= 60;
+							}
+							$vcardData['end']		= $dtend_ts;
+							break;
+						case 'DTSTART':
+							$vcardData['start']		= $attributes['value'];
+							break;
+						case 'LOCATION':
+							$vcardData['location']	= $attributes['value'];
+							break;
+						case 'RRULE':
+							$recurence = $attributes['value'];
+							$type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence[0];
+							// vCard 2.0 values for all types
+							if (preg_match('/UNTIL=([0-9T]+)/',$recurence,$matches))
+							{
+								$vcardData['recur_enddate'] = $vcal->_parseDateTime($matches[1]);
+							}
+							elseif (preg_match('/COUNT=([0-9]+)/',$recurence,$matches))
+							{
+								$vcardData['recur_count'] = (int)$matches[1];
+							}
+							if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches))
+							{
+								// 1 is invalid,, egw uses 0 for interval
+								$vcardData['recur_interval'] = (int) $matches[1] != 0 ? (int) $matches[1] : 0;
+							}
+							if (!isset($vcardData['start']))	// it might not yet be set, because the RRULE is before it
+							{
+								$vcardData['start'] = self::_get_attribute($component->_attributes,'DTSTART');
+								$vcardData['end'] = self::_get_attribute($component->_attributes,'DTEND');
+							}
+							$vcardData['recur_data'] = 0;
+							switch($type)
+							{
+								case 'W':
+								case 'WEEKLY':
+									$days = array();
+									if(preg_match('/W(\d+) (.*) (.*)/',$recurence, $recurenceMatches))		// 1.0
+									{
+										$vcardData['recur_interval'] = $recurenceMatches[1];
+										$days = explode(' ',trim($recurenceMatches[2]));
+										if($recurenceMatches[3] != '#0')
+											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[3]);
+										$recur_days = $this->recur_days_1_0;
+									}
+									elseif (preg_match('/BYDAY=([^;: ]+)/',$recurence,$recurenceMatches))	// 2.0
+									{
+										$days = explode(',',$recurenceMatches[1]);
+										$recur_days = $this->recur_days;
+									}
+									else	// no day given, use the day of dtstart
+									{
+										$vcardData['recur_data'] |= 1 << (int)date('w',$vcardData['start']);
+										$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
+									}
+									if ($days)
+									{
+										foreach($recur_days as $id => $day)
+	            						{
+	            							if (in_array(strtoupper(substr($day,0,2)),$days))
+        									{
+        										$vcardData['recur_data'] |= $id;
+        									}
+        								}
+										$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
+									}
+
+									if (!empty($vcardData['recur_count']))
+									{
+										$vcardData['recur_enddate'] = mktime(0,0,0,
+											date('m',$vcardData['start']),
+											date('d',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)*7),
+											date('Y',$vcardData['start']));
+									}
+									break;
+
+								case 'D':	// 1.0
+									if(preg_match('/D(\d+) #(.\d)/', $recurence, $recurenceMatches)) {
+										$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[2] > 0 && $vcardData['end']) {
+											$vcardData['recur_enddate'] = mktime(
+												date('H', $vcardData['end']),
+												date('i', $vcardData['end']),
+												date('s', $vcardData['end']),
+												date('m', $vcardData['end']),
+												date('d', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval']),
+												date('Y', $vcardData['end'])
+											);
+										}
+									} elseif(preg_match('/D(\d+) (.*)/', $recurence, $recurenceMatches)) {
+										$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[2] != '#0') {
+											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
+										}
+									} else {
+										break;
+									}
+									// fall-through
+								case 'DAILY':	// 2.0
+									$vcardData['recur_type'] = MCAL_RECUR_DAILY;
+
+									if (!empty($vcardData['recur_count']))
+									{
+										$vcardData['recur_enddate'] = mktime(0,0,0,
+											date('m',$vcardData['start']),
+											date('d',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)),
+											date('Y',$vcardData['start']));
+									}
+									break;
+
+								case 'M':
+									if(preg_match('/MD(\d+) #(.\d)/', $recurence, $recurenceMatches)) {
+										$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY;
+										$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[2] > 0 && $vcardData['end']) {
+											$vcardData['recur_enddate'] = mktime(
+												date('H', $vcardData['end']),
+												date('i', $vcardData['end']),
+												date('s', $vcardData['end']),
+												date('m', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval']),
+												date('d', $vcardData['end']),
+												date('Y', $vcardData['end'])
+											);
+										}
+									} elseif(preg_match('/MD(\d+) (.*)/',$recurence, $recurenceMatches)) {
+										$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY;
+										if($recurenceMatches[1] > 1)
+											$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[2] != '#0')
+											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
+									} elseif(preg_match('/MP(\d+) (.*) (.*) (.*)/',$recurence, $recurenceMatches)) {
+										$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_WDAY;
+										if($recurenceMatches[1] > 1)
+											$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[4] != '#0')
+											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[4]);
+									}
+									break;
+								case 'MONTHLY':
+									$vcardData['recur_type'] = strpos($recurence,'BYDAY') !== false ?
+										MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY;
+
+									if (!empty($vcardData['recur_count']))
+									{
+										$vcardData['recur_enddate'] = mktime(0,0,0,
+											date('m',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)),
+											date('d',$vcardData['start']),
+											date('Y',$vcardData['start']));
+									}
+									break;
+
+								case 'Y':		// 1.0
+									if(preg_match('/YM(\d+) #(.\d)/', $recurence, $recurenceMatches)) {
+										$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[2] > 0 && $vcardData['end']) {
+											$vcardData['recur_enddate'] = mktime(
+												date('H', $vcardData['end']),
+												date('i', $vcardData['end']),
+												date('s', $vcardData['end']),
+												date('m', $vcardData['end']),
+												date('d', $vcardData['end']),
+												date('Y', $vcardData['end']) + ($recurenceMatches[2] * $vcardData['recur_interval'])
+											);
+										}
+									} elseif(preg_match('/YM(\d+) (.*)/',$recurence, $recurenceMatches)) {
+										$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[2] != '#0') {
+											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
+										}
+									} else {
+										break;
+									}
+									// fall-through
+								case 'YEARLY':	// 2.0
+									$vcardData['recur_type'] = MCAL_RECUR_YEARLY;
+
+									if (!empty($vcardData['recur_count']))
+									{
+										$vcardData['recur_enddate'] = mktime(0,0,0,
+											date('m',$vcardData['start']),
+											date('d',$vcardData['start']),
+											date('Y',$vcardData['start']) + ($vcardData['recur_interval']*($vcardData['recur_count']-1)));
+									}
+									break;
+							}
+							if( $syncevo_enddate_fix && $vcardData['recur_enddate'] )
+							{
+								// Does syncevolution need to adjust recur_enddate
+								$vcardData['recur_enddate'] = (int)$vcardData['recur_enddate'] + 86400;
+							}
+							break;
+						case 'EXDATE':
+							$vcardData['recur_exception'] = array_merge($vcardData['recur_exception'],$attributes['value']);
+							break;
+						case 'SUMMARY':
+							$vcardData['title']		= $attributes['value'];
+							break;
+						case 'UID':
+							$event['uid'] = $vcardData['uid'] = $attributes['value'];
+							if ($cal_id <= 0 && !empty($vcardData['uid']) && ($uid_event = $this->read($vcardData['uid'])))
+							{
+								$cal_id = $event['id'] = $uid_event['id'];
+								unset($uid_event);
+							}
+							break;
+ 						case 'TRANSP':
+ 							if($version == '1.0') {
+ 								$vcardData['non_blocking'] = $attributes['value'] == 1;
+ 							} else {
+								$vcardData['non_blocking'] = $attributes['value'] == 'TRANSPARENT';
+							}
+							break;
+						case 'PRIORITY':
+ 							$vcardData['priority'] = (int) $this->priority_ical2egw[$attributes['value']];
+ 							break;
+ 						case 'CATEGORIES':
+ 							if ($attributes['value'])
+ 							{
+ 								$vcardData['category'] = $this->find_or_add_categories(explode(',',$attributes['value']));
+ 							}
+							else
+							{
+ 								$vcardData['category'] = array();
+							}
+ 							break;
+ 						case 'ATTENDEE':
+ 							if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) ||
+ 								preg_match('/<([@.a-z0-9_-]+)>/i',$attributes['value'],$matches))
+								{
+									$email = $matches[1];
+								}
+								elseif(strpos($attributes['value'],'@') !== false)
+								{
+									$email = $attributes['value'];
+								}
+ 							if (($uid = $attributes['params']['X-EGROUPWARE-UID']) &&
+ 								($info = $this->resource_info($uid)) && $info['email'] == $email)
+ 							{
+ 								// we use the (checked) X-EGROUPWARE-UID
+ 							}
+ 							/*elseif($attributes['params']['CUTYPE'] == 'RESOURCE')
+ 							{
+
+ 							}*/
+	 						elseif($attributes['value'] == 'Unknown')
+ 							{
+ 								$uid = $GLOBALS['egw_info']['user']['account_id'];
+ 							}
+ 							elseif (($uid = $GLOBALS['egw']->accounts->name2id($email,'account_email')))
+ 							{
+ 								// we use the account we found
+ 							}
+							elseif ((list($data) = ExecMethod2('addressbook.addressbook_bo.search',array(
+								'email' => $email,
+								'email_home' => $email,
+							),true,'','','',false,'OR')))
+							{
+								$uid = 'c'.$data['id'];
+							}
+ 							else
+ 							{
+ 								$uid = 'e'.($attributes['params']['CN'] ? $attributes['params']['CN'].' <'.$email.'>' : $email);
+ 							}
+ 							$event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ?
+ 									$this->status_ical2egw[strtoupper($attributes['params']['PARTSTAT'])] :
+ 									($uid == $event['owner'] ? 'A' : 'U');
+ 							break;
+ 						case 'ORGANIZER':	// will be written direct to the event
+ 							if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
+ 								($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email')))
+ 							{
+ 								$event['owner'] = $uid;
+ 							}
+ 							break;
+ 						case 'CREATED':		// will be written direct to the event
+ 							if ($event['modified']) break;
+ 							// fall through
+ 						case 'LAST-MODIFIED':	// will be written direct to the event
+							$event['modified'] = $attributes['value'];
+							break;
+					}
+				}
+
+				// check if the entry is a birthday
+				// this field is only set from NOKIA clients
+				$agendaEntryType = $component->getAttribute('X-EPOCAGENDAENTRYTYPE');
+				if (!is_a($agendaEntryType, 'PEAR_Error')) {
+					if(strtolower($agendaEntryType) == 'anniversary') {
+						$event['special'] = '1';
+						// make it a whole day event for eGW
+						$vcardData['end'] = $vcardData['start'] + 86399;
+					}
+				}
+
+				if(!empty($vcardData['recur_enddate']))
+				{
+					// reset recure_enddate to 00:00:00 on the last day
+					$vcardData['recur_enddate'] = mktime(0, 0, 0,
+						date('m',$vcardData['recur_enddate']),
+						date('d',$vcardData['recur_enddate']),
+						date('Y',$vcardData['recur_enddate'])
+					);
+				}
+				//echo "event=";_debug_array($vcardData);
+
+				// now that we know what the vard provides, we merge that data with the information we have about the device
+				$event['priority']		= 2;
+				if($cal_id > 0)
+				{
+					$event['id'] = $cal_id;
+				}
+				while(($fieldName = array_shift($supportedFields)))
+				{
+					switch($fieldName)
+					{
+						case 'alarms':
+							// not handled here
+							break;
+						case 'recur_type':
+							$event['recur_type'] = $vcardData['recur_type'];
+							if ($event['recur_type'] != MCAL_RECUR_NONE)
+							{
+								foreach(array('recur_interval','recur_enddate','recur_data','recur_exception') as $r)
+								{
+									if(isset($vcardData[$r]))
+									{
+										$event[$r] = $vcardData[$r];
+									}
+								}
+							}
+							unset($supportedFields['recur_type']);
+							unset($supportedFields['recur_interval']);
+							unset($supportedFields['recur_enddate']);
+							unset($supportedFields['recur_data']);
+							break;
+						default:
+							if (isset($vcardData[$fieldName]))
+							{
+								$event[$fieldName] = $vcardData[$fieldName];
+							}
+							unset($supportedFields[$fieldName]);
+							break;
+					}
+				}
+
+				// add ourself to new events as participant
+ 				if($cal_id == -1 && !isset($this->supportedFields['participants']))
+ 				{
+					$event['participants'] = array($GLOBALS['egw_info']['user']['account_id'] => 'A');
+ 				}
+
+				// If this is an updated meeting, and the client doesn't support
+				// participants, add them back
+				if( $cal_id > 0 && !isset($this->supportedFields['participants']))
+				{
+					if (($egw_event = $this->read($cal_id)))
+					{
+						$event['participants'] = $egw_event['participants'];
+						$event['participant_types'] = $egw_event['participant_types'];
+					}
+				}
+
+				// Check for resources, and don't remove them
+				if( $cal_id > 0 )
+				{
+					// for each existing participant:
+					if (($egw_event = $this->read($cal_id)))
+					{
+						foreach( $egw_event['participants'] as $uid => $status )
+						{
+							// Is it a resource and not longer present in the event?
+							if ( $uid[0] == 'r' && !isset($event['participants'][$uid]) )
+							{
+								// Add it back in
+								$event['participants'][$uid] = $event['participant_types']['r'][substr($uid,1)] = $status;
+							}
+						}
+					}
+				}
+
+				// check if iCal changes the organizer, which is not allowed
+				if ($cal_id > 0 && ($egw_event = $this->read($cal_id)) && $event['owner'] != $egw_event['owner'])
+				{
+					$event['owner'] = $egw_event['owner'];	// set it back to the original owner
+				}
+
+				#error_log('ALARMS');
+				#error_log(print_r($event, true));
+
+				// if an etag is given, include it in the update
+				if (!is_null($etag))
+				{
+					$event['etag'] = $etag;
+				}
+				if (!($Ok = $this->update($event, TRUE)))
+				{
+					// check if current user is an attendee and tried to change his status
+					if ($Ok === false && $cal_id && ($egw_event = $this->read($cal_id)) && isset($egw_event['participants'][$this->user]) &&
+						$egw_event['participants'][$this->user] !== $event['participants'][$this->user])
+					{
+						$this->set_status($egw_event,$this->user,
+							$status = $event['participants'][$this->user] ? $event['participants'][$this->user] : 'R');
+
+						$Ok = $cal_id;
+						continue;
+					}
+					break;	// stop with the first error
+				}
+				else
+				{
+					$eventID =& $Ok;
+
+					// handle the alarms
+					foreach ($component->getComponents() as $valarm)
+					{
+						if (is_a($valarm, 'Horde_iCalendar_valarm'))
+						{
+							$this->valarm2egw($alarms,$valarm);
+						}
+					}
+
+					if(count($alarms) > 0 || (isset($this->supportedFields['alarms'])  && count($alarms) == 0))
+					{
+						// delete the old alarms
+						$updatedEvent = $this->read($eventID);
+						foreach($updatedEvent['alarm'] as $alarmID => $alarmData)
+						{
+							$this->delete_alarm($alarmID);
+						}
+					}
+
+					foreach($alarms as $alarm)
+					{
+						$alarm['offset'] = $event['start'] - $alarm['time'];
+						$alarm['owner'] = $GLOBALS['egw_info']['user']['account_id'];
+						$this->save_alarm($eventID, $alarm);
+					}
+				}
+			}
+		}
+		return $Ok;
+	}
+
+	/**
+	 * get the value of an attribute by its name
+	 *
+	 * @param array $attributes
+	 * @param string $name eg. 'DTSTART'
+	 * @param string $what='value'
+	 * @return mixed
+	 */
+	static function _get_attribute($components,$name,$what='value')
+	{
+		foreach($components as $attribute)
+		{
+			if ($attribute['name'] == $name)
+			{
+				return !$what ? $attribute : $attribute[$what];
+			}
+		}
+		return false;
+	}
+
+	static function valarm2egw(&$alarms, &$valarm)
+	{
+		$count = 0;
+		foreach($valarm->_attributes as $vattr)
+		{
+			switch($vattr['name'])
+			{
+				case 'TRIGGER':
+					$vtype = (isset($vattr['params']['VALUE']))
+						? $vattr['params']['VALUE'] : 'DURATION'; //default type
+					switch ($vtype)
+					{
+						case 'DURATION':
+							if (isset($vattr['params']['RELATED'])
+								&& $vattr['params']['RELATED'] != 'START')
+							{
+								error_log("Unsupported VALARM offset anchor ".$vattr['params']['RELATED']);
+							}
+							else
+							{
+								$alarms[] = array('offset' => -$vattr['value']);
+								$count++;
+							}
+							break;
+						case 'DATE-TIME':
+							$alarms[] = array('time' => $vattr['value']);
+							$count++;
+							break;
+						default:
+							// we should also do ;RELATED=START|END
+							error_log('VALARM/TRIGGER: unsupported value type:' . $vtype);
+					}
+					break;
+				// case 'ACTION':
+				// 	break;
+				// case 'DISPLAY':
+				// 	break;
+
+				default:
+					error_log('VALARM field:' .$vattr['name'] .':' . print_r($vattrval,true) . ' HAS NO CONVERSION YET');
+			}
+		}
+		return $count;
+	}
+
+	function setSupportedFields($_productManufacturer='file', $_productName='')
+	{
+		// save them vor later use
+		$this->productManufacturer = $_productManufacturer;
+		$this->productName = $_productName;
+
+		$defaultFields['minimal'] = array(
+			'public'			=> 'public',
+			'description'		=> 'description',
+			'end'				=> 'end',
+			'start'				=> 'start',
+			'location'			=> 'location',
+			'recur_type'		=> 'recur_type',
+			'recur_interval'	=> 'recur_interval',
+			'recur_data'		=> 'recur_data',
+			'recur_enddate'		=> 'recur_enddate',
+			'title'				=> 'title',
+			'alarms'			=> 'alarms',
+		);
+
+		$defaultFields['basic'] = $defaultFields['minimal'] + array(
+			'recur_exception'	=> 'recur_exception',
+			'priority'			=> 'priority',
+		);
+
+		$defaultFields['nexthaus'] = $defaultFields['basic'] + array(
+			'participants'		=> 'participants',
+		);
+
+		$defaultFields['synthesis'] = $defaultFields['basic'] + array(
+			'non_blocking'		=> 'non_blocking',
+			'category'			=> 'category',
+		);
+
+		$defaultFields['evolution'] = $defaultFields['basic'] + array(
+			'participants'		=> 'participants',
+			'owner'				=> 'owner',
+			'category'			=> 'category',
+		);
+
+		$defaultFields['full'] = $defaultFields['basic'] + array(
+			'participants'		=> 'participants',
+			'owner'				=> 'owner',
+			'category'			=> 'category',
+			'non_blocking'		=> 'non_blocking',
+		);
+
+
+		switch(strtolower($_productManufacturer))
+		{
+			case 'nexthaus corporation':
+			case 'nexthaus corp':
+				switch(strtolower($_productName))
+				{
+					default:
+						$this->supportedFields = $defaultFields['nexthaus'];
+						break;
+				}
+				break;
+
+			// multisync does not provide anymore information then the manufacturer
+			// we suppose multisync with evolution
+			case 'the multisync project':
+				switch(strtolower($_productName))
+				{
+					default:
+						$this->supportedFields = $defaultFields['basic'];
+						break;
+				}
+				break;
+
+			case 'nokia':
+				switch(strtolower($_productName))
+				{
+					case 'e61':
+						$this->supportedFields = $defaultFields['minimal'];
+						break;
+					default:
+						error_log("Unknown Nokia phone '$_productName', assuming E61");
+						$this->supportedFields = $defaultFields['minimal'];
+						break;
+				}
+				break;
+
+			case 'sonyericsson':
+			case 'sony ericsson':
+				switch(strtolower($_productName))
+				{
+					case 'd750i':
+					case 'p910i':
+						$this->supportedFields = $defaultFields['basic'];
+						break;
+					default:
+						error_log("Unknown Sony Ericsson phone '$_productName' assuming d750i");
+						$this->supportedFields = $defaultFields['basic'];
+						break;
+				}
+				break;
+
+			case 'synthesis ag':
+				switch(strtolower($_productName))
+				{
+					case 'sysync client pocketpc std':
+					case 'sysync client pocketpc pro':
+						$this->supportedFields = $defaultFields['full'];
+						break;
+					default:
+						$this->supportedFields = $defaultFields['synthesis'];
+						break;
+				}
+				break;
+
+			//Syncevolution compatibility
+			case 'patrick ohly':
+				$this->supportedFields = $defaultFields['evolution'];
+				break;
+
+			case '': // seems syncevolution 0.5 doesn't send a manufacturer
+				error_log("No vendor name, assuming syncevolution 0.5");
+				$this->supportedFields = $defaultFields['evolution'];
+				break;
+
+			case 'file':	// used outside of SyncML, eg. by the calendar itself ==> all possible fields
+				$this->supportedFields = $defaultFields['full'];
+				break;
+
+			// the fallback for SyncML
+			default:
+				error_log("Unknown calendar SyncML client: manufacturer='$_productManufacturer'  product='$_productName'");
+				$this->supportedFields = $defaultFields['full'];
+				break;
+		}
+	}
+
+	function icaltoegw($_vcalData)
+	{
+		// our (patched) horde classes, do NOT unfold folded lines, which causes a lot trouble in the import
+		$_vcalData = preg_replace("/[\r\n]+ /",'',$_vcalData);
+
+		$vcal = &new Horde_iCalendar;
+		if(!$vcal->parsevCalendar($_vcalData))
+		{
+			return FALSE;
+		}
+
+		if(!is_array($this->supportedFields))
+		{
+			$this->setSupportedFields();
+		}
+		//echo "supportedFields="; _debug_array($this->supportedFields);
+
+		$Ok = false;	// returning false, if file contains no components
+		foreach($vcal->getComponents() as $component)
+		{
+			if(is_a($component, 'Horde_iCalendar_vevent'))
+			{
+				$supportedFields = $this->supportedFields;
+				#$event = array('participants' => array());
+				$event		= array();
+				$alarms		= array();
+				$vcardData	= array('recur_type' => 0);
+
+				// lets see what we can get from the vcard
+				foreach($component->_attributes as $attributes)
+				{
+					switch($attributes['name'])
+					{
+						case 'AALARM':
+						case 'DALARM':
+							if (preg_match('/.*Z$/',$attributes['value'],$matches))
+							{
+								$alarmTime = $vcal->_parseDateTime($attributes['value']);
+								$alarms[$alarmTime] = array(
+									'time' => $alarmTime
+								);
+							}
+							break;
+						case 'CLASS':
+							$vcardData['public']		= (int)(strtolower($attributes['value']) == 'public');
+							break;
+						case 'DESCRIPTION':
+							$vcardData['description']	= $attributes['value'];
+							break;
+						case 'DTEND':
+							if(date('H:i:s',$attributes['value']) == '00:00:00')
+								$attributes['value']--;
+							$vcardData['end']		= $attributes['value'];
+							break;
+						case 'DTSTART':
+							$vcardData['start']		= $attributes['value'];
+							break;
+						case 'LOCATION':
+							$vcardData['location']	= $attributes['value'];
+							break;
+						case 'RRULE':
+							$recurence = $attributes['value'];
+							$type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence[0];
+							// vCard 2.0 values for all types
+							if (preg_match('/UNTIL=([0-9T]+)/',$recurence,$matches))
+							{
+								$vcardData['recur_enddate'] = $vcal->_parseDateTime($matches[1]);
+							}
+							if (preg_match('/INTERVAL=([0-9]+)/',$recurence,$matches))
+							{
+								$vcardData['recur_interval'] = (int) $matches[1];
+							}
+							$vcardData['recur_data'] = 0;
+							switch($type)
+							{
+								case 'W':
+								case 'WEEKLY':
+									$days = array();
+									if(preg_match('/W(\d+) (.*) (.*)/',$recurence, $recurenceMatches))		// 1.0
+									{
+										$vcardData['recur_interval'] = $recurenceMatches[1];
+										$days = explode(' ',trim($recurenceMatches[2]));
+										if($recurenceMatches[3] != '#0')
+											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[3]);
+										$recur_days = $this->recur_days_1_0;
+									}
+									elseif (preg_match('/BYDAY=([^;: ]+)/',$recurence,$recurenceMatches))	// 2.0
+									{
+										$days = explode(',',$recurenceMatches[1]);
+										$recur_days = $this->recur_days;
+									}
+									if ($days)
+									{
+										foreach($recur_days as $id => $day)
+			            						{
+			            							if (in_array(strtoupper(substr($day,0,2)),$days))
+	            									{
+	            										$vcardData['recur_data'] |= $id;
+	            									}
+	            								}
+										$vcardData['recur_type'] = MCAL_RECUR_WEEKLY;
+									}
+									break;
+
+								case 'D':		// 1.0
+									if(!preg_match('/D(\d+) (.*)/',$recurence, $recurenceMatches)) break;
+									$vcardData['recur_interval'] = $recurenceMatches[1];
+									if($recurenceMatches[2] != '#0')
+										$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
+									// fall-through
+								case 'DAILY':	// 2.0
+									$vcardData['recur_type'] = MCAL_RECUR_DAILY;
+									break;
+
+								case 'M':
+									if(preg_match('/MD(\d+) (.*)/',$recurence, $recurenceMatches))
+									{
+										$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_MDAY;
+										if($recurenceMatches[1] > 1)
+											$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[2] != '#0')
+											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
+									}
+									elseif(preg_match('/MP(\d+) (.*) (.*) (.*)/',$recurence, $recurenceMatches))
+									{
+										$vcardData['recur_type'] = MCAL_RECUR_MONTHLY_WDAY;
+										if($recurenceMatches[1] > 1)
+											$vcardData['recur_interval'] = $recurenceMatches[1];
+										if($recurenceMatches[4] != '#0')
+											$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[4]);
+									}
+									break;
+								case 'MONTHLY':
+									$vcardData['recur_type'] = strpos($recurence,'BYDAY') !== false ?
+										MCAL_RECUR_MONTHLY_WDAY : MCAL_RECUR_MONTHLY_MDAY;
+									break;
+
+								case 'Y':		// 1.0
+									if(!preg_match('/YM(\d+) (.*)/',$recurence, $recurenceMatches)) break;
+									$vcardData['recur_interval'] = $recurenceMatches[1];
+									if($recurenceMatches[2] != '#0')
+										$vcardData['recur_enddate'] = $vcal->_parseDateTime($recurenceMatches[2]);
+									// fall-through
+								case 'YEARLY':	// 2.0
+									$vcardData['recur_type'] = MCAL_RECUR_YEARLY;
+									break;
+							}
+							break;
+						case 'EXDATE':
+							$vcardData['recur_exception'] = $attributes['value'];
+							break;
+						case 'SUMMARY':
+							$vcardData['title']		= $attributes['value'];
+							break;
+						case 'UID':
+							$event['uid'] = $vcardData['uid'] = $attributes['value'];
+							if ($cal_id <= 0 && !empty($vcardData['uid']) && ($uid_event = $this->read($vcardData['uid'])))
+							{
+								$event['id'] = $uid_event['id'];
+								unset($uid_event);
+							}
+							break;
+ 						case 'TRANSP':
+							$vcardData['non_blocking'] = $attributes['value'] == 'TRANSPARENT';
+							break;
+						case 'PRIORITY':
+							if ($this->productManufacturer == 'nexthaus corporation'
+								|| $this->productManufacturer == 'nexthaus corp')
+							{
+								$vcardData['priority'] = $attributes['value'] == 1 ? 3 : 2; // 1=high, 2=normal
+							}
+							else
+							{
+ 								$vcardData['priority'] = (int) $this->priority_ical2egw[$attributes['value']];
+							}
+ 							break;
+ 						case 'CATEGORIES':
+ 							if ($attributes['value'])
+ 							{
+								$vcardData['category'] = $this->find_or_add_categories(explode(',',$attributes['value']));
+							}
+							else
+							{
+ 								$vcardData['category'] = array();
+							}
+ 							break;
+ 						case 'ATTENDEE':
+ 							if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
+ 								($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email')))
+ 							{
+ 								$event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ?
+ 									$this->status_ical2egw[strtoupper($attributes['params']['PARTSTAT'])] :
+ 									($uid == $event['owner'] ? 'A' : 'U');
+ 							}
+ 							break;
+ 						case 'ORGANIZER':	// will be written direct to the event
+ 							if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
+ 								($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email')))
+ 							{
+ 								$event['owner'] = $uid;
+ 							}
+ 							break;
+ 						case 'CREATED':		// will be written direct to the event
+ 							if ($event['modified']) break;
+ 							// fall through
+ 						case 'LAST-MODIFIED':	// will be written direct to the event
+							$event['modified'] = $attributes['value'];
+							break;
+					}
+				}
+
+				// check if the entry is a birthday
+				// this field is only set from NOKIA clients
+				$agendaEntryType = $component->getAttribute('X-EPOCAGENDAENTRYTYPE');
+				if (!is_a($agendaEntryType, 'PEAR_Error')) {
+					if(strtolower($agendaEntryType) == 'anniversary') {
+						$event['special'] = '1';
+						$vcardData['end'] = $vcardData['start'] + 86399;
+					}
+				}
+
+				if(!empty($vcardData['recur_enddate']))
+				{
+					// reset recure_enddate to 00:00:00 on the last day
+					$vcardData['recur_enddate'] = mktime(0, 0, 0,
+						date('m',$vcardData['recur_enddate']),
+						date('d',$vcardData['recur_enddate']),
+						date('Y',$vcardData['recur_enddate'])
+					);
+				}
+				//echo "event=";_debug_array($vcardData);
+
+				while(($fieldName = array_shift($supportedFields)))
+				{
+					switch($fieldName)
+					{
+						case 'recur_interval':
+						case 'recur_enddate':
+						case 'recur_data':
+						case 'recur_exception':
+						case 'alarms':
+							// not handled here
+							break;
+						case 'recur_type':
+							$event['recur_type'] = $vcardData['recur_type'];
+							if ($event['recur_type'] != MCAL_RECUR_NONE)
+							{
+								foreach(array('recur_interval','recur_enddate','recur_data','recur_exception') as $r)
+								{
+									if(isset($vcardData[$r]))
+									{
+										$event[$r] = $vcardData[$r];
+									}
+								}
+							}
+							break;
+						default:
+							if (isset($vcardData[$fieldName]))
+							{
+								$event[$fieldName] = $vcardData[$fieldName];
+							}
+							break;
+					}
+				}
+
+				return $event;
+			}
+		}
+
+		return false;
+	}
+
+	function search($_vcalData)
+	{
+		if(!$event = $this->icaltoegw($_vcalData)) {
+			return false;
+		}
+
+		$query = array(
+			'cal_start='.$this->date2ts($event['start'],true),	// true = Server-time
+			'cal_end='.$this->date2ts($event['end'],true),
+		);
+
+		#foreach(array('title','location','priority','public','non_blocking') as $name) {
+		foreach(array('title','location','public','non_blocking') as $name) {
+			if (isset($event[$name])) $query['cal_'.$name] = $event[$name];
+		}
+
+		if($foundEvents = parent::search(array(
+			'user'  => $this->user,
+			'query' => $query,
+		))) {
+			if(is_array($foundEvents)) {
+				$event = array_shift($foundEvents);
+				return $event['id'];
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Create a freebusy vCal for the given user(s)
+	 *
+	 * @param int $user account_id
+	 * @param mixed $end=null end-date, default now+1 month
+	 * @return string
+	 */
+	function freebusy($user,$end=null)
+	{
+		if (!$end) $end = $this->now_su + 100*DAY_s;	// default next 100 days
+
+		$vcal = &new Horde_iCalendar;
+		$vcal->setAttribute('PRODID','-//eGroupWare//NONSGML eGroupWare Calendar '.$GLOBALS['egw_info']['apps']['calendar']['version'].'//'.
+			strtoupper($GLOBALS['egw_info']['user']['preferences']['common']['lang']));
+		$vcal->setAttribute('VERSION','2.0');
+
+		$vfreebusy = Horde_iCalendar::newComponent('VFREEBUSY',$vcal);
+		$parameters = array(
+			'ORGANIZER' => $GLOBALS['egw']->translation->convert(
+				$GLOBALS['egw']->accounts->id2name($user,'account_firstname').' '.
+				$GLOBALS['egw']->accounts->id2name($user,'account_lastname'),
+				$GLOBALS['egw']->translation->charset(),'utf-8'),
+		);
+		foreach(array(
+			'URL' => $this->freebusy_url($user),
+			'DTSTART' => $this->date2ts($this->now_su,true),	// true = server-time
+			'DTEND' => $this->date2ts($end,true),	// true = server-time
+		  	'ORGANIZER' => $GLOBALS['egw']->accounts->id2name($user,'account_email'),
+			'DTSTAMP' => time(),
+		) as $attr => $value)
+		{
+			$vfreebusy->setAttribute($attr, $value, $parameters[$name]);
+		}
+		$fbdata = parent::search(array(
+					'start' => $this->now_su,
+					'end'   => $end,
+					'users' => $user,
+					'date_format' => 'server',
+					'show_rejected' => false,
+				));
+		if (is_array($fbdata))
+		{
+			foreach ($fbdata as $event)
+			{
+				if ($event['non_blocking']) continue;
+
+				$vfreebusy->setAttribute('FREEBUSY',array(array(
+					'start' => $event['start'],
+					'end' => $event['end'],
+				)));
+			}
+		}
+		$vcal->addComponent($vfreebusy);
+
+		return $vcal->exportvCalendar();
+	}
+}
diff --git a/calendar/inc/class.calendar_sif.inc.php b/calendar/inc/class.calendar_sif.inc.php
new file mode 100644
index 0000000000..119118344e
--- /dev/null
+++ b/calendar/inc/class.calendar_sif.inc.php
@@ -0,0 +1,503 @@
+
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package calendar
+ * @subpackage export
+ * @version $Id$
+ */
+
+require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/Horde/iCalendar.php';
+
+/**
+ * SIF Parser for SyncML
+ */
+class sifcalendar extends calendar_boupdate
+{
+	var $sifMapping = array(
+		'Start'				=> 'start',
+		'End'				=> 'end',
+		'AllDayEvent'			=> 'alldayevent',
+		'BillingInformation'		=> '',
+		'Body'				=> 'description',
+		'BusyStatus'			=> '',
+		'Categories'			=> 'category',
+		'Companies'			=> '',
+		'Importance'			=> 'priority',
+		'IsRecurring'			=> 'isrecurring',
+		'Location'			=> 'location',
+		'MeetingStatus'			=> '',
+		'Mileage'			=> '',
+		'ReminderMinutesBeforeStart'	=> 'reminderstart',
+		'ReminderSet'			=> 'reminderset',
+		'ReplyTime'			=> '',
+		'Sensitivity'			=> 'public',
+		'Subject'			=> 'title',
+		'RecurrenceType'		=> 'recur_type',
+		'Interval'			=> 'recur_interval',
+		'MonthOfYear'			=> '',
+		'DayOfMonth'			=> '',
+		'DayOfWeekMask'			=> 'recur_weekmask',
+		'Instance'			=> '',
+		'PatternStartDate'		=> '',
+		'NoEndDate'			=> 'recur_noenddate',
+		'PatternEndDate'		=> 'recur_enddate',
+		'Occurrences'			=> '',
+	);
+
+	// the calendar event array
+	var $event;
+
+	// constants for recurence type
+	const olRecursDaily	= 0;
+	const olRecursWeekly	= 1;
+	const olRecursMonthly	= 2;
+	const olRecursMonthNth	= 3;
+	const olRecursYearly	= 5;
+	const olRecursYearNth	= 6;
+
+	// constants for weekdays
+	const olSunday = 1;
+	const olMonday = 2;
+	const olTuesday = 4;
+	const olWednesday = 8;
+	const olThursday = 16;
+	const olFriday = 32;
+	const olSaturday = 64;
+
+	function startElement($_parser, $_tag, $_attributes) {
+	}
+
+	function endElement($_parser, $_tag) {
+		//error_log('endElem: ' . $_tag .' => '. trim($this->sifData));
+		if(!empty($this->sifMapping[$_tag])) {
+			$this->event[$this->sifMapping[$_tag]] = trim($this->sifData);
+		}
+		unset($this->sifData);
+	}
+
+	function characterData($_parser, $_data) {
+		$this->sifData .= $_data;
+	}
+
+	function siftoegw($_sifdata) {
+		$vcal		= &new Horde_iCalendar;
+		$finalEvent	= array();
+		$sysCharSet	= $GLOBALS['egw']->translation->charset();
+		$sifData	= base64_decode($_sifdata);
+		#error_log($sifData);
+
+		$tmpfname = tempnam('/tmp/sync/contents','sife_');
+
+		$handle = fopen($tmpfname, "w");
+		fwrite($handle, $sifData);
+		fclose($handle);
+
+		$this->xml_parser = xml_parser_create('UTF-8');
+		xml_set_object($this->xml_parser, $this);
+		xml_parser_set_option($this->xml_parser, XML_OPTION_CASE_FOLDING, false);
+		xml_set_element_handler($this->xml_parser, "startElement", "endElement");
+		xml_set_character_data_handler($this->xml_parser, "characterData");
+		$this->strXmlData = xml_parse($this->xml_parser, $sifData);
+		if(!$this->strXmlData) {
+			error_log(sprintf("XML error: %s at line %d",
+				xml_error_string(xml_get_error_code($this->xml_parser)),
+				xml_get_current_line_number($this->xml_parser)));
+			return false;
+		}
+		#error_log(print_r($this->event, true));
+
+		foreach($this->event as $key => $value) {
+			$value = $GLOBALS['egw']->translation->convert($value, 'utf-8', $sysCharSet);
+			#error_log("$key => $value");
+			switch($key) {
+				case 'alldayevent':
+					if($value == 1) {
+						$startParts = explode('-',$this->event['start']);
+						$finalEvent['start'] = mktime(0, 0, 0, $startParts[1], $startParts[2], $startParts[0]);
+						$endParts = explode('-',$this->event['end']);
+						$finalEvent['end'] = mktime(23, 59, 59, $endParts[1], $endParts[2], $endParts[0]);
+					}
+					break;
+
+				case 'public':
+					$finalEvent[$key] = ((int)$value > 0) ? 0 : 1;
+					break;
+
+				case 'category':
+					if(!empty($value)) {
+						$finalEvent[$key] = implode(',',$this->find_or_add_categories(explode(';', $value)));
+					}
+					break;
+
+				case 'end':
+				case 'start':
+					if($this->event['alldayevent'] < 1) {
+						$finalEvent[$key] = $vcal->_parseDateTime($value);
+						error_log("event ".$key." val=".$value.", parsed=".$finalEvent[$key]);
+					}
+					break;
+
+				case 'isrecurring':
+					if($value == 1) {
+						$finalEvent['recur_interval'] = $this->event['recur_interval'];
+						if($this->event['recur_noenddate'] == 0) {
+							$finalEvent['recur_enddate'] = $vcal->_parseDateTime($this->event['recur_enddate']);
+						}
+						switch($this->event['recur_type']) {
+							case self::olRecursDaily:
+								$finalEvent['recur_type']	= MCAL_RECUR_DAILY;
+								break;
+
+							case self::olRecursWeekly:
+								$finalEvent['recur_type']	= MCAL_RECUR_WEEKLY;
+								$finalEvent['recur_data']	= $this->event['recur_weekmask'];
+								break;
+
+							case self::olRecursMonthly:
+								$finalEvent['recur_type']	= MCAL_RECUR_MONTHLY_MDAY;
+								break;
+
+							case self::olRecursMonthNth:
+								$finalEvent['recur_type']	= MCAL_RECUR_MONTHLY_WDAY;
+								break;
+
+							case self::olRecursYearly:
+								$finalEvent['recur_type']	= MCAL_RECUR_YEARLY;
+								$finalEvent['recur_interval'] = 1;
+								break;
+						}
+					}
+					break;
+
+				case 'priority':
+					$finalEvent[$key] = $value+1;
+					break;
+
+				case 'reminderset':
+					if($value == 1) {
+						$finalEvent['alarm'] = $this->event['reminderstart'];
+					}
+					break;
+
+				case 'recur_type':
+				case 'recur_enddate':
+				case 'recur_interval':
+				case 'recur_weekmask':
+				case 'reminderstart':
+					// do nothing, get's handled in isrecuring clause
+					break;
+
+				default:
+					$finalEvent[$key] = $value;
+					break;
+			}
+		}
+
+		#$middleName = ($finalEvent['n_middle']) ? ' '.trim($finalEvent['n_middle']) : '';
+		#$finalEvent['fn']  = trim($finalEvent['n_given']. $middleName .' '. $finalEvent['n_family']);
+
+		#error_log(print_r($finalEvent, true));
+
+		return $finalEvent;
+	}
+
+	function search($_sifdata) {
+		if(!$event = $this->siftoegw($_sifdata)) {
+			return false;
+		}
+
+		$query = array(
+			'cal_start='.$this->date2ts($event['start'],true),	// true = Server-time
+			'cal_end='.$this->date2ts($event['end'],true),
+		);
+
+		#foreach(array('title','location','priority','public','non_blocking') as $name) {
+		foreach(array('title','location','public','non_blocking') as $name) {
+			if (isset($event[$name])) $query['cal_'.$name] = $event[$name];
+		}
+
+		if($foundEvents = parent::search(array(
+			'user'  => $this->user,
+			'query' => $query,
+		))) {
+			if(is_array($foundEvents)) {
+				$event = array_shift($foundEvents);
+				return $event['id'];
+			}
+		}
+		return false;
+
+		$search['start'] = $event['start'];
+		$search['end']	= $event['end'];
+
+		unset($event['description']);
+		unset($event['start']);
+		unset($event['end']);
+
+		foreach($event as $key => $value) {
+			if (substr($key,0,6) != 'recur_' && substr($key,0,5) != 'alarm') {
+				$search['query']['cal_'.$key] = $value;
+			} else {
+				#$search['query'][$key] = $value;
+			}
+		}
+
+		if($foundEvents = parent::search($search)) {
+			if(is_array($foundEvents)) {
+				$event = array_shift($foundEvents);
+				return $event['id'];
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	* @return int contact id
+	* @param string	$_vcard		the vcard
+	* @param int	$_abID		the internal addressbook id
+	* @desc import a vard into addressbook
+	*/
+	function addSIF($_sifdata, $_calID)
+	{
+		$calID = false;
+
+		#error_log('ABID: '.$_abID);
+		#error_log(base64_decode($_sifdata));
+
+		if(!$event = $this->siftoegw($_sifdata)) {
+			return false;
+		}
+
+		if(isset($event['alarm'])) {
+			$alarm = $event['alarm'];
+			unset($event['alarm']);
+		}
+
+		if($_calID > 0)
+		{
+			// update entry
+			$event['id'] = $_calID;
+		}
+
+		if($eventID = $this->update($event, TRUE)) {
+			$updatedEvent = $this->read($eventID);
+			foreach($updatedEvent['alarm'] as $alarmID => $alarmData)
+			{
+				$this->delete_alarm($alarmID);
+			}
+
+			if(isset($alarm)) {
+				$alarmData['time']	= $event['start'] - ($alarm*60);
+				$alarmData['offset']	= $alarm*60;
+				$alarmData['all']	= 1;
+				$alarmData['owner']	= $GLOBALS['egw_info']['user']['account_id'];
+				$this->save_alarm($eventID, $alarmData);
+			}
+		}
+
+		return $eventID;
+	}
+
+	/**
+	* return a vcard
+	*
+	* @param int	$_id		the id of the contact
+	* @param int	$_vcardProfile	profile id for mapping from vcard values to egw addressbook
+	* @return string containing the vcard
+	*/
+	function getSIF($_id)
+	{
+		$fields = array_unique(array_values($this->sifMapping));
+		sort($fields);
+
+		#$event = $this->read($_id,null,false,'server');
+		#error_log("FOUND EVENT: ". print_r($event, true));
+
+		if($event = $this->read($_id,null,false,'server')) {
+			$sysCharSet	= $GLOBALS['egw']->translation->charset();
+			$vcal		= &new Horde_iCalendar;
+
+
+			$sifEvent = '';
+
+			foreach($this->sifMapping as $sifField => $egwField)
+			{
+				if(empty($egwField)) continue;
+
+				#error_log("$sifField => $egwField");
+				#error_log('VALUE1: '.$event[$egwField]);
+				$value = $GLOBALS['egw']->translation->convert($event[$egwField], $sysCharSet, 'utf-8');
+				#error_log('VALUE2: '.$value);
+
+				switch($sifField)
+				{
+					case 'Categories':
+						if(!empty($value)) {
+							$value = implode('; ', $this->get_categories(explode(',',$value)));
+							$value = $GLOBALS['egw']->translation->convert($value, $sysCharSet, 'utf-8');
+						}
+						$sifEvent .= "<$sifField>$value";
+						break;
+
+					case 'Importance':
+						$value = $value-1;
+						$sifEvent .= "<$sifField>$value";
+						break;
+
+					case 'RecurrenceType':
+					case 'Interval':
+					case 'PatternStartDate':
+					case 'NoEndDate':
+					case 'DayOfWeekMask':
+					case 'PatternEndDate':
+						break;
+
+					case 'IsRecurring':
+						switch($event['recur_type']) {
+							case MCAL_RECUR_NONE:
+								$sifEvent .= "<$sifField>0";
+								break;
+
+							case MCAL_RECUR_DAILY:
+								$eventInterval = ($event['recur_interval'] > 1 ? $event['recur_interval'] : 1);
+								$recurStartDate = mktime(0,0,0,date('m',$event['start']), date('d', $event['start']), date('Y', $event['start']));
+
+								$sifEvent .= "<$sifField>1";
+								$sifEvent .= ''. self::olRecursDaily .'';
+								$sifEvent .= ''. $eventInterval .'';
+								$sifEvent .= ''. $vcal->_exportDateTime($recurStartDate) .'';
+								if($event['recur_enddate'] == 0) {
+									$sifEvent .= '1';
+								} else {
+									$recurEndDate = mktime(24,0,0,date('m',$event['recur_enddate']), date('d', $event['recur_enddate']), date('Y', $event['recur_enddate']));
+
+									$sifEvent .= '0';
+									$sifEvent .= ''. $vcal->_exportDateTime($recurEndDate) .'';
+									$totalDays = ($recurEndDate - $recurStartDate) / 86400;
+									$occurrences = ceil($totalDays / $eventInterval);
+									$sifEvent .= ''. $occurrences .'';
+								}
+								break;
+
+							case MCAL_RECUR_WEEKLY:
+								$eventInterval = ($event['recur_interval'] > 1 ? $event['recur_interval'] : 1);
+								$recurStartDate = mktime(0,0,0,date('m',$event['start']), date('d', $event['start']), date('Y', $event['start']));
+
+								$sifEvent .= "<$sifField>1";
+								$sifEvent .= ''. self::olRecursWeekly .'';
+								$sifEvent .= ''. $eventInterval .'';
+								$sifEvent .= ''. $vcal->_exportDateTime($recurStartDate) .'';
+								$sifEvent .= ''. $event['recur_data'] .'';
+								if($event['recur_enddate'] == 0) {
+									$sifEvent .= '1';
+								} else {
+									$recurEndDate = mktime(24, 0, 0, date('m',$event['recur_enddate']), date('d', $event['recur_enddate']), date('Y', $event['recur_enddate']));
+
+									$daysPerWeek = substr_count(decbin($event['recur_data']),'1');
+									$sifEvent .= '0';
+									$sifEvent .= ''. $vcal->_exportDateTime($recurEndDate) .'';
+									$totalWeeks = floor(($recurEndDate - $recurStartDate) / (86400*7));
+									#error_log("AAA: $daysPerWeek $totalWeeks");
+									$occurrences = ($totalWeeks / $eventInterval) * $daysPerWeek;
+									for($i = $recurEndDate; $i > $recurStartDate + ($totalWeeks * 86400*7); $i = $i - 86400) {
+										switch(date('w', $i-1)) {
+											case 0:
+												if($event['recur_data'] & 1) $occurrences++;
+												break;
+											// monday
+											case 1:
+												if($event['recur_data'] & 2) $occurrences++;
+												break;
+											case 2:
+												if($event['recur_data'] & 4) $occurrences++;
+												break;
+											case 3:
+												if($event['recur_data'] & 8) $occurrences++;
+												break;
+											case 4:
+												if($event['recur_data'] & 16) $occurrences++;
+												break;
+											case 5:
+												if($event['recur_data'] & 32) $occurrences++;
+												break;
+											case 6:
+												if($event['recur_data'] & 64) $occurrences++;
+												break;
+										}
+									}
+									$sifEvent .= ''. $occurrences .'';
+								}
+								break;
+						}
+						break;
+
+					case 'Sensitivity':
+						$value = (!$value ? '2' : '0');
+						$sifEvent .= "<$sifField>$value";
+						break;
+
+					case 'Folder':
+						# skip currently. This is the folder where Outlook stores the contact.
+						#$sifEvent .= "<$sifField>/";
+						break;
+
+					case 'AllDayEvent':
+					case 'End':
+						// get's handled by Start clause
+						break;
+
+					case 'Start':
+						if($event['end'] - $event['start'] == 86399 && date('Y-m-d', $event['end']) == date('Y-m-d', $event['start'])) {
+							$value = date('Y-m-d', $event['start']);
+							$sifEvent .= "$value";
+							$sifEvent .= "$value";
+							$sifEvent .= "1";
+						} else {
+							$value = $vcal->_exportDateTime($event['start']);
+							$sifEvent .= "$value";
+							$value = $vcal->_exportDateTime($event['end']);
+							$sifEvent .= "$value";
+							$sifEvent .= "0";
+						}
+						break;
+
+					case 'ReminderMinutesBeforeStart':
+						break;
+
+					case 'ReminderSet':
+						if(count((array)$event['alarm']) > 0) {
+							$sifEvent .= "<$sifField>1";
+							foreach($event['alarm'] as $alarmID => $alarmData)
+							{
+								$sifEvent .= ''. $alarmData['offset']/60 .'';
+								// lets take only the first alarm
+								break;
+							}
+						} else {
+							$sifEvent .= "<$sifField>0";
+						}
+						break;
+
+					default:
+						$sifEvent .= "<$sifField>$value";
+						break;
+				}
+			}
+			$sifEvent .= '';
+
+			return base64_encode($sifEvent);
+		}
+
+		if($this->xmlrpc)
+		{
+			$GLOBALS['server']->xmlrpc_error($GLOBALS['xmlrpcerr']['no_access'],$GLOBALS['xmlrpcstr']['no_access']);
+		}
+		return False;
+	}
+
+}
diff --git a/calendar/inc/class.socal.inc.php b/calendar/inc/class.calendar_so.inc.php
similarity index 98%
rename from calendar/inc/class.socal.inc.php
rename to calendar/inc/class.calendar_so.inc.php
index d0842987de..cea5feb0fa 100644
--- a/calendar/inc/class.socal.inc.php
+++ b/calendar/inc/class.calendar_so.inc.php
@@ -58,7 +58,7 @@ define('ACCEPTED',3);
  *  BO's functions take and return user-time only (!), they convert internaly everything to servertime, because
  *  SO operates only on server-time
  */
-class socal
+class calendar_so
 {
 	/**
 	 * name of the main calendar table and prefix for all other calendar tables
@@ -82,7 +82,7 @@ class socal
 	/**
 	 * Constructor of the socal class
 	 */
-	function socal()
+	function __construct()
 	{
 		$this->async = $GLOBALS['egw']->asyncservice;
 		$this->db = $GLOBALS['egw']->db;
@@ -1045,8 +1045,19 @@ ORDER BY cal_user_type, cal_usre_id
 		return $this->async->cancel_timer($id);
 	}
 
-	function change_delete_user($old_user,$new_user=false)
+	/**
+	 * Delete account hook
+	 *
+	 * @param array|int $old_user integer old user or array with keys 'account_id' and 'new_owner' as the deleteaccount hook uses it
+	 * @param int $new_user=null
+	 */
+	function deleteaccount($data)
 	{
+		if (is_array($old_user))
+		{
+			$new_user = $old_user['new_owner'];
+			$old_user = $old_user['account_id'];
+		}
 		if (!(int)$new_user)
 		{
 			$this->split_user($old_user,$user_type,$user_id);
diff --git a/calendar/inc/class.uical.inc.php b/calendar/inc/class.calendar_ui.inc.php
similarity index 93%
rename from calendar/inc/class.uical.inc.php
rename to calendar/inc/class.calendar_ui.inc.php
index 3dd9d8c237..0a6f87360d 100644
--- a/calendar/inc/class.uical.inc.php
+++ b/calendar/inc/class.calendar_ui.inc.php
@@ -22,7 +22,7 @@
  *
  * All permanent debug messages of the calendar-code should done via the debug-message method of the bocal class !!!
  */
-class uical
+class calendar_ui
 {
 	/**
 	 * @var $debug mixed integer level or string function-name
@@ -31,7 +31,7 @@ class uical
 	/**
 	 * instance of the bocal or bocalupdate class
 	 *
-	 * @var bocalupdate
+	 * @var calendar_boupdate
 	 */
 	var $bo;
 	/**
@@ -40,12 +40,6 @@ class uical
 	 * @var jscalendar
 	 */
 	var $jscal;
-	/**
-	 * Reference to global html class
-	 *
-	 * @var html
-	 */
-	var $html;
 	/**
 	 * Reference to global datetime class
 	 *
@@ -129,14 +123,19 @@ class uical
 	/**
 	 * Constructor
 	 *
-	 * @param boolean $use_bocalupdate use bocalupdate as parenent instead of bocal
+	 * @param boolean $use_boupdate use bocalupdate as parenent instead of bocal
 	 * @param array $set_states=null to manualy set / change one of the states, default NULL = use $_REQUEST
 	 */
-	function uical($use_bocalupdate=false,$set_states=NULL)
+	function __construct($use_boupdate=false,$set_states=NULL)
 	{
-		$bo_class = $use_bocalupdate ? 'bocalupdate' : 'bocal';
-		require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.'.$bo_class.'.inc.php');
-		$this->bo = new $bo_class();
+		if ($use_boupdate)
+		{
+			$this->bo = new calendar_boupdate();
+		}
+		else
+		{
+			$this->bo = new calendar_bo();
+		}
 		$this->jscal = $GLOBALS['egw']->jscalendar;
 		$this->datetime = $GLOBALS['egw']->datetime;
 		$this->accountsel = $GLOBALS['egw']->uiaccountsel;
@@ -299,7 +298,7 @@ class uical
 					}
 				}
 				// for the uiforms class (eg. edit), dont store the (new) owner, as it might change the view
-				if (substr($_GET['menuaction'],0,16) == 'calendar.uiforms')
+				if (substr($_GET['menuaction'],0,16) == 'calendar.calendar_uiforms')
 				{
 					$this->owner = $set_states[$state];
 					continue;
@@ -359,7 +358,7 @@ class uical
 			}
 			$this->view = $states['view'] = $func;
 		}
-		$this->view_menuaction = $this->view == 'listview' ? 'calendar.uilist.listview' : 'calendar.uiviews.'.$this->view;
+		$this->view_menuaction = $this->view == 'listview' ? 'calendar.calendar_uilist.listview' : 'calendar.calendar_uiviews.'.$this->view;
 
 		if ($this->debug > 0 || $this->debug == 'manage_states') $this->bo->debug_message('uical::manage_states(%1) session was %2, states now %3',True,$set_states,$states_session,$states);
 		// save the states in the session
@@ -464,7 +463,7 @@ class uical
 	function add_link($content,$date=null,$hour=null,$minute=0)
 	{
 		$vars = array(
-			'menuaction'=>'calendar.uiforms.edit',
+			'menuaction'=>'calendar.calendar_uiforms.edit',
 			'date' => $date ? $date : $this->date,
 		);
 		if (!is_null($hour))
@@ -537,11 +536,11 @@ class uical
 		$views = ''."\n";
 		foreach(array(
 			'add' => array('icon'=>'new','text'=>'add'),
-			'day' => array('icon'=>'today','text'=>'Today','menuaction' => 'calendar.uiviews.day','date' => $this->bo->date2string($this->bo->now_su)),
-			'week' => array('icon'=>'week','text'=>'Weekview','menuaction' => 'calendar.uiviews.week'),
-			'month' => array('icon'=>'month','text'=>'Monthview','menuaction' => 'calendar.uiviews.month'),
-			'planner' => array('icon'=>'planner','text'=>'Group planner','menuaction' => 'calendar.uiviews.planner','sortby' => $this->sortby),
-			'list' => array('icon'=>'list','text'=>'Listview','menuaction'=>'calendar.uilist.listview'),
+			'day' => array('icon'=>'today','text'=>'Today','menuaction' => 'calendar.calendar_uiviews.day','date' => $this->bo->date2string($this->bo->now_su)),
+			'week' => array('icon'=>'week','text'=>'Weekview','menuaction' => 'calendar.calendar_uiviews.week'),
+			'month' => array('icon'=>'month','text'=>'Monthview','menuaction' => 'calendar.calendar_uiviews.month'),
+			'planner' => array('icon'=>'planner','text'=>'Group planner','menuaction' => 'calendar.calendar_uiviews.planner','sortby' => $this->sortby),
+			'list' => array('icon'=>'list','text'=>'Listview','menuaction'=>'calendar.calendar_uilist.listview'),
 		) as $view => $data)
 		{
 			$icon = array_shift($data);
@@ -562,49 +561,49 @@ class uical
 		foreach(array(
 			array(
 				'text' => lang('dayview'),
-				'value' => 'menuaction=calendar.uiviews.day',
+				'value' => 'menuaction=calendar.calendar_uiviews.day',
 				'selected' => $this->view == 'day',
 			),
 			array(
 				'text' => lang('four days view'),
-				'value' => 'menuaction=calendar.uiviews.day4',
+				'value' => 'menuaction=calendar.calendar_uiviews.day4',
 				'selected' => $this->view == 'day4',
 			),
 			array(
 				'text' => lang('weekview with weekend'),
-				'value' => 'menuaction=calendar.uiviews.week&days=7',
+				'value' => 'menuaction=calendar.calendar_uiviews.week&days=7',
 				'selected' => $this->view == 'week' && $this->cal_prefs['days_in_weekview'] != 5,
 			),
 			array(
 				'text' => lang('weekview without weekend'),
-				'value' => 'menuaction=calendar.uiviews.week&days=5',
+				'value' => 'menuaction=calendar.calendar_uiviews.week&days=5',
 				'selected' => $this->view == 'week' && $this->cal_prefs['days_in_weekview'] == 5,
 			),
 			array(
 				'text' => lang('Multiple week view'),
-				'value' => 'menuaction=calendar.uiviews.weekN',
+				'value' => 'menuaction=calendar.calendar_uiviews.weekN',
 				'selected' => $this->view == 'weekN',
 			),
 			array(
 				'text' => lang('monthview'),
-				'value' => 'menuaction=calendar.uiviews.month',
+				'value' => 'menuaction=calendar.calendar_uiviews.month',
 				'selected' => $this->view == 'month',
 			),
 			array(
 				'text' => lang('planner by category'),
-				'value' => 'menuaction=calendar.uiviews.planner&sortby=category'.
+				'value' => 'menuaction=calendar.calendar_uiviews.planner&sortby=category'.
 					($planner_days_for_view !== false ? '&planner_days='.$planner_days_for_view : ''),
 				'selected' => $this->view == 'planner' && $this->sortby != 'user',
 			),
 			array(
 				'text' => lang('planner by user'),
-				'value' => 'menuaction=calendar.uiviews.planner&sortby=user'.
+				'value' => 'menuaction=calendar.calendar_uiviews.planner&sortby=user'.
 					($planner_days_for_view !== false ? '&planner_days='.$planner_days_for_view : ''),
 				'selected' => $this->view == 'planner' && $this->sortby == 'user',
 			),
 			array(
 				'text' => lang('listview'),
-				'value' => 'menuaction=calendar.uilist.listview',
+				'value' => 'menuaction=calendar.calendar_uilist.listview',
 				'selected' => $this->view == 'listview',
 			),
 		) as $data)
@@ -620,16 +619,16 @@ class uical
 			'text' => html::form('',
-				'','/index.php',array('menuaction'=>'calendar.uilist.listview')),
+				'','/index.php',array('menuaction'=>'calendar.calendar_uilist.listview')),
 			'no_lang' => True,
 			'link' => False,
 		);
 		// Minicalendar
 		$link = array();
 		foreach(array(
-			'day'   => 'calendar.uiviews.day',
-			'week'  => 'calendar.uiviews.week',
-			'month' => 'calendar.uiviews.month') as $view => $menuaction)
+			'day'   => 'calendar.calendar_uiviews.day',
+			'week'  => 'calendar.calendar_uiviews.week',
+			'month' => 'calendar.calendar_uiviews.month') as $view => $menuaction)
 		{
 			if ($this->view == 'planner' || $this->view == 'listview')
 			{
@@ -715,7 +714,7 @@ function load_cal(url,id) {
 		}
 		// Import & Export
 		$file[] = array(
-			'text' => lang('Export').': '.html::a_href(lang('iCal'),'calendar.uiforms.export',$this->first ? array(
+			'text' => lang('Export').': '.html::a_href(lang('iCal'),'calendar.calendar_uiforms.export',$this->first ? array(
 				'start' => $this->bo->date2string($this->first),
 				'end'   => $this->bo->date2string($this->last),
 			) : false),
@@ -723,15 +722,15 @@ function load_cal(url,id) {
 			'link' => False,
 		);
 		$file[] = array(
-			'text' => lang('Import').': '.html::a_href(lang('iCal'),'calendar.uiforms.import').
+			'text' => lang('Import').': '.html::a_href(lang('iCal'),'calendar.calendar_uiforms.import').
 				' & '.html::a_href(lang('CSV'),'/calendar/csv_import.php'),
 			'no_lang' => True,
 			'link' => False,
 		);
 /*
 		$print_functions = array(
-			'calendar.uiviews.day'	=> 'calendar.pdfcal.day',
-			'calendar.uiviews.week'	=> 'calendar.pdfcal.week',
+			'calendar.calendar_uiviews.day'	=> 'calendar.pdfcal.day',
+			'calendar.calendar_uiviews.week'	=> 'calendar.pdfcal.week',
 		);
 		if (isset($print_functions[$_GET['menuaction']]))
 		{
diff --git a/calendar/inc/class.uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php
similarity index 98%
rename from calendar/inc/class.uiforms.inc.php
rename to calendar/inc/class.calendar_uiforms.inc.php
index 6e135f5aba..8fd0eae99e 100644
--- a/calendar/inc/class.uiforms.inc.php
+++ b/calendar/inc/class.calendar_uiforms.inc.php
@@ -5,13 +5,11 @@
  * @link http://www.egroupware.org
  * @package calendar
  * @author Ralf Becker 
- * @copyright (c) 2004-7 by RalfBecker-At-outdoor-training.de
+ * @copyright (c) 2004-8 by RalfBecker-At-outdoor-training.de
  * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
  * @version $Id$
  */
 
-include_once(EGW_INCLUDE_ROOT . '/calendar/inc/class.uical.inc.php');
-
 /**
  * calendar UserInterface forms: view and edit events, freetime search
  *
@@ -24,7 +22,7 @@ include_once(EGW_INCLUDE_ROOT . '/calendar/inc/class.uical.inc.php');
  *
  * All permanent debug messages of the calendar-code should done via the debug-message method of the bocal class !!!
  */
-class uiforms extends uical
+class calendar_uiforms extends calendar_ui
 {
 	var $public_functions = array(
 		'freetimesearch'  => True,
@@ -32,12 +30,6 @@ class uiforms extends uical
 		'export' => true,
 		'import' => true,
 	);
-	/**
-	 * Reference to link-class of bocal
-	 *
-	 * @var bolink
-	 */
-	var $link;
 
 	/**
 	 * Standard durations used in edit and freetime search
@@ -63,11 +55,9 @@ class uiforms extends uical
 	/**
 	 * Constructor
 	 */
-	function uiforms()
+	function __construct()
 	{
-		$this->uical(true);	// call the parent's constructor
-
-		$this->link =& $this->bo->link;
+		parent::__construct(true);	// call the parent's constructor
 
 		for ($n=15; $n <= 8*60; $n+=($n < 60 ? 15 : ($n < 240 ? 30 : 60)))
 		{
@@ -436,7 +426,7 @@ class uiforms extends uical
 				$msg .= ($msg ? ', ' : '') .lang('Error: the entry has been updated since you opened it for editing!').'
'. lang('Copy your changes to the clipboard, %1reload the entry%2 and merge them.','',''); @@ -745,7 +735,7 @@ class uiforms extends uical elseif(egw_vfs::lock($lock_path,$preserv['lock_token'],$locktime,$lock_owner,$scope='shared',$type='write',false,false)) { // install ajax handler to unlock the entry again, if the window get's closed by the user - $GLOBALS['egw']->js->set_onunload("xajax_doXMLHTTP('calendar.uiforms.ajax_unlock',$event[id],'$preserv[lock_token]');"); + $GLOBALS['egw']->js->set_onunload("xajax_doXMLHTTP('calendar.calendar_uiforms.ajax_unlock',$event[id],'$preserv[lock_token]');"); } else { @@ -952,7 +942,7 @@ class uiforms extends uical } else { - $etpl->exec('calendar.uiforms.process_edit',$content,$sel_options,$readonlys,$preserv,$preserv['no_popup'] ? 0 : 2); + $etpl->exec('calendar.calendar_uiforms.process_edit',$content,$sel_options,$readonlys,$preserv,$preserv['no_popup'] ? 0 : 2); } } @@ -1007,7 +997,7 @@ class uiforms extends uical ); $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('Scheduling conflict'); - $etpl->exec('calendar.uiforms.process_edit',$content,false,false,array_merge($event,$preserv),$preserv['no_popup'] ? 0 : 2); + $etpl->exec('calendar.calendar_uiforms.process_edit',$content,false,false,array_merge($event,$preserv),$preserv['no_popup'] ? 0 : 2); } /** @@ -1169,7 +1159,7 @@ class uiforms extends uical // the call to set_style_by_class has to be in onload, to make sure the function and the element is already created $GLOBALS['egw']->js->set_onload("set_style_by_class('table','end_hide','visibility','".($content['duration'] && isset($sel_options['duration'][$content['duration']]) ? 'hidden' : 'visible')."');"); - $etpl->exec('calendar.uiforms.freetimesearch',$content,$sel_options,'',array( + $etpl->exec('calendar.calendar_uiforms.freetimesearch',$content,$sel_options,'',array( 'participants' => $content['participants'], 'cal_id' => $content['cal_id'], 'recur_type' => $content['recur_type'], @@ -1389,7 +1379,7 @@ class uiforms extends uical $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('iCal Export'); $etpl =& CreateObject('etemplate.etemplate','calendar.export'); - $etpl->exec('calendar.uiforms.export',$content); + $etpl->exec('calendar.calendar_uiforms.export',$content); } /** @@ -1423,6 +1413,6 @@ class uiforms extends uical $GLOBALS['egw_info']['flags']['app_header'] = lang('calendar') . ' - ' . lang('iCal Import'); $etpl =& CreateObject('etemplate.etemplate','calendar.import'); - $etpl->exec('calendar.uiforms.import',$content); + $etpl->exec('calendar.calendar_uiforms.import',$content); } } diff --git a/calendar/inc/class.uilist.inc.php b/calendar/inc/class.calendar_uilist.inc.php similarity index 95% rename from calendar/inc/class.uilist.inc.php rename to calendar/inc/class.calendar_uilist.inc.php index 9b43b241e6..061aa9def8 100644 --- a/calendar/inc/class.uilist.inc.php +++ b/calendar/inc/class.calendar_uilist.inc.php @@ -5,13 +5,11 @@ * @link http://www.egroupware.org * @package calendar * @author Ralf Becker - * @copyright (c) 2005-7 by RalfBecker-At-outdoor-training.de + * @copyright (c) 2005-8 by RalfBecker-At-outdoor-training.de * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ -include_once(EGW_INCLUDE_ROOT . '/calendar/inc/class.uical.inc.php'); - /** * Class to generate the calendar listview and the search * @@ -24,20 +22,20 @@ include_once(EGW_INCLUDE_ROOT . '/calendar/inc/class.uical.inc.php'); * * All permanent debug messages of the calendar-code should done via the debug-message method of the bocal class !!! */ -class uilist extends uical +class calendar_uilist extends calendar_ui { var $public_functions = array( 'listview' => True, ); /** * integer level or string function- or widget-name - * + * * @var mixed */ var $debug=false; /** * Filternames - * + * * @var array */ var $date_filters = array( @@ -52,22 +50,22 @@ class uilist extends uical * * @param array $set_states=null to manualy set / change one of the states, default NULL = use $_REQUEST */ - function uilist($set_states=null) + function __construct($set_states=null) { - $this->uical(true,$set_states); // call the parent's constructor + parent::__construct(true,$set_states); // call the parent's constructor $GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps']['calendar']['title'].' - '.lang('Listview'). // for a single owner we add it's name to the app-header (count(explode(',',$this->owner)) == 1 ? ': '.$this->bo->participant_name($this->owner) : ''); - + foreach($this->date_filters as $name => $label) { $this->date_filters[$name] = lang($label); } - + $this->check_owners_access(); } - + /** * Show the calendar on the home page * @@ -82,13 +80,13 @@ class uilist extends uical 'filter' => 'all', 'owner' => $this->user, 'multiple' => 0, - 'view' => $this->bo->cal_prefs['defaultcalendar'], + 'view' => $this->bo->cal_prefs['defaultcalendar'], )); $GLOBALS['egw']->session->appsession('calendar_list','calendar',''); // in case there's already something set return $this->listview(null,'',true); - } - + } + /** * Show the listview */ @@ -112,12 +110,12 @@ class uilist extends uical if (is_array($content) && $content['deleteall']) { //_debug_array($content); - + foreach($content['nm']['rows']['checked'] as $num => $id) { if ($this->bo->delete($id)) { - $msg .= lang('Event deleted'); + $msg .= lang('Event deleted'); } } } @@ -128,7 +126,7 @@ class uilist extends uical if (!is_array($content['nm'])) { $content['nm'] = array( - 'get_rows' => 'calendar.uilist.get_rows', + 'get_rows' => 'calendar.calendar_uilist.get_rows', 'filter_no_lang' => True, // I set no_lang for filter (=dont translate the options) 'no_filter2' => True, // I disable the 2. filter (params are the same as for filter) 'no_cat' => True, // I disable the cat-selectbox @@ -145,11 +143,11 @@ class uilist extends uical { $this->adjust_for_search($_REQUEST['keywords'],$content['nm']); } - return $etpl->exec('calendar.uilist.listview',$content,array( + return $etpl->exec('calendar.calendar_uilist.listview',$content,array( 'filter' => &$this->date_filters, ),$readonlys,'',$home ? -1 : 0); } - + /** * set filter for search, so that everything is shown */ @@ -174,7 +172,7 @@ class uilist extends uical /** * query calendar for nextmatch in the listview * - * @internal + * @internal * @param array &$params parameters * @param array &$rows returned rows/events * @param array &$readonlys eg. to disable buttons based on acl @@ -214,7 +212,7 @@ class uilist extends uical $this->adjust_for_search($params['search'],$params); } $GLOBALS['egw']->session->appsession('calendar_list','calendar',$params); - + $search_params = array( 'cat_id' => $this->cat_id, 'filter' => $this->filter, @@ -311,7 +309,7 @@ class uilist extends uical $rows['format'] = '16'; $dv=1; } - if ($wv&&$dv) + if ($wv&&$dv) { $rows['format'] = '64'; } diff --git a/calendar/inc/class.uiviews.inc.php b/calendar/inc/class.calendar_uiviews.inc.php similarity index 97% rename from calendar/inc/class.uiviews.inc.php rename to calendar/inc/class.calendar_uiviews.inc.php index 44386630dd..bc242a302b 100644 --- a/calendar/inc/class.uiviews.inc.php +++ b/calendar/inc/class.calendar_uiviews.inc.php @@ -10,12 +10,9 @@ * @version $Id$ */ -include_once(EGW_INCLUDE_ROOT . '/calendar/inc/class.uical.inc.php'); -require_once(EGW_INCLUDE_ROOT . '/phpgwapi/inc/class.dragdrop.inc.php'); - /** * Class to generate the calendar views and the necesary widgets - * + * * The listview is in a separate class uilist! * * The new UI, BO and SO classes have a strikt definition, in which time-zone they operate: @@ -27,7 +24,7 @@ require_once(EGW_INCLUDE_ROOT . '/phpgwapi/inc/class.dragdrop.inc.php'); * * All permanent debug messages of the calendar-code should done via the debug-message method of the bocal class !!! */ -class uiviews extends uical +class calendar_uiviews extends calendar_ui { var $public_functions = array( 'day' => True, @@ -36,32 +33,33 @@ class uiviews extends uical 'weekN' => True, 'month' => True, 'planner' => True, + 'index' => True, ); /** * integer level or string function- or widget-name - * + * * @var mixed */ var $debug=false; /** * minimum width for an event - * + * * @var int */ var $eventCol_min_width = 80; - + /** * extra rows above and below the workday - * + * * @var int */ var $extraRows = 2; /** * extra rows original (save original value even if it gets changed in the class) - * + * * @var int */ var $extraRowsOriginal; @@ -70,46 +68,46 @@ class uiviews extends uical /** * how many rows per day get displayed, gets set be the timeGridWidget - * + * * @var int */ var $rowsToDisplay; /** * height in percent of one row, gets set be the timeGridWidget - * + * * @var int */ var $rowHeight; - + /** * standard params for calling bocal::search for all views, set by the constructor - * + * * @var array */ var $search_params; - + /** * should we use a time grid, can be set for week- and month-view to false by the cal_pref no_time_grid - * + * * @var boolean */ var $use_time_grid=true; - + /** * Dragdrop Object * * @var dragdrop; */ var $dragdrop; - + /** * Can we display the whole day in a timeGrid of the size of the workday and just scroll to workday start * * @var boolean */ var $scroll_to_wdstart=false; - + /** * counter for the current whole day event of a single day * @@ -122,9 +120,9 @@ class uiviews extends uical * * @param array $set_states=null to manualy set / change one of the states, default NULL = use $_REQUEST */ - function uiviews($set_states=null) + function __construct($set_states=null) { - $this->uical(false,$set_states); // call the parent's constructor + parent::__construct(false,$set_states); // call the parent's constructor $this->extraRowsOriginal = $this->extraRows; //save original extraRows value $GLOBALS['egw_info']['flags']['nonavbar'] = False; @@ -149,7 +147,7 @@ class uiviews extends uical 'daywise' => True, ); $this->holidays = $this->bo->read_holidays($this->year); - + $this->check_owners_access(); if($GLOBALS['egw_info']['user']['preferences']['common']['enable_dragdrop']) @@ -160,7 +158,7 @@ class uiviews extends uical if(!$this->dragdrop->validateBrowser()) { $this->dragdrop = false; } } } - + /** * Show the last view or the default one, if no last */ @@ -175,10 +173,10 @@ class uiviews extends uical } // get manual to load the right page $GLOBALS['egw_info']['flags']['params']['manual'] = array('page' => 'ManualCalendar'.ucfirst($this->view)); - + $this->{$this->view}(); } - + /** * Show the calendar on the home page * @@ -191,12 +189,12 @@ class uiviews extends uical 'date' => $this->bo->date2string($this->bo->now_su), 'cat_id' => 0, 'filter' => 'all', - 'owner' => substr($this->cal_prefs['defaultcalendar'],0,7) == 'planner' && $this->cal_prefs['planner_start_with_group'] ? + 'owner' => substr($this->cal_prefs['defaultcalendar'],0,7) == 'planner' && $this->cal_prefs['planner_start_with_group'] ? $this->cal_prefs['planner_start_with_group'] : $this->user, 'multiple' => 0, - 'view' => $this->bo->cal_prefs['defaultcalendar'], + 'view' => $this->bo->cal_prefs['defaultcalendar'], )); - + if (($error = $this->check_owners_access())) { return $error; @@ -218,12 +216,12 @@ class uiviews extends uical default: case 'week': return $group_warning.$this->week(0,true); - + case 'day': return $group_warning.$this->day(true); } } - + /** * Displays the planner view * @@ -265,7 +263,7 @@ class uiviews extends uical $GLOBALS['egw_info']['flags']['app_header'] .= ': '.($this->planner_days == 1 ? lang(date('l',$this->first)).', ' : ''). $this->bo->long_date($this->first,$this->planner_days > 1 ? $this->last : 0); } - + $search_params = $this->search_params; $search_params['daywise'] = false; $search_params['start'] = $this->first; @@ -280,12 +278,12 @@ class uiviews extends uical if (!$home) { $this->do_header(); - + echo $content; } return $content; } - + /** * Displays a multiple week-view * @@ -339,7 +337,7 @@ class uiviews extends uical $week[$day_ymd] = array_shift($days); } $week_view = array( - 'menuaction' => 'calendar.uiviews.week', + 'menuaction' => 'calendar.calendar_uiviews.week', 'date' => $this->bo->date2string($week_start), ); $title = lang('Wk').' '.adodb_date('W',$week_start); @@ -350,7 +348,7 @@ class uiviews extends uical if (!$home) { $this->do_header(); - + echo $content; } @@ -378,17 +376,17 @@ class uiviews extends uical else { $last = $this->datetime->get_weekday_start($this->year,$this->month+1,$day); - } + } // now we need to calculate the end of the last day of that week // as simple $last += WEEK_s - 1; does NOT work, if daylight saving changes in that week!!! $last = $this->bo->date2array($last); - $last['day'] += 6; - $last['hour'] = 23; + $last['day'] += 6; + $last['hour'] = 23; $last['min'] = $last['sec'] = 59; unset($last['raw']); // otherwise date2ts does not calc raw new, but uses it $last = $this->bo->date2ts($last); } - + /** * Four days view, everythings done by the week-view code ... * @@ -445,7 +443,7 @@ class uiviews extends uical break; } } - $this->last = strtotime("+$days days",$this->first) - 1; + $this->last = strtotime("+$days days",$this->first) - 1; $GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang('Week').' '.adodb_date('W',$this->first).': '.$this->bo->long_date($this->first,$this->last); } @@ -473,7 +471,7 @@ class uiviews extends uical $users = $this->search_params['users']; if (!is_array($users)) $users = array($users); - + if (count($users) == 1 || count($users) > 5) // for more then 3 users, show all in one row { $content =& $this->timeGridWidget($this->tagWholeDayOnTop($this->bo->search($search_params)),$this->cal_prefs['interval']); @@ -488,11 +486,11 @@ class uiviews extends uical $content .= $this->timeGridWidget($this->tagWholeDayOnTop($this->bo->search($search_params)), count($users) * $this->cal_prefs['interval'],400 / count($users),'','',$uid); } - } + } if (!$home) { $this->do_header(); - + echo $content; } @@ -513,20 +511,20 @@ class uiviews extends uical $this->last = $this->first = $this->bo->date2ts((string)$this->date); $GLOBALS['egw_info']['flags']['app_header'] .= ': '.$this->bo->long_date($this->first,0,false,true); - + $this->use_time_grid = true; // day-view always uses a time-grid, independent what's set in the prefs! - + $this->search_params['end'] = $this->last = $this->first+DAY_s-1; - + if (!$home) { $this->do_header(); - + $users = $this->search_params['users']; if (!is_array($users)) $users = array($users); // for more then 5 users, show all in one row - if (count($users) == 1 || count($users) > 5) + if (count($users) == 1 || count($users) > 5) { $dayEvents =& $this->bo->search($this->search_params); $owner = 0; @@ -577,7 +575,7 @@ class uiviews extends uical else { $content = $this->timeGridWidget($this->bo->search($this->search_params),$this->cal_prefs['interval'],300); - + // make wz_dragdrop elements work if(is_object($this->dragdrop)) { $this->dragdrop->setJSCode(); } @@ -658,7 +656,7 @@ class uiviews extends uical } return $todo_label ? '' : false; } - + /** * Calculates the vertical position based on the time * @@ -735,10 +733,10 @@ class uiviews extends uical // determine if the browser supports scrollIntoView: IE4+, firefox1.0+ and safari2.0+ does // then show all hours in a div of the size of the workday and scroll to the workday start // still disabled, as things need to be re-aranged first, to that the column headers are not scrolled - $this->scroll_to_wdstart = false;/*$this->use_time_grid && (html::$user_agent == 'msie' || + $this->scroll_to_wdstart = false;/*$this->use_time_grid && (html::$user_agent == 'msie' || html::$user_agent == 'mozilla' && html::ua_version >= 5.0 || html::$user_agent == 'safari' && html::ua_version >= 2.0);*/ - + if ($this->scroll_to_wdstart) { $this->extraRows = 0; // no extra rows necessary @@ -752,7 +750,7 @@ class uiviews extends uical $totalDisplayMinutes = $this->wd_end - $this->wd_start; $this->rowsToDisplay = ($totalDisplayMinutes/$granularity_m)+2+2*$this->extraRows; $this->rowHeight = round(100/$this->rowsToDisplay,1); - + // ensure a minimum height of each row if ($height < ($this->rowsToDisplay+1) * 12) { @@ -767,7 +765,7 @@ class uiviews extends uical { $off = false; // Off-row means a different bgcolor $add_links = count($daysEvents) == 1; - + // the hour rows for($t = $this->scroll_to_wdstart ? 0 : $this->wd_start,$i = 1+$this->extraRows; $t <= $this->wd_end || $this->scroll_to_wdstart && $t < 24*60; @@ -814,12 +812,12 @@ class uiviews extends uical { // Lars Kneschke 2005-08-28 // why do we use a div in a div which has the same height and width??? - // To make IE6 happy!!! Without the second div you can't use + // To make IE6 happy!!! Without the second div you can't use // style="left: 50px; right: 0px;" //$html .= '
'."\n"; // Ralf Becker 2006-06-19 - // Lars original typo "width=100%; height: 100%;" is important ;-) + // Lars original typo "width=100%; height: 100%;" is important ;-) // means you width: 100% does NOT work, you need no width! $html .= '
'."\n"; } @@ -845,7 +843,7 @@ class uiviews extends uical $html .= $indent."\t
\n"; // calDayCols } $html .= $indent."
\n"; // calTimeGrid - + if ($this->scroll_to_wdstart) { $html .= "