Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117753936
kolab_sync_data.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
36 KB
Referenced Files
None
Subscribers
None
kolab_sync_data.php
View Options
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
abstract
class
kolab_sync_data
implements
Syncroton_Data_IData
{
/**
* ActiveSync protocol version
*
* @var int
*/
protected
$asversion
=
0
;
/**
* information about the current device
*
* @var Syncroton_Model_IDevice
*/
protected
$device
;
/**
* timestamp to use for all sync requests
*
* @var DateTime
*/
protected
$syncTimeStamp
;
/**
* name of model to use
*
* @var string
*/
protected
$modelName
;
/**
* type of the default folder
*
* @var int
*/
protected
$defaultFolderType
;
/**
* default container for new entries
*
* @var string
*/
protected
$defaultFolder
;
/**
* type of user created folders
*
* @var int
*/
protected
$folderType
;
/**
* Default namespace
*
* @var string
*/
protected
$defaultNS
=
'Calendar'
;
/**
* field to sort search results by
*
* @var string
*/
protected
$sortField
;
/**
* Internal cache for kolab_storage folder objects
*
* @var array
*/
protected
$folders
=
array
();
/**
* Timezone
*
* @var string
*/
protected
$timezone
;
const
RESULT_OBJECT
=
0
;
const
RESULT_UID
=
1
;
const
RESULT_COUNT
=
2
;
/**
* Recurrence types
*/
const
RECUR_TYPE_DAILY
=
0
;
// Recurs daily.
const
RECUR_TYPE_WEEKLY
=
1
;
// Recurs weekly
const
RECUR_TYPE_MONTHLY
=
2
;
// Recurs monthly
const
RECUR_TYPE_MONTHLY_DAYN
=
3
;
// Recurs monthly on the nth day
const
RECUR_TYPE_YEARLY
=
5
;
// Recurs yearly
const
RECUR_TYPE_YEARLY_DAYN
=
6
;
// Recurs yearly on the nth day
/**
* Day of week constants
*/
const
RECUR_DOW_SUNDAY
=
1
;
const
RECUR_DOW_MONDAY
=
2
;
const
RECUR_DOW_TUESDAY
=
4
;
const
RECUR_DOW_WEDNESDAY
=
8
;
const
RECUR_DOW_THURSDAY
=
16
;
const
RECUR_DOW_FRIDAY
=
32
;
const
RECUR_DOW_SATURDAY
=
64
;
const
RECUR_DOW_LAST
=
127
;
// The last day of the month. Used as a special value in monthly or yearly recurrences.
/**
* Mapping of recurrence types
*
* @var array
*/
protected
$recurTypeMap
=
array
(
'DAILY'
=>
self
::
RECUR_TYPE_DAILY
,
'WEEKLY'
=>
self
::
RECUR_TYPE_WEEKLY
,
'MONTHLY'
=>
self
::
RECUR_TYPE_MONTHLY
,
'MONTHLY'
=>
self
::
RECUR_TYPE_MONTHLY_DAYN
,
'YEARLY'
=>
self
::
RECUR_TYPE_YEARLY
,
'YEARLY'
=>
self
::
RECUR_TYPE_YEARLY_DAYN
,
);
/**
* Mapping of weekdays
* NOTE: ActiveSync uses a bitmask
*
* @var array
*/
protected
$recurDayMap
=
array
(
'SU'
=>
self
::
RECUR_DOW_SUNDAY
,
'MO'
=>
self
::
RECUR_DOW_MONDAY
,
'TU'
=>
self
::
RECUR_DOW_TUESDAY
,
'WE'
=>
self
::
RECUR_DOW_WEDNESDAY
,
'TH'
=>
self
::
RECUR_DOW_THURSDAY
,
'FR'
=>
self
::
RECUR_DOW_FRIDAY
,
'SA'
=>
self
::
RECUR_DOW_SATURDAY
,
);
/**
* the constructor
*
* @param Syncroton_Model_IDevice $device
* @param DateTime $syncTimeStamp
*/
public
function
__construct
(
Syncroton_Model_IDevice
$device
,
DateTime
$syncTimeStamp
)
{
$this
->
backend
=
kolab_sync_backend
::
get_instance
();
$this
->
device
=
$device
;
$this
->
asversion
=
floatval
(
$device
->
acsversion
);
$this
->
syncTimeStamp
=
$syncTimeStamp
;
$this
->
defaultRootFolder
=
$this
->
defaultFolder
.
'::Syncroton'
;
// set internal timezone of kolab_format to user timezone
try
{
$this
->
timezone
=
rcube
::
get_instance
()->
config
->
get
(
'timezone'
,
'GMT'
);
kolab_format
::
$timezone
=
new
DateTimeZone
(
$this
->
timezone
);
}
catch
(
Exception
$e
)
{
//rcube::raise_error($e, true);
$this
->
timezone
=
'GMT'
;
kolab_format
::
$timezone
=
new
DateTimeZone
(
'GMT'
);
}
}
/**
* return list of supported folders for this backend
*
* @return array
*/
public
function
getAllFolders
()
{
$list
=
array
();
// device supports multiple folders ?
if
(
in_array
(
strtolower
(
$this
->
device
->
devicetype
),
array
(
'iphone'
,
'ipad'
,
'thundertine'
,
'windowsphone'
)))
{
// get the folders the user has access to
$list
=
$this
->
backend
->
folders_list
(
$this
->
device
->
deviceid
,
$this
->
modelName
);
}
if
(
$default
=
$this
->
getDefaultFolder
())
{
$list
=
array
(
$default
[
'folderid'
]
=>
$default
);
}
foreach
(
$list
as
$idx
=>
$folder
)
{
$list
[
$idx
]
=
new
Syncroton_Model_Folder
(
$folder
);
}
return
$list
;
}
/**
* Returns default folder for current class type.
*/
protected
function
getDefaultFolder
()
{
// Check if there's any folder configured for sync
$folders
=
$this
->
backend
->
folders_list
(
$this
->
device
->
deviceid
,
$this
->
modelName
);
if
(
empty
(
$folders
))
{
return
null
;
}
foreach
(
$folders
as
$folder
)
{
if
(
$folder
[
'type'
]
==
$this
->
defaultFolderType
)
{
$default
=
$folder
;
break
;
}
}
// Return first on the list if there's no default
if
(
empty
(
$default
))
{
$default
=
$folders
[
key
(
$folders
)];
}
// Remember real folder ID and set ID/name to root folder
$default
[
'realid'
]
=
$default
[
'folderid'
];
$default
[
'folderid'
]
=
$this
->
defaultRootFolder
;
$default
[
'displayname'
]
=
$this
->
defaultFolder
;
return
$default
;
}
/**
* Creates a folder
*/
public
function
createFolder
(
Syncroton_Model_IFolder
$folder
)
{
$parentid
=
$folder
->
parentid
;
$type
=
$folder
->
type
;
$display_name
=
$folder
->
displayname
;
if
(
$parentid
)
{
$parent
=
$this
->
backend
->
folder_id2name
(
$parentid
,
$this
->
device
->
deviceid
);
}
$name
=
rcube_charset
::
convert
(
$display_name
,
kolab_sync
::
CHARSET
,
'UTF7-IMAP'
);
if
(
$parent
!==
null
)
{
$rcube
=
rcube
::
get_instance
();
$storage
=
$rcube
->
get_storage
();
$delim
=
$storage
->
get_hierarchy_delimiter
();
$name
=
$parent
.
$delim
.
$name
;
}
// Create IMAP folder
$result
=
$this
->
backend
->
folder_create
(
$name
,
$type
,
$this
->
device
->
deviceid
);
if
(
$result
)
{
$folder
->
folderid
=
$this
->
backend
->
folder_id
(
$name
);
return
$folder
;
}
// @TODO: throw exception
}
/**
* Updates a folder
*/
public
function
updateFolder
(
Syncroton_Model_IFolder
$folder
)
{
$parentid
=
$folder
->
parentid
;
$type
=
$folder
->
type
;
$display_name
=
$folder
->
displayname
;
$old_name
=
$this
->
backend
->
folder_id2name
(
$folder
->
folderid
,
$this
->
device
->
deviceid
);
if
(
$parentid
)
{
$parent
=
$this
->
backend
->
folder_id2name
(
$parentid
,
$this
->
device
->
deviceid
);
}
$name
=
rcube_charset
::
convert
(
$display_name
,
kolab_sync
::
CHARSET
,
'UTF7-IMAP'
);
if
(
$parent
!==
null
)
{
$rcube
=
rcube
::
get_instance
();
$storage
=
$rcube
->
get_storage
();
$delim
=
$storage
->
get_hierarchy_delimiter
();
$name
=
$parent
.
$delim
.
$name
;
}
// Rename/move IMAP folder
if
(
$name
==
$old_name
)
{
$result
=
true
;
// @TODO: folder type change?
}
else
{
$result
=
$this
->
backend
->
folder_rename
(
$old_name
,
$name
,
$type
,
$this
->
device
->
deviceid
);
}
if
(
$result
)
{
$folder
->
folderid
=
$this
->
backend
->
folder_id
(
$name
);
return
$folder
;
}
// @TODO: throw exception
}
/**
* Deletes a folder
*/
public
function
deleteFolder
(
$folder
)
{
if
(
$folder
instanceof
Syncroton_Model_IFolder
)
{
$folder
=
$folder
->
folderid
;
}
$name
=
$this
->
backend
->
folder_id2name
(
$folder
,
$this
->
device
->
deviceid
);
// @TODO: throw exception
return
$this
->
backend
->
folder_delete
(
$name
,
$this
->
device
->
deviceid
);
}
public
function
moveItem
(
$srcFolderId
,
$serverId
,
$dstFolderId
)
{
$item
=
$this
->
getObject
(
$srcFolderId
,
$serverId
,
$folder
);
if
(
$item
&&
$folder
)
{
$dstname
=
$this
->
backend
->
folder_id2name
(
$dstFolderId
,
$this
->
device
->
deviceid
);
if
(
$dstname
===
null
)
{
return
;
}
if
(!
$folder
->
move
(
$serverId
,
$dstname
))
{
return
;
}
}
return
$item
[
'uid'
];
}
/**
* add entry from xml data
*
* @param string $folderId
* @param Syncroton_Model_IEntry $entry
*
* @return array
*/
public
function
createEntry
(
$folderId
,
Syncroton_Model_IEntry
$entry
)
{
$entry
=
$this
->
toKolab
(
$entry
,
$folderId
);
$entry
=
$this
->
createObject
(
$folderId
,
$entry
);
return
$entry
[
'uid'
];
}
/**
* update existing entry
*
* @param string $folderId
* @param string $serverId
* @param SimpleXMLElement $entry
* @return array
*/
public
function
updateEntry
(
$folderId
,
$serverId
,
Syncroton_Model_IEntry
$entry
)
{
$oldEntry
=
$this
->
getObject
(
$folderId
,
$serverId
);
if
(
empty
(
$oldEntry
))
{
throw
new
Syncroton_Exception_NotFound
(
'id not found'
);
}
$entry
=
$this
->
toKolab
(
$entry
,
$folderId
,
$oldEntry
);
$entry
=
$this
->
updateObject
(
$folderId
,
$serverId
,
$entry
);
return
$entry
[
'uid'
];
}
/**
* delete entry
*
* @param string $folderId
* @param string $serverId
* @param array $collectionData
*/
public
function
deleteEntry
(
$folderId
,
$serverId
,
$collectionData
)
{
$this
->
deleteObject
(
$folderId
,
$serverId
);
}
public
function
getFileReference
(
$fileReference
)
{
}
/**
* Search for existing entries
*
* @param string $folderid
* @param array $filter
* @param int $result_type Type of the result (see RESULT_* constants)
*
* @return array|int Search result as count or array of uids/objects
*/
public
function
search
(
$folderid
,
$filter
=
array
(),
$result_type
=
self
::
RESULT_UID
)
{
if
(
$folderid
==
$this
->
defaultRootFolder
)
{
$folders
=
$this
->
backend
->
folders_list
(
$this
->
device
->
deviceid
,
$this
->
modelName
);
$folders
=
array_keys
(
$folders
);
}
else
{
$folders
=
array
(
$folderid
);
}
// there's a PHP Warning from kolab_storage if $filter isn't an array
if
(
empty
(
$filter
))
{
$filter
=
array
();
}
$result
=
$result_type
==
self
::
RESULT_COUNT
?
0
:
array
();
foreach
(
$folders
as
$folderid
)
{
$foldername
=
$this
->
backend
->
folder_id2name
(
$folderid
,
$this
->
device
->
deviceid
);
if
(
$foldername
===
null
)
{
continue
;
}
$folder
=
$this
->
getFolderObject
(
$foldername
);
if
(!
$folder
)
{
continue
;
}
switch
(
$result_type
)
{
case
self
::
RESULT_COUNT
:
$result
+=
(
int
)
$folder
->
count
(
$filter
);
break
;
case
self
::
RESULT_UID
:
if
(
$uids
=
$folder
->
get_uids
(
$filter
))
{
$result
=
array_merge
(
$result
,
$uids
);
}
break
;
case
self
::
RESULT_OBJECT
:
default
:
if
(
$objects
=
$folder
->
select
(
$filter
))
{
$result
=
array_merge
(
$result
,
$objects
);
}
}
}
return
$result
;
}
/**
* Returns filter query array according to specified ActiveSync FilterType
*
* @param int $filter_type Filter type
*
* @param array Filter query
*/
protected
function
filter
(
$filter_type
=
0
)
{
/*
From Syncroton_Command_Sync:
const FILTER_NOTHING = 0;
const FILTER_1_DAY_BACK = 1;
const FILTER_3_DAYS_BACK = 2;
const FILTER_1_WEEK_BACK = 3;
const FILTER_2_WEEKS_BACK = 4;
const FILTER_1_MONTH_BACK = 5;
const FILTER_3_MONTHS_BACK = 6;
const FILTER_6_MONTHS_BACK = 7;
const FILTER_INCOMPLETE = 8;
Doc: MS-ASCMD 2.2.3.64.2 FilterType (Sync)
*/
// overwrite by child class according to specified type
return
array
();
}
/**
* get all entries changed between to dates
*
* @param string $folderId
* @param DateTime $start
* @param DateTime $end
* @return array
*/
public
function
getChangedEntries
(
$folderId
,
DateTime
$start
,
DateTime
$end
=
null
)
{
$filter
=
array
();
$filter
[]
=
array
(
'changed'
,
'>'
,
$start
);
if
(
$endTimeStamp
)
{
$filter
[]
=
array
(
'changed'
,
'<='
,
$end
);
}
$result
=
$this
->
search
(
$folderId
,
$filter
,
self
::
RESULT_UID
);
return
$result
;
}
/**
* get count of entries changed between two dates
*
* @param string $folderId
* @param DateTime $start
* @param DateTime $end
*
* @return int
*/
public
function
getChangedEntriesCount
(
$folderId
,
DateTime
$start
,
DateTime
$end
=
null
)
{
$filter
=
array
();
$filter
[]
=
array
(
'changed'
,
'>'
,
$start
);
if
(
$endTimeStamp
)
{
$filter
[]
=
array
(
'changed'
,
'<='
,
$end
);
}
$result
=
$this
->
search
(
$folderId
,
$filter
,
self
::
RESULT_COUNT
);
return
$result
;
}
/**
* get id's of all entries available on the server
*
* @param string $folderId
* @param int $filterType
*
* @return array
*/
public
function
getServerEntries
(
$folder_id
,
$filter_type
)
{
// @TODO: sorting? 'sort' => $this->sortField
$filter
=
$this
->
filter
(
$filter_type
);
$result
=
$this
->
search
(
$folder_id
,
$filter
,
self
::
RESULT_UID
);
return
$result
;
}
/**
* get count of all entries available on the server
*
* @param string $folderId
* @param int $filterType
*
* @return int
*/
public
function
getServerEntriesCount
(
$folder_id
,
$filter_type
)
{
$filter
=
$this
->
filter
(
$filter_type
);
$result
=
$this
->
search
(
$folder_id
,
$filter
,
self
::
RESULT_COUNT
);
return
$result
;
}
public
function
getCountOfChanges
(
Syncroton_Backend_IContent
$contentBackend
,
Syncroton_Model_IFolder
$folder
,
Syncroton_Model_ISyncState
$syncState
)
{
$allClientEntries
=
$contentBackend
->
getFolderState
(
$this
->
device
,
$folder
);
// @TODO: Consider looping over all folders here, not in getServerEntries() and
// getChangesEntriesCount(). This way we could break the loop and not check all folders (see @TODO below)
// or at least skip redundant cache sync of the same folder
$allServerEntries
=
$this
->
getServerEntries
(
$folder
->
folderid
,
$folder
->
lastfiltertype
);
$addedEntries
=
array_diff
(
$allServerEntries
,
$allClientEntries
);
$deletedEntries
=
array_diff
(
$allClientEntries
,
$allServerEntries
);
// @TODO: Count is needed only for GetItemEstimate command
// in Ping command we need only information that anything is changed/added/deleted
// so in case when count($addedEntries) + count($deletedEntries) != 0 we don't need
// to count changed entries here. We could also revert the order in such case
// and execute getChangedEntriesCount() before
$changedEntries
=
$this
->
getChangedEntriesCount
(
$folder
->
folderid
,
$syncState
->
lastsync
);
return
count
(
$addedEntries
)
+
count
(
$deletedEntries
)
+
$changedEntries
;
}
/**
* Fetches the entry from the backend
*/
protected
function
getObject
(
$folderid
,
$entryid
,
&
$folder
=
null
)
{
if
(
$folderid
instanceof
Syncroton_Model_IFolder
)
{
$folderid
=
$folderid
->
folderid
;
}
if
(
$folderid
==
$this
->
defaultRootFolder
)
{
$folders
=
$this
->
backend
->
folders_list
(
$this
->
device
->
deviceid
,
$this
->
modelName
);
$folders
=
array_keys
(
$folders
);
}
else
{
$folders
=
array
(
$folderid
);
}
foreach
(
$folders
as
$folderid
)
{
$foldername
=
$this
->
backend
->
folder_id2name
(
$folderid
,
$this
->
device
->
deviceid
);
if
(
$foldername
===
null
)
{
continue
;
}
$folder
=
$this
->
getFolderObject
(
$foldername
);
if
(
$object
=
$folder
->
get_object
(
$entryid
))
{
$object
[
'_folderid'
]
=
$folderid
;
return
$object
;
}
}
}
/**
* Saves the entry on the backend
*/
protected
function
createObject
(
$folderid
,
$data
)
{
if
(
$folderid
==
$this
->
defaultRootFolder
)
{
$default
=
$this
->
getDefaultFolder
();
$folderid
=
isset
(
$default
[
'realid'
])
?
$default
[
'realid'
]
:
$default
[
'folderid'
];
}
$foldername
=
$this
->
backend
->
folder_id2name
(
$folderid
,
$this
->
device
->
deviceid
);
$folder
=
$this
->
getFolderObject
(
$foldername
);
if
(
$folder
->
save
(
$data
))
{
return
$data
;
}
}
/**
* Updates the entry on the backend
*/
protected
function
updateObject
(
$folderid
,
$entryid
,
$data
)
{
$object
=
$this
->
getObject
(
$folderid
,
$entryid
);
if
(
$object
)
{
$folder
=
$this
->
getFolderObject
(
$object
[
'_mailbox'
]);
if
(
$folder
->
save
(
$data
))
{
return
$data
;
}
}
}
/**
* Removes the entry from the backend
*/
protected
function
deleteObject
(
$folderid
,
$entryid
)
{
$object
=
$this
->
getObject
(
$folderid
,
$entryid
);
if
(
$object
)
{
$folder
=
$this
->
getFolderObject
(
$object
[
'_mailbox'
]);
return
$folder
->
delete
(
$entryid
);
}
}
/**
* Returns Folder object (uses internal cache)
*
* @param string $name Folder name (UTF7-IMAP)
*
* @return kolab_storage_folder Folder object
*/
protected
function
getFolderObject
(
$name
)
{
if
(!
isset
(
$this
->
folders
[
$name
]))
{
$this
->
folders
[
$name
]
=
kolab_storage
::
get_folder
(
$name
);
}
return
$this
->
folders
[
$name
];
}
/**
* Returns ActiveSync settings of specified folder
*
* @param string $name Folder name (UTF7-IMAP)
*
* @return array Folder settings
*/
protected
function
getFolderConfig
(
$name
)
{
$metadata
=
$this
->
backend
->
folder_meta
();
$deviceid
=
$this
->
device
->
deviceid
;
$config
=
$metadata
[
$name
][
'FOLDER'
][
$deviceid
];
return
array
(
'ALARMS'
=>
$config
[
'S'
]
==
2
,
);
}
/**
* Convert contact from xml to kolab format
*
* @param Syncroton_Model_IEntry $data Contact data
* @param string $folderId Folder identifier
* @param array $entry Old Contact data for merge
*
* @return array
*/
abstract
public
function
toKolab
(
Syncroton_Model_IEntry
$data
,
$folderId
,
$entry
=
null
);
/**
* Removes control chars from string which are not allowed in ActiveSync
*
* @param string $value Text
*
* @return string Text
* @deprecated
*/
public
static
function
quote
(
$value
)
{
if
(
$value
&&
!
ctype_print
(
$value
))
{
$value
=
preg_replace
(
'/[
\x
00-
\x
08
\x
0B
\x
0C
\x
0E-
\x
1F]/'
,
null
,
$value
);
}
return
$value
;
}
/**
* Extracts data from kolab data array
*/
protected
function
getKolabDataItem
(
$data
,
$name
)
{
$name_items
=
explode
(
'.'
,
$name
);
$count
=
count
(
$name_items
);
// multi-level array (e.g. address, phone)
if
(
$count
==
3
)
{
$name
=
$name_items
[
0
];
$type
=
$name_items
[
1
];
$key_name
=
$name_items
[
2
];
if
(!
empty
(
$data
[
$name
])
&&
is_array
(
$data
[
$name
]))
{
foreach
(
$data
[
$name
]
as
$element
)
{
if
(
$element
[
'type'
]
==
$type
)
{
return
$element
[
$key_name
];
}
}
}
return
null
;
}
/*
// hash array e.g. organizer
else if ($count == 2) {
$name = $name_items[0];
$type = $name_items[1];
$key_name = $name_items[2];
if (!empty($data[$name]) && is_array($data[$name])) {
foreach ($data[$name] as $element) {
if ($element['type'] == $type) {
return $element[$key_name];
}
}
}
return null;
}
*/
$name_items
=
explode
(
':'
,
$name
);
$name
=
$name_items
[
0
];
if
(
empty
(
$data
[
$name
]))
{
return
null
;
}
// simple array (e.g. email)
if
(
count
(
$name_items
)
==
2
)
{
return
$data
[
$name
][
$name_items
[
1
]];
}
return
$data
[
$name
];
}
/**
* Saves data in kolab data array
*/
protected
function
setKolabDataItem
(&
$data
,
$name
,
$value
)
{
if
(
empty
(
$value
))
{
return
$this
->
unsetKolabDataItem
(
$data
,
$name
);
}
$name_items
=
explode
(
'.'
,
$name
);
// multi-level array (e.g. address, phone)
if
(
count
(
$name_items
)
==
3
)
{
$name
=
$name_items
[
0
];
$type
=
$name_items
[
1
];
$key_name
=
$name_items
[
2
];
if
(!
isset
(
$data
[
$name
]))
{
$data
[
$name
]
=
array
();
}
foreach
(
$data
[
$name
]
as
$idx
=>
$element
)
{
if
(
$element
[
'type'
]
==
$type
)
{
$found
=
$idx
;
break
;
}
}
if
(!
isset
(
$found
))
{
$data
[
$name
]
=
array_values
(
$data
[
$name
]);
$found
=
count
(
$data
[
$name
]);
$data
[
$name
][
$found
]
=
array
(
'type'
=>
$type
);
}
$data
[
$name
][
$found
][
$key_name
]
=
$value
;
return
;
}
$name_items
=
explode
(
':'
,
$name
);
$name
=
$name_items
[
0
];
// simple array (e.g. email)
if
(
count
(
$name_items
)
==
2
)
{
$data
[
$name
][
$name_items
[
1
]]
=
$value
;
return
;
}
$data
[
$name
]
=
$value
;
}
/**
* Unsets data item in kolab data array
*/
protected
function
unsetKolabDataItem
(&
$data
,
$name
)
{
$name_items
=
explode
(
'.'
,
$name
);
// multi-level array (e.g. address, phone)
if
(
count
(
$name_items
)
==
3
)
{
$name
=
$name_items
[
0
];
$type
=
$name_items
[
1
];
$key_name
=
$name_items
[
2
];
if
(!
isset
(
$data
[
$name
]))
{
return
;
}
foreach
(
$data
[
$name
]
as
$idx
=>
$element
)
{
if
(
$element
[
'type'
]
==
$type
)
{
$found
=
$idx
;
break
;
}
}
if
(!
isset
(
$found
))
{
return
;
}
unset
(
$data
[
$name
][
$found
][
$key_name
]);
// if there's only one element and it's 'type', remove it
if
(
count
(
$data
[
$name
][
$found
])
==
1
&&
isset
(
$data
[
$name
][
$found
][
'type'
]))
{
unset
(
$data
[
$name
][
$found
][
'type'
]);
}
if
(
empty
(
$data
[
$name
][
$found
]))
{
unset
(
$data
[
$name
][
$found
]);
}
if
(
empty
(
$data
[
$name
]))
{
unset
(
$data
[
$name
]);
}
return
;
}
$name_items
=
explode
(
':'
,
$name
);
$name
=
$name_items
[
0
];
// simple array (e.g. email)
if
(
count
(
$name_items
)
==
2
)
{
unset
(
$data
[
$name
][
$name_items
[
1
]]);
if
(
empty
(
$data
[
$name
]))
{
unset
(
$data
[
$name
]);
}
return
;
}
unset
(
$data
[
$name
]);
}
/**
* Setter for Body attribute according to client version
*
* @param string $value Body
* @param array $param Body parameters
*
* @reurn Syncroton_Model_EmailBody Body element
*/
protected
function
setBody
(
$value
,
$params
=
array
())
{
if
(
empty
(
$value
))
{
return
;
}
$params
[
'Data'
]
=
$value
;
if
(!
isset
(
$params
[
'Type'
]))
{
$params
[
'Type'
]
=
1
;
}
return
new
Syncroton_Model_EmailBody
(
$params
);
/*
// strip off any non printable control characters
$value = self::quote($value);
if ($this->asversion >= 12) {
$body = $domParent->appendChild(new DOMElement('Body', null, 'uri:AirSyncBase'));
$body->appendChild(new DOMElement('Type', 1, 'uri:AirSyncBase'));
$dataTag = new DOMElement('Data', null, 'uri:AirSyncBase');
// ... append it to parent node aka append it to the document ...
$body->appendChild($dataTag);
// ... and now add the content (DomText takes care of special chars)
$dataTag->appendChild(new DOMText($value));
} else {
// create a new DOMElement ...
$node = new DOMElement('Body', null, 'uri:' . $this->defaultNS);
// ... append it to parent node aka append it to the document ...
$domParent->appendChild($node);
// ... and now add the content (DomText takes care of special chars)
$node->appendChild(new DOMText($value));
}
*/
}
/**
* Getter for Body attribute value according to client version
*
* @param mixed $data Data element
*/
protected
function
getBody
(
$data
)
{
if
(
$data
&&
$data
->
Data
)
{
return
$data
->
Data
;
}
/*
if ($this->asversion >= 12) {
$airSyncBase = $data->children('uri:AirSyncBase');
if (isset($airSyncBase->Body)) {
return (string)$airSyncBase->Body->Data;
}
}
else if (isset($classData->Body)) {
return (string) $classData->Body;
}
*/
}
/**
* Converts PHP DateTime, date (YYYY-MM-DD) or unixtimestamp into PHP DateTime in UTC
*
* @param DateTime|int|string $date Unix timestamp, date (YYYY-MM-DD) or PHP DateTime object
*
* @return DateTime Datetime object
*/
protected
static
function
date_from_kolab
(
$date
)
{
if
(!
empty
(
$date
))
{
if
(
is_numeric
(
$date
))
{
$date
=
new
DateTime
(
'@'
.
$date
);
}
else
if
(
is_string
(
$date
))
{
$date
=
new
DateTime
(
$date
,
new
DateTimeZone
(
'UTC'
));
}
else
{
$date
=
clone
$date
;
$tz
=
$date
->
getTimezone
();
$tz_name
=
$tz
->
getName
();
// convert to UTC if needed
if
(
$tz_name
!=
'UTC'
)
{
$date
->
setTimezone
(
new
DateTimeZone
(
'UTC'
));
}
}
return
$date
;
}
}
/**
* Convert Kolab event/task recurrence into ActiveSync
*/
protected
function
recurrence_from_kolab
(
$data
,
$type
=
'Event'
)
{
if
(
empty
(
$data
[
'recurrence'
]))
{
return
null
;
}
$recurrence
=
array
();
$r
=
$data
[
'recurrence'
];
// required fields
switch
(
$r
[
'FREQ'
])
{
case
'DAILY'
:
$recurrence
[
'Type'
]
=
self
::
RECUR_TYPE_DAILY
;
break
;
case
'WEEKLY'
:
$recurrence
[
'Type'
]
=
self
::
RECUR_TYPE_WEEKLY
;
$recurrence
[
'DayOfWeek'
]
=
$this
->
day2bitmask
(
$r
[
'BYDAY'
]);
break
;
case
'MONTHLY'
:
if
(!
empty
(
$r
[
'BYMONTHDAY'
]))
{
// @TODO: ActiveSync doesn't support multi-valued month days,
// should we replicate the recurrence element for each day of month?
$month_day
=
array_shift
(
explode
(
','
,
$r
[
'BYMONTHDAY'
]));
$recurrence
[
'Type'
]
=
self
::
RECUR_TYPE_MONTHLY
;
$recurrence
[
'DayOfMonth'
]
=
$month_day
;
}
else
{
$week
=
(
int
)
substr
(
$r
[
'BYDAY'
],
0
,
-
2
);
$week
=
(
$week
==
-
1
)
?
5
:
$week
;
$day
=
substr
(
$r
[
'BYDAY'
],
-
2
);
$recurrence
[
'Type'
]
=
self
::
RECUR_TYPE_MONTHLY_DAYN
;
$recurrence
[
'WeekOfMonth'
]
=
$week
;
$recurrence
[
'DayOfWeek'
]
=
$this
->
day2bitmask
(
$day
);
}
break
;
case
'YEARLY'
:
// @TODO: ActiveSync doesn't support multi-valued months,
// should we replicate the recurrence element for each month?
$month
=
array_shift
(
explode
(
','
,
$r
[
'BYMONTH'
]));
if
(!
empty
(
$r
[
'BYDAY'
]))
{
$week
=
(
int
)
substr
(
$r
[
'BYDAY'
],
0
,
-
2
);
$week
=
(
$week
==
-
1
)
?
5
:
$week
;
$day
=
substr
(
$r
[
'BYDAY'
],
-
2
);
$recurrence
[
'Type'
]
=
self
::
RECUR_TYPE_YEARLY_DAYN
;
$recurrence
[
'WeekOfMonth'
]
=
$week
;
$recurrence
[
'DayOfWeek'
]
=
$this
->
day2bitmask
(
$day
);
$recurrence
[
'MonthOfYear'
]
=
$month
;
}
else
if
(!
empty
(
$r
[
'BYMONTHDAY'
]))
{
// @TODO: ActiveSync doesn't support multi-valued month days,
// should we replicate the recurrence element for each day of month?
$month_day
=
array_shift
(
explode
(
','
,
$r
[
'BYMONTHDAY'
]));
$recurrence
[
'Type'
]
=
self
::
RECUR_TYPE_YEARLY
;
$recurrence
[
'DayOfMonth'
]
=
$month_day
;
$recurrence
[
'MonthOfYear'
]
=
$month
;
}
else
{
$recurrence
[
'Type'
]
=
self
::
RECUR_TYPE_YEARLY
;
$recurrence
[
'MonthOfYear'
]
=
$month
;
}
break
;
}
// required field
$recurrence
[
'Interval'
]
=
$r
[
'INTERVAL'
]
?
$r
[
'INTERVAL'
]
:
1
;
if
(!
empty
(
$r
[
'UNTIL'
]))
{
$recurrence
[
'Until'
]
=
$this
->
date_from_kolab
(
$r
[
'UNTIL'
]);
}
else
if
(!
empty
(
$r
[
'COUNT'
]))
{
$recurrence
[
'Occurrences'
]
=
$r
[
'COUNT'
];
}
$class
=
'Syncroton_Model_'
.
$type
.
'Recurrence'
;
return
new
$class
(
$recurrence
);
}
/**
* Convert ActiveSync event/task recurrence into Kolab
*/
protected
function
recurrence_to_kolab
(
$data
,
$timezone
=
null
)
{
if
(!
isset
(
$data
->
Recurrence
)
||
isset
(
$data
->
Recurrence
->
Type
))
{
return
null
;
}
$type
=
$data
->
Recurrence
->
Type
;
$rrule
[
'FREQ'
]
=
array_search
(
$type
,
$this
->
recurTypeMap
);
switch
(
$type
)
{
case
self
::
RECUR_TYPE_DAILY
:
break
;
case
self
::
RECUR_TYPE_WEEKLY
:
$rrule
[
'BYDAY'
]
=
$this
->
bitmask2day
(
$data
->
Recurrence
->
DayOfWeek
);
break
;
case
self
::
RECUR_TYPE_MONTHLY
:
$rrule
[
'BYMONTHDAY'
]
=
$data
->
Recurrence
->
DayOfMonth
;
break
;
case
self
::
RECUR_TYPE_MONTHLY_DAYN
:
$week
=
$data
->
Recurrence
->
WeekOfMonth
;
$day
=
$data
->
Recurrence
->
DayOfWeek
;
$byDay
=
$week
==
5
?
-
1
:
$week
;
$byDay
.=
$this
->
bitmask2day
(
$day
);
$rrule
[
'BYDAY'
]
=
$byDay
;
break
;
case
self
::
RECUR_TYPE_YEARLY
:
$rrule
[
'BYMONTH'
]
=
$data
->
Recurrence
->
MonthOfYear
;
$rrule
[
'BYMONTHDAY'
]
=
$data
->
Recurrence
->
DayOfMonth
;
break
;
case
self
::
RECUR_TYPE_YEARLY_DAYN
:
$rrule
[
'BYMONTH'
]
=
$data
->
Recurrence
->
MonthOfYear
;
$week
=
$data
->
Recurrence
->
WeekOfMonth
;
$day
=
$data
->
Recurrence
->
DayOfWeek
;
$byDay
=
$week
==
5
?
-
1
:
$week
;
$byDay
.=
$this
->
bitmask2day
(
$day
);
$rrule
[
'BYDAY'
]
=
$byDay
;
break
;
}
$rrule
[
'INTERVAL'
]
=
isset
(
$data
->
Recurrence
->
Interval
)
?
$data
->
Recurrence
->
Interval
:
1
;
if
(
isset
(
$data
->
Recurrence
->
Until
))
{
if
(
$timezone
)
{
$data
->
Recurrence
->
Until
->
setTimezone
(
$timezone
);
}
$rrule
[
'UNTIL'
]
=
$data
->
Recurrence
->
Until
;
}
else
if
(!
empty
(
$data
->
Recurrence
->
Occurrences
))
{
$rrule
[
'COUNT'
]
=
$data
->
Recurrence
->
Occurrences
;
}
return
$rrule
;
}
/**
* Convert Kolab event recurrence exceptions into ActiveSync
*/
protected
function
exceptions_from_kolab
(
$data
)
{
/*
// handle exceptions of repeating events
if($data->exdate && $data->exdate->count() > 0) {
$exceptionsTag = $_domParent->appendChild(new DOMElement('Exceptions', null, 'uri:Calendar'));
foreach ($data->exdate as $exception) {
$exceptionTag = $exceptionsTag->appendChild(new DOMElement('Exception', null, 'uri:Calendar'));
$exceptionTag->appendChild(new DOMElement('Deleted', (int)$exception->is_deleted, 'uri:Calendar'));
$exceptionTag->appendChild(new DOMElement('ExceptionStartTime', $exception->getOriginalDtStart()->format('Ymd\THis') . 'Z', 'uri:Calendar'));
if ((int)$exception->is_deleted === 0) {
$this->appendXML($exceptionTag, $_collectionData, $exception);
}
}
}
*/
}
/**
* Convert ActiveSync event recurrence exceptions into Kolab
*/
protected
function
exceptions_to_kolab
(
$data
,
$timezone
=
null
)
{
/*
// handle exceptions from recurrence
if (isset($xmlData->Exceptions)) {
$exdates = new Tinebase_Record_RecordSet('Calendar_Model_Event');
foreach ($xmlData->Exceptions->Exception as $exception) {
$eventException = new Calendar_Model_Event(array(
'recurid' => new Tinebase_DateTime((string)$exception->ExceptionStartTime)
));
if ((int)$exception->Deleted === 0) {
$eventException->is_deleted = false;
$this->toTineModel($exception, $eventException);
} else {
$eventException->is_deleted = true;
}
$exdates->addRecord($eventException);
}
$event->exdate = $exdates;
}
*/
}
/**
* Converts string of days (TU,TH) to bitmask used by ActiveSync
*
* @param string $days
*
* @return int
*/
protected
function
day2bitmask
(
$days
)
{
$days
=
explode
(
','
,
$days
);
$result
=
0
;
foreach
(
$days
as
$day
)
{
$result
=
$result
+
$this
->
recurDayMap
[
$day
];
}
return
$result
;
}
/**
* Convert bitmask used by ActiveSync to string of days (TU,TH)
*
* @param int $days
*
* @return string
*/
protected
function
bitmask2day
(
$days
)
{
$days_arr
=
array
();
for
(
$bitmask
=
1
;
$bitmask
<=
self
::
RECUR_DOW_SATURDAY
;
$bitmask
=
$bitmask
<<
1
)
{
$dayMatch
=
$days
&
$bitmask
;
if
(
$dayMatch
===
$bitmask
)
{
$days_arr
[]
=
array_search
(
$bitmask
,
$this
->
recurDayMap
);
}
}
$result
=
implode
(
','
,
$days_arr
);
return
$result
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, Apr 4, 6:23 AM (1 w, 1 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
1e/bb/3cbf82ed03b70e18e1e81d97a210
Default Alt Text
kolab_sync_data.php (36 KB)
Attached To
Mode
rS syncroton
Attached
Detach File
Event Timeline