Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F8199046
kolab_sync_data_calendar.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
kolab_sync_data_calendar.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> |
+--------------------------------------------------------------------------+
*/
/**
* Calendar (Events) data class for Syncroton
*/
class
kolab_sync_data_calendar
extends
kolab_sync_data
implements
Syncroton_Data_IDataCalendar
{
/**
* Mapping from ActiveSync Calendar namespace fields
*/
protected
$mapping
=
array
(
'allDayEvent'
=>
'allday'
,
'startTime'
=>
'start'
,
// keep it before endTime here
//'attendees' => 'attendees',
'body'
=>
'description'
,
//'bodyTruncated' => 'bodytruncated',
'busyStatus'
=>
'free_busy'
,
//'categories' => 'categories',
'dtStamp'
=>
'changed'
,
'endTime'
=>
'end'
,
//'exceptions' => 'exceptions',
'location'
=>
'location'
,
//'meetingStatus' => 'meetingstatus',
//'organizerEmail' => 'organizeremail',
//'organizerName' => 'organizername',
//'recurrence' => 'recurrence',
//'reminder' => 'reminder',
//'responseRequested' => 'responserequested',
//'responseType' => 'responsetype',
'sensitivity'
=>
'sensitivity'
,
'subject'
=>
'title'
,
//'timezone' => 'timezone',
'uID'
=>
'uid'
,
);
/**
* Kolab object type
*
* @var string
*/
protected
$modelName
=
'event'
;
/**
* Type of the default folder
*
* @var int
*/
protected
$defaultFolderType
=
Syncroton_Command_FolderSync
::
FOLDERTYPE_CALENDAR
;
/**
* Default container for new entries
*
* @var string
*/
protected
$defaultFolder
=
'Calendar'
;
/**
* Type of user created folders
*
* @var int
*/
protected
$folderType
=
Syncroton_Command_FolderSync
::
FOLDERTYPE_CALENDAR_USER_CREATED
;
/**
* attendee status
*/
const
ATTENDEE_STATUS_UNKNOWN
=
0
;
const
ATTENDEE_STATUS_TENTATIVE
=
2
;
const
ATTENDEE_STATUS_ACCEPTED
=
3
;
const
ATTENDEE_STATUS_DECLINED
=
4
;
const
ATTENDEE_STATUS_NOTRESPONDED
=
5
;
/**
* attendee types
*/
const
ATTENDEE_TYPE_REQUIRED
=
1
;
const
ATTENDEE_TYPE_OPTIONAL
=
2
;
const
ATTENDEE_TYPE_RESOURCE
=
3
;
/**
* busy status constants
*/
const
BUSY_STATUS_FREE
=
0
;
const
BUSY_STATUS_TENTATIVE
=
1
;
const
BUSY_STATUS_BUSY
=
2
;
const
BUSY_STATUS_OUTOFOFFICE
=
3
;
/**
* Sensitivity values
*/
const
SENSITIVITY_NORMAL
=
0
;
const
SENSITIVITY_PERSONAL
=
1
;
const
SENSITIVITY_PRIVATE
=
2
;
const
SENSITIVITY_CONFIDENTIAL
=
3
;
/**
* Mapping of attendee status
*
* @var array
*/
protected
$attendeeStatusMap
=
array
(
'UNKNOWN'
=>
self
::
ATTENDEE_STATUS_UNKNOWN
,
'TENTATIVE'
=>
self
::
ATTENDEE_STATUS_TENTATIVE
,
'ACCEPTED'
=>
self
::
ATTENDEE_STATUS_ACCEPTED
,
'DECLINED'
=>
self
::
ATTENDEE_STATUS_DECLINED
,
'DELEGATED'
=>
self
::
ATTENDEE_STATUS_UNKNOWN
,
'NEEDS-ACTION'
=>
self
::
ATTENDEE_STATUS_UNKNOWN
,
//self::ATTENDEE_STATUS_NOTRESPONDED,
);
/**
* Mapping of attendee type
*
* NOTE: recurrences need extra handling!
* @var array
*/
protected
$attendeeTypeMap
=
array
(
'REQ-PARTICIPANT'
=>
self
::
ATTENDEE_TYPE_REQUIRED
,
'OPT-PARTICIPANT'
=>
self
::
ATTENDEE_TYPE_OPTIONAL
,
// 'NON-PARTICIPANT' => self::ATTENDEE_TYPE_RESOURCE,
// 'CHAIR' => self::ATTENDEE_TYPE_RESOURCE,
);
/**
* Mapping of busy status
*
* @var array
*/
protected
$busyStatusMap
=
array
(
'free'
=>
self
::
BUSY_STATUS_FREE
,
'tentative'
=>
self
::
BUSY_STATUS_TENTATIVE
,
'busy'
=>
self
::
BUSY_STATUS_BUSY
,
'outofoffice'
=>
self
::
BUSY_STATUS_OUTOFOFFICE
,
);
/**
* mapping of sensitivity
*
* @var array
*/
protected
$sensitivityMap
=
array
(
'public'
=>
self
::
SENSITIVITY_PERSONAL
,
'private'
=>
self
::
SENSITIVITY_PRIVATE
,
'confidential'
=>
self
::
SENSITIVITY_CONFIDENTIAL
,
);
/**
* Appends contact data to xml element
*
* @param Syncroton_Model_SyncCollection $collection Collection data
* @param string $serverId Local entry identifier
* @param boolean $as_array Return entry as array
*
* @return array|Syncroton_Model_Event|array Event object
*/
public
function
getEntry
(
Syncroton_Model_SyncCollection
$collection
,
$serverId
,
$as_array
=
false
)
{
$event
=
is_array
(
$serverId
)
?
$serverId
:
$this
->
getObject
(
$collection
->
collectionId
,
$serverId
);
$config
=
$this
->
getFolderConfig
(
$event
[
'_mailbox'
]);
$result
=
array
();
// Timezone
// Kolab Format 3.0 and xCal does support timezone per-date, but ActiveSync allows
// only one timezone per-event. We'll use timezone of the start date
if
(
$event
[
'start'
]
instanceof
DateTime
)
{
$timezone
=
$event
[
'start'
]->
getTimezone
();
if
(
$timezone
&&
(
$tz_name
=
$timezone
->
getName
())
!=
'UTC'
)
{
$tzc
=
kolab_sync_timezone_converter
::
getInstance
();
if
(
$tz_name
=
$tzc
->
encodeTimezone
(
$tz_name
))
{
$result
[
'timezone'
]
=
$tz_name
;
}
}
}
// Calendar namespace fields
foreach
(
$this
->
mapping
as
$key
=>
$name
)
{
$value
=
$this
->
getKolabDataItem
(
$event
,
$name
);
switch
(
$name
)
{
case
'changed'
:
case
'end'
:
case
'start'
:
// For all-day events Kolab uses different times
// At least Android doesn't display such event as all-day event
if
(
$value
&&
is_a
(
$value
,
'DateTime'
))
{
$date
=
clone
$value
;
if
(
$event
[
'allday'
])
{
// need this for self::date_from_kolab()
$date
->
_dateonly
=
false
;
if
(
$name
==
'start'
)
{
$date
->
setTime
(
0
,
0
,
0
);
}
else
if
(
$name
==
'end'
)
{
$date
->
setTime
(
0
,
0
,
0
);
$date
->
modify
(
'+1 day'
);
}
}
// set this date for use in recurrence exceptions handling
if
(
$name
==
'start'
)
{
$event
[
'_start'
]
=
$date
;
}
$value
=
self
::
date_from_kolab
(
$date
);
}
break
;
case
'sensitivity'
:
$value
=
intval
(
$this
->
sensitivityMap
[
$value
]);
break
;
case
'free_busy'
:
$value
=
$this
->
busyStatusMap
[
$value
];
break
;
case
'description'
:
$value
=
$this
->
body_from_kolab
(
$value
,
$collection
);
break
;
}
if
(
empty
(
$value
)
||
is_array
(
$value
))
{
continue
;
}
$result
[
$key
]
=
$value
;
}
// Event reminder time
if
(
$config
[
'ALARMS'
])
{
$result
[
'reminder'
]
=
$this
->
from_kolab_alarm
(
$event
);
}
$result
[
'categories'
]
=
array
();
$result
[
'attendees'
]
=
array
();
// Categories, Roundcube Calendar plugin supports only one category at a time
if
(!
empty
(
$event
[
'categories'
]))
{
$result
[
'categories'
]
=
(
array
)
$event
[
'categories'
];
}
// Organizer
if
(!
empty
(
$event
[
'attendees'
]))
{
foreach
(
$event
[
'attendees'
]
as
$idx
=>
$attendee
)
{
if
(
$attendee
[
'role'
]
==
'ORGANIZER'
)
{
if
(
$name
=
$attendee
[
'name'
])
{
$result
[
'organizerName'
]
=
$name
;
}
if
(
$email
=
$attendee
[
'email'
])
{
$result
[
'organizerEmail'
]
=
$email
;
}
unset
(
$event
[
'attendees'
][
$idx
]);
break
;
}
}
}
// Attendees
if
(!
empty
(
$event
[
'attendees'
]))
{
foreach
(
$event
[
'attendees'
]
as
$idx
=>
$attendee
)
{
$att
=
array
();
if
(
$email
=
$attendee
[
'email'
])
{
$att
[
'email'
]
=
$email
;
}
else
{
// In Activesync email is required
continue
;
}
$att
[
'name'
]
=
$attendee
[
'name'
]
?:
$email
;
if
(
$this
->
asversion
>=
12
)
{
$type
=
isset
(
$attendee
[
'role'
])
?
$this
->
attendeeTypeMap
[
$attendee
[
'role'
]]
:
null
;
$status
=
isset
(
$attendee
[
'status'
])
?
$this
->
attendeeStatusMap
[
$attendee
[
'status'
]]
:
null
;
$att
[
'attendeeType'
]
=
$type
?
$type
:
self
::
ATTENDEE_TYPE_REQUIRED
;
$att
[
'attendeeStatus'
]
=
$status
?
$status
:
self
::
ATTENDEE_STATUS_UNKNOWN
;
}
$result
[
'attendees'
][]
=
new
Syncroton_Model_EventAttendee
(
$att
);
}
}
// Event meeting status
$this
->
meeting_status_from_kolab
(
$collection
,
$event
,
$result
);
// Recurrence (and exceptions)
$this
->
recurrence_from_kolab
(
$collection
,
$event
,
$result
);
return
$as_array
?
$result
:
new
Syncroton_Model_Event
(
$result
);
}
/**
* convert contact from xml to libkolab array
*
* @param Syncroton_Model_IEntry $data Contact to convert
* @param string $folderid Folder identifier
* @param array $entry Existing entry
* @param DateTimeZone $timezone Timezone of the event
*
* @return array
*/
public
function
toKolab
(
Syncroton_Model_IEntry
$data
,
$folderid
,
$entry
=
null
,
$timezone
=
null
)
{
$event
=
!
empty
(
$entry
)
?
$entry
:
array
();
$foldername
=
isset
(
$event
[
'_mailbox'
])
?
$event
[
'_mailbox'
]
:
$this
->
getFolderName
(
$folderid
);
$config
=
$this
->
getFolderConfig
(
$foldername
);
$is_exception
=
$data
instanceof
Syncroton_Model_EventException
;
$last_update
=
$event
[
'changed'
];
$event
[
'allday'
]
=
0
;
// check data validity
$this
->
eventCheck
(
$data
);
// Timezone
if
(!
$timezone
&&
isset
(
$data
->
timezone
))
{
$tzc
=
kolab_sync_timezone_converter
::
getInstance
();
$expected
=
kolab_format
::
$timezone
->
getName
();
if
(!
empty
(
$event
[
'start'
])
&&
(
$event
[
'start'
]
instanceof
DateTime
))
{
$expected
=
$event
[
'start'
]->
getTimezone
()->
getName
();
}
$timezone
=
$tzc
->
getTimezone
(
$data
->
timezone
,
$expected
);
try
{
$timezone
=
new
DateTimeZone
(
$timezone
);
}
catch
(
Exception
$e
)
{
$timezone
=
null
;
}
}
if
(
empty
(
$timezone
))
{
$timezone
=
new
DateTimeZone
(
'UTC'
);
}
// Calendar namespace fields
foreach
(
$this
->
mapping
as
$key
=>
$name
)
{
// skip UID field, unsupported in event exceptions
// we need to do this here, because the next line (data getter) will throw an exception
if
(
$is_exception
&&
$key
==
'uID'
)
{
continue
;
}
$value
=
$data
->
$key
;
switch
(
$name
)
{
case
'changed'
:
$value
=
null
;
break
;
case
'end'
:
case
'start'
:
if
(
$timezone
&&
$value
)
{
$value
->
setTimezone
(
$timezone
);
}
if
(
$value
&&
$data
->
allDayEvent
)
{
$value
->
_dateonly
=
true
;
// In ActiveSync all-day event ends on 00:00:00 next day
// In Kolab we just ignore the time spec.
if
(
$name
==
'end'
)
{
$diff
=
date_diff
(
$event
[
'start'
],
$value
);
$value
=
clone
$event
[
'start'
];
if
(
$diff
->
days
>
1
)
{
$value
->
add
(
new
DateInterval
(
'P'
.
(
$diff
->
days
-
1
)
.
'D'
));
}
}
}
break
;
case
'sensitivity'
:
$map
=
array_flip
(
$this
->
sensitivityMap
);
$value
=
$map
[
$value
];
break
;
case
'free_busy'
:
$map
=
array_flip
(
$this
->
busyStatusMap
);
$value
=
$map
[
$value
];
break
;
case
'description'
:
$value
=
$this
->
getBody
(
$value
,
Syncroton_Model_EmailBody
::
TYPE_PLAINTEXT
);
// If description isn't specified keep old description
if
(
$value
===
null
)
{
continue
2
;
}
break
;
}
$this
->
setKolabDataItem
(
$event
,
$name
,
$value
);
}
// Try to fix allday events from Android
// It doesn't set all-day flag but the period is a whole day
if
(!
$event
[
'allday'
]
&&
$event
[
'end'
]
&&
$event
[
'start'
])
{
$interval
=
@
date_diff
(
$event
[
'start'
],
$event
[
'end'
]);
if
(
$interval
&&
$interval
->
format
(
'%y%m%d%h%i%s'
)
===
'001000'
)
{
$event
[
'allday'
]
=
1
;
$event
[
'end'
]
=
clone
$event
[
'start'
];
}
}
// Reminder
// @TODO: should alarms be used when importing event from phone?
if
(
$config
[
'ALARMS'
])
{
$event
[
'valarms'
]
=
$this
->
to_kolab_alarm
(
$data
->
reminder
,
$event
);
}
$event
[
'attendees'
]
=
array
();
$event
[
'categories'
]
=
array
();
// Categories
if
(
isset
(
$data
->
categories
))
{
foreach
(
$data
->
categories
as
$category
)
{
$event
[
'categories'
][]
=
$category
;
}
}
// Organizer
if
(!
$is_exception
)
{
$name
=
$data
->
organizerName
;
$email
=
$data
->
organizerEmail
;
if
(
$name
||
$email
)
{
$event
[
'attendees'
][]
=
array
(
'role'
=>
'ORGANIZER'
,
'name'
=>
$name
,
'email'
=>
$email
,
);
}
}
// Attendees
if
(
isset
(
$data
->
attendees
))
{
foreach
(
$data
->
attendees
as
$attendee
)
{
$role
=
false
;
if
(
isset
(
$attendee
->
attendeeType
))
{
$role
=
array_search
(
$attendee
->
attendeeType
,
$this
->
attendeeTypeMap
);
}
if
(
$role
===
false
)
{
$role
=
array_search
(
self
::
ATTENDEE_TYPE_REQUIRED
,
$this
->
attendeeTypeMap
);
}
$_attendee
=
array
(
'role'
=>
$role
,
'name'
=>
$attendee
->
name
!=
$attendee
->
email
?
$attendee
->
name
:
''
,
'email'
=>
$attendee
->
email
,
);
if
(
isset
(
$attendee
->
attendeeStatus
))
{
$_attendee
[
'status'
]
=
$attendee
->
attendeeStatus
?
array_search
(
$attendee
->
attendeeStatus
,
$this
->
attendeeStatusMap
)
:
null
;
if
(!
$_attendee
[
'status'
])
{
$_attendee
[
'status'
]
=
'NEEDS-ACTION'
;
$_attendee
[
'rsvp'
]
=
true
;
}
}
else
if
(!
empty
(
$event
[
'attendees'
]))
{
// copy the old attendee status
foreach
(
$event
[
'attendees'
]
as
$old_attendee
)
{
if
(
$old_attendee
[
'email'
]
==
$_attendee
[
'email'
]
&&
isset
(
$old_attendee
[
'status'
]))
{
$_attendee
[
'status'
]
=
$old_attendee
[
'status'
];
$_attendee
[
'rsvp'
]
=
$old_attendee
[
'rsvp'
];
break
;
}
}
}
$event
[
'attendees'
][]
=
$_attendee
;
}
}
// recurrence (and exceptions)
if
(!
$is_exception
)
{
$event
[
'recurrence'
]
=
$this
->
recurrence_to_kolab
(
$data
,
$folderid
,
$timezone
);
}
// Bump SEQUENCE number on update (Outlook only).
// It's been confirmed that any change of the event that has attendees specified
// bumps SEQUENCE number of the event (we can see this in sent iTips).
if
(!
empty
(
$entry
)
&&
!
$is_exception
&&
!
empty
(
$data
->
attendees
)
&&
$data
->
dtStamp
&&
$last_update
&&
$data
->
dtStamp
>
$last_update
&&
stripos
(
$this
->
device
->
devicetype
,
'outlook'
)
!==
false
)
{
$event
[
'sequence'
]
+=
1
;
}
return
$event
;
}
/**
* Set attendee status for meeting
*
* @param Syncroton_Model_MeetingResponse $request The meeting response
*
* @return string ID of new calendar entry
*/
public
function
setAttendeeStatus
(
Syncroton_Model_MeetingResponse
$request
)
{
// @TODO: not implemented
throw
new
Syncroton_Exception_Status_MeetingResponse
(
Syncroton_Exception_Status_MeetingResponse
::
MEETING_ERROR
);
}
/**
* 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
)
{
$filter
=
array
(
array
(
'type'
,
'='
,
$this
->
modelName
));
switch
(
$filter_type
)
{
case
Syncroton_Command_Sync
::
FILTER_2_WEEKS_BACK
:
$mod
=
'-2 weeks'
;
break
;
case
Syncroton_Command_Sync
::
FILTER_1_MONTH_BACK
:
$mod
=
'-1 month'
;
break
;
case
Syncroton_Command_Sync
::
FILTER_3_MONTHS_BACK
:
$mod
=
'-3 months'
;
break
;
case
Syncroton_Command_Sync
::
FILTER_6_MONTHS_BACK
:
$mod
=
'-6 months'
;
break
;
}
if
(!
empty
(
$mod
))
{
$dt
=
new
DateTime
(
'now'
,
new
DateTimeZone
(
'UTC'
));
$dt
->
modify
(
$mod
);
$filter
[]
=
array
(
'dtend'
,
'>'
,
$dt
);
}
return
$filter
;
}
/**
* Set MeetingStatus according to event data
*/
protected
function
meeting_status_from_kolab
(
$collection
,
$event
,
&
$result
)
{
// 0 - The event is an appointment, which has no attendees.
// 1 - The event is a meeting and the user is the meeting organizer.
// 3 - This event is a meeting, and the user is not the meeting organizer.
// 5 - The meeting has been canceled and the user was the meeting organizer.
// 7 - The meeting has been canceled. The user was not the meeting organizer.
$status
=
0
;
if
(!
empty
(
$event
[
'attendees'
]))
{
// Find out if the user is an organizer
// TODO: Delegation/aliases support
$user_emails
=
kolab_sync
::
get_instance
()->
user
->
list_emails
();
$user_emails
=
array_map
(
function
(
$v
)
{
return
$v
[
'email'
];
},
$user_emails
);
$is_organizer
=
true
;
foreach
(
$event
[
'attendees'
]
as
$attendee
)
{
if
(
in_array_nocase
(
$attendee
[
'email'
],
$user_emails
))
{
$is_organizer
=
false
;
break
;
}
}
if
(
$event
[
'status'
]
==
'CANCELLED'
)
{
$status
=
!
empty
(
$is_organizer
)
?
5
:
7
;
}
else
{
$status
=
!
empty
(
$is_organizer
)
?
1
:
3
;
}
}
$result
[
'meetingStatus'
]
=
$status
;
}
/**
* Converts libkolab alarms spec. into a number of minutes
*/
protected
function
from_kolab_alarm
(
$event
)
{
if
(
isset
(
$event
[
'valarms'
]))
{
foreach
(
$event
[
'valarms'
]
as
$alarm
)
{
if
(
in_array
(
$alarm
[
'action'
],
array
(
'DISPLAY'
,
'AUDIO'
)))
{
$value
=
$alarm
[
'trigger'
];
break
;
}
}
}
if
(
$value
&&
$value
instanceof
DateTime
)
{
if
(
$event
[
'start'
]
&&
(
$interval
=
$event
[
'start'
]->
diff
(
$value
)))
{
if
(
$interval
->
invert
&&
!
$interval
->
m
&&
!
$interval
->
y
)
{
return
intval
(
round
(
$interval
->
s
/
60
)
+
$interval
->
i
+
$interval
->
h
*
60
+
$interval
->
d
*
60
*
24
);
}
}
}
else
if
(
$value
&&
preg_match
(
'/^([-+]*)[PT]*([0-9]+)([WDHMS])$/'
,
$value
,
$matches
))
{
$value
=
intval
(
$matches
[
2
]);
if
(
$value
&&
$matches
[
1
]
!=
'-'
)
{
return
null
;
}
switch
(
$matches
[
3
])
{
case
'S'
:
$value
=
intval
(
round
(
$value
/
60
));
break
;
case
'H'
:
$value
*=
60
;
break
;
case
'D'
:
$value
*=
24
*
60
;
break
;
case
'W'
:
$value
*=
7
*
24
*
60
;
break
;
}
return
$value
;
}
}
/**
* Converts ActiveSync reminder into libkolab alarms spec.
*/
protected
function
to_kolab_alarm
(
$value
,
$event
)
{
if
(
$value
===
null
||
$value
===
''
)
{
return
(
array
)
$event
[
'valarms'
];
}
$valarms
=
array
();
$unsupported
=
array
();
if
(!
empty
(
$event
[
'valarms'
]))
{
foreach
(
$event
[
'valarms'
]
as
$alarm
)
{
if
(!
$current
&&
in_array
(
$alarm
[
'action'
],
array
(
'DISPLAY'
,
'AUDIO'
)))
{
$current
=
$alarm
;
}
else
{
$unsupported
[]
=
$alarm
;
}
}
}
$valarms
[]
=
array
(
'action'
=>
$current
[
'action'
]
?:
'DISPLAY'
,
'description'
=>
$current
[
'description'
]
?:
''
,
'trigger'
=>
sprintf
(
'-PT%dM'
,
$value
),
);
if
(!
empty
(
$unsupported
))
{
$valarms
=
array_merge
(
$valarms
,
$unsupported
);
}
return
$valarms
;
}
/**
* Sanity checks on event input
*
* @param Syncroton_Model_IEntry &$entry Entry object
*
* @throws Syncroton_Exception_Status_Sync
*/
protected
function
eventCheck
(
Syncroton_Model_IEntry
&
$entry
)
{
// https://msdn.microsoft.com/en-us/library/jj194434(v=exchg.80).aspx
$now
=
new
DateTime
(
'now'
);
$rounded
=
new
DateTime
(
'now'
);
$min
=
(
int
)
$rounded
->
format
(
'i'
);
$add
=
$min
>
30
?
(
60
-
$min
)
:
(
30
-
$min
);
$rounded
->
add
(
new
DateInterval
(
'PT'
.
$add
.
'M'
));
if
(
empty
(
$entry
->
startTime
)
&&
empty
(
$entry
->
endTime
))
{
// use current time rounded to 30 minutes
$end
=
clone
$rounded
;
$end
->
add
(
new
DateInterval
(
$entry
->
allDayEvent
?
'P1D'
:
'PT30M'
));
$entry
->
startTime
=
$rounded
;
$entry
->
endTime
=
$end
;
}
else
if
(
empty
(
$entry
->
startTime
))
{
if
(
$entry
->
endTime
<
$now
||
$entry
->
endTime
<
$rounded
)
{
throw
new
Syncroton_Exception_Status_Sync
(
Syncroton_Exception_Status_Sync
::
INVALID_ITEM
);
}
$entry
->
startTime
=
$rounded
;
}
else
if
(
empty
(
$entry
->
endTime
))
{
if
(
$entry
->
startTime
<
$now
)
{
throw
new
Syncroton_Exception_Status_Sync
(
Syncroton_Exception_Status_Sync
::
INVALID_ITEM
);
}
$rounded
->
add
(
new
DateInterval
(
$entry
->
allDayEvent
?
'P1D'
:
'PT30M'
));
$entry
->
endTime
=
$rounded
;
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Wed, Jul 8, 9:52 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1052087
Default Alt Text
kolab_sync_data_calendar.php (24 KB)
Attached To
Mode
R4 syncroton
Attached
Detach File
Event Timeline
Log In to Comment