mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-22 07:53:39 +01:00
* Calendar/Mail: allow every participant to apply changes from extern organizer and warn if sender is not identical to iCal organizer or participant
This commit is contained in:
parent
9c29863079
commit
6afd07da03
@ -2223,10 +2223,10 @@ class calendar_boupdate extends calendar_bo
|
|||||||
*
|
*
|
||||||
* @param array $event
|
* @param array $event
|
||||||
* @param array $old_event
|
* @param array $old_event
|
||||||
* @param Api\DateTime|int|null $instance_date For recurring events, this is the date we
|
* @param Api\DateTime|int|null $instance_date For recurring events, this is the date we are dealing with
|
||||||
* are dealing with
|
* @param boolean $ignore_acl=false true: no acl check
|
||||||
*/
|
*/
|
||||||
function check_move_alarms(Array &$event, Array $old_event = null, $instance_date = null)
|
function check_move_alarms(Array &$event, Array $old_event = null, $instance_date = null, $ignore_acl=false)
|
||||||
{
|
{
|
||||||
if ($old_event !== null && $event['start'] == $old_event['start']) return;
|
if ($old_event !== null && $event['start'] == $old_event['start']) return;
|
||||||
|
|
||||||
@ -2254,7 +2254,7 @@ class calendar_boupdate extends calendar_bo
|
|||||||
else if ($alarm['time'] !== $time->format('ts') - $alarm['offset'])
|
else if ($alarm['time'] !== $time->format('ts') - $alarm['offset'])
|
||||||
{
|
{
|
||||||
$alarm['time'] = $time->format('ts') - $alarm['offset'];
|
$alarm['time'] = $time->format('ts') - $alarm['offset'];
|
||||||
$this->save_alarm($event['id'], $alarm);
|
$this->save_alarm($event['id'], $alarm, true, $ignore_acl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2265,11 +2265,12 @@ class calendar_boupdate extends calendar_bo
|
|||||||
* @param int $cal_id Id of the calendar-entry
|
* @param int $cal_id Id of the calendar-entry
|
||||||
* @param array $alarm array with fields: text, owner, enabled, ..
|
* @param array $alarm array with fields: text, owner, enabled, ..
|
||||||
* @param boolean $update_modified =true call update modified, default true
|
* @param boolean $update_modified =true call update modified, default true
|
||||||
|
* @param boolean $ignore_acl=false true: no acl check
|
||||||
* @return string id of the alarm, or false on error (eg. no perms)
|
* @return string id of the alarm, or false on error (eg. no perms)
|
||||||
*/
|
*/
|
||||||
function save_alarm($cal_id, $alarm, $update_modified=true)
|
function save_alarm($cal_id, $alarm, $update_modified=true, $ignore_acl=false)
|
||||||
{
|
{
|
||||||
if (!$cal_id || !$this->check_perms(Acl::EDIT,$alarm['all'] ? $cal_id : 0,!$alarm['all'] ? $alarm['owner'] : 0))
|
if (!$cal_id || !$ignore_acl && !$this->check_perms(Acl::EDIT,$alarm['all'] ? $cal_id : 0,!$alarm['all'] ? $alarm['owner'] : 0))
|
||||||
{
|
{
|
||||||
//echo "<p>no rights to save the alarm=".print_r($alarm,true)." to event($cal_id)</p>";
|
//echo "<p>no rights to save the alarm=".print_r($alarm,true)." to event($cal_id)</p>";
|
||||||
return false; // no rights to add the alarm
|
return false; // no rights to add the alarm
|
||||||
@ -2283,13 +2284,14 @@ class calendar_boupdate extends calendar_bo
|
|||||||
* delete one alarms identified by its id
|
* delete one alarms identified by its id
|
||||||
*
|
*
|
||||||
* @param string $id alarm-id is a string of 'cal:'.$cal_id.':'.$alarm_nr, it is used as the job-id too
|
* @param string $id alarm-id is a string of 'cal:'.$cal_id.':'.$alarm_nr, it is used as the job-id too
|
||||||
|
* @param boolean $ignore_acl=false true: no acl check
|
||||||
* @return int number of alarms deleted, false on error (eg. no perms)
|
* @return int number of alarms deleted, false on error (eg. no perms)
|
||||||
*/
|
*/
|
||||||
function delete_alarm($id)
|
function delete_alarm($id, $ignore_acl=false)
|
||||||
{
|
{
|
||||||
list(,$cal_id) = explode(':',$id);
|
list(,$cal_id) = explode(':',$id);
|
||||||
|
|
||||||
if (!($alarm = $this->so->read_alarm($id)) || !$cal_id || !$this->check_perms(Acl::EDIT,$alarm['all'] ? $cal_id : 0,!$alarm['all'] ? $alarm['owner'] : 0))
|
if (!($alarm = $this->so->read_alarm($id)) || !$cal_id || !$ignore_acl && !$this->check_perms(Acl::EDIT,$alarm['all'] ? $cal_id : 0,!$alarm['all'] ? $alarm['owner'] : 0))
|
||||||
{
|
{
|
||||||
return false; // no rights to delete the alarm
|
return false; // no rights to delete the alarm
|
||||||
}
|
}
|
||||||
|
@ -2066,7 +2066,6 @@ class calendar_uiforms extends calendar_ui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove (shared) lock via ajax, when edit popup get's closed
|
* Remove (shared) lock via ajax, when edit popup get's closed
|
||||||
*
|
*
|
||||||
@ -2084,6 +2083,34 @@ class calendar_uiforms extends calendar_ui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get email of participant
|
||||||
|
*
|
||||||
|
* @param string $uid
|
||||||
|
* @return string|null
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function participantEmail($uid)
|
||||||
|
{
|
||||||
|
if (is_numeric($uid))
|
||||||
|
{
|
||||||
|
$email = Api\Accounts::id2name($uid, 'account_email') ?: null;
|
||||||
|
}
|
||||||
|
elseif ($uid[0] === 'e')
|
||||||
|
{
|
||||||
|
$email = substr($uid, 1);
|
||||||
|
}
|
||||||
|
elseif ($uid[0] === 'c' && ($contact = (new Api\Contacts)->read(substr($uid, 1))))
|
||||||
|
{
|
||||||
|
$email = $contact['email'] ?? $contact['email_home'];
|
||||||
|
}
|
||||||
|
if (!empty($email) && preg_match('/<([^>]+?)>$/', $email, $matches))
|
||||||
|
{
|
||||||
|
$email = $matches[1];
|
||||||
|
}
|
||||||
|
return $email;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display iCal meeting request for EMail app and allow to accept, tentative or reject it or a reply and allow to apply it
|
* Display iCal meeting request for EMail app and allow to accept, tentative or reject it or a reply and allow to apply it
|
||||||
*
|
*
|
||||||
@ -2114,6 +2141,7 @@ class calendar_uiforms extends calendar_ui
|
|||||||
$ical_string = $session_data['attachment'];
|
$ical_string = $session_data['attachment'];
|
||||||
$ical_charset = $session_data['charset'];
|
$ical_charset = $session_data['charset'];
|
||||||
$ical_method = $session_data['method'];
|
$ical_method = $session_data['method'];
|
||||||
|
$ical_sender = $session_data['sender'];
|
||||||
unset($session_data);
|
unset($session_data);
|
||||||
}
|
}
|
||||||
$ical = new calendar_ical();
|
$ical = new calendar_ical();
|
||||||
@ -2140,6 +2168,18 @@ class calendar_uiforms extends calendar_ui
|
|||||||
if (($existing_event = $this->bo->read($event['uid'], $event['recurrence'], false, 'ts', null, true)) && // true = read the exception
|
if (($existing_event = $this->bo->read($event['uid'], $event['recurrence'], false, 'ts', null, true)) && // true = read the exception
|
||||||
!$existing_event['deleted'])
|
!$existing_event['deleted'])
|
||||||
{
|
{
|
||||||
|
// check if mail is from extern organizer
|
||||||
|
$from_extern_organizer = false;
|
||||||
|
if (strtolower($ical_method) !== 'reply' &&
|
||||||
|
($extern_organizer = !empty($ical_sender) ? array_filter($existing_event['participants'], static function($status, $user)
|
||||||
|
{
|
||||||
|
calendar_so::split_status($status, $quantity, $role);
|
||||||
|
return $role === 'CHAIR' && is_string($user) && in_array($user[0], ['e', 'c']);
|
||||||
|
}, ARRAY_FILTER_USE_BOTH) : []) &&
|
||||||
|
!($from_extern_organizer = $ical_sender === strtolower($organizer=self::participantEmail(key($extern_organizer)))))
|
||||||
|
{
|
||||||
|
$event['sender_warning'] = lang('The sender "%1" is NOT the extern organizer "%2", proceed with caution!', $ical_sender, $organizer);
|
||||||
|
}
|
||||||
switch(strtolower($ical_method))
|
switch(strtolower($ical_method))
|
||||||
{
|
{
|
||||||
case 'reply':
|
case 'reply':
|
||||||
@ -2150,7 +2190,11 @@ class calendar_uiforms extends calendar_ui
|
|||||||
$event['ical_sender_status'] = current($parts);
|
$event['ical_sender_status'] = current($parts);
|
||||||
$quantity = $role = null;
|
$quantity = $role = null;
|
||||||
calendar_so::split_status($event['ical_sender_status'], $quantity, $role);
|
calendar_so::split_status($event['ical_sender_status'], $quantity, $role);
|
||||||
|
// let user know, that sender is not the participant
|
||||||
|
if ($ical_sender !== strtolower($participant=self::participantEmail($event['ical_sender_uid'])))
|
||||||
|
{
|
||||||
|
$event['sender_warning'] = lang('The sender "%1" is NOT the participant replying "%2", proceed with caution!', $ical_sender, $participant);
|
||||||
|
}
|
||||||
if ($event['ical_sender_uid'] && $this->bo->check_status_perms($event['ical_sender_uid'], $existing_event))
|
if ($event['ical_sender_uid'] && $this->bo->check_status_perms($event['ical_sender_uid'], $existing_event))
|
||||||
{
|
{
|
||||||
$existing_status = $existing_event['participants'][$event['ical_sender_uid']];
|
$existing_status = $existing_event['participants'][$event['ical_sender_uid']];
|
||||||
@ -2169,7 +2213,12 @@ class calendar_uiforms extends calendar_ui
|
|||||||
case 'request':
|
case 'request':
|
||||||
$status = $existing_event['participants'][$user];
|
$status = $existing_event['participants'][$user];
|
||||||
calendar_so::split_status($status, $quantity, $role);
|
calendar_so::split_status($status, $quantity, $role);
|
||||||
if (strtolower($ical_method) == 'response' && isset($existing_event['participants'][$user]) &&
|
if (!empty($extern_organizer) && self::event_changed($event, $existing_event))
|
||||||
|
{
|
||||||
|
$event['error'] = lang('The extern organizer changed the event!',);
|
||||||
|
$readonlys['button[apply]'] = false;
|
||||||
|
}
|
||||||
|
elseif (isset($existing_event['participants'][$user]) &&
|
||||||
$status != 'U' && isset($this->bo->verbose_status[$status]))
|
$status != 'U' && isset($this->bo->verbose_status[$status]))
|
||||||
{
|
{
|
||||||
$event['error'] = lang('You already replied to this invitation with').': '.lang($this->bo->verbose_status[$status]);
|
$event['error'] = lang('You already replied to this invitation with').': '.lang($this->bo->verbose_status[$status]);
|
||||||
@ -2185,7 +2234,7 @@ class calendar_uiforms extends calendar_ui
|
|||||||
$event['error'] .= ($event['error'] ? "\n" : '').lang('You are not invited to that event!');
|
$event['error'] .= ($event['error'] ? "\n" : '').lang('You are not invited to that event!');
|
||||||
if ($event['id'])
|
if ($event['id'])
|
||||||
{
|
{
|
||||||
$readonlys['button[accept]'] = $readonlys['button[tentativ]'] =
|
$readonlys['button[accept]'] = $readonlys['button[tentativ]'] = $readonlys['button[apply]'] =
|
||||||
$readonlys['button[reject]'] = $readonlys['button[cancel]'] = true;
|
$readonlys['button[reject]'] = $readonlys['button[cancel]'] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2257,6 +2306,54 @@ class calendar_uiforms extends calendar_ui
|
|||||||
// clear notification errors
|
// clear notification errors
|
||||||
notifications::errors(true);
|
notifications::errors(true);
|
||||||
|
|
||||||
|
$msg = [];
|
||||||
|
// do we need to update the event itself (user-status is reset to old in event_changed!)
|
||||||
|
if ($button !== 'delete' && !empty($event['old']) && self::event_changed($event, $event['old']))
|
||||||
|
{
|
||||||
|
// check if we are allowed to update the event
|
||||||
|
if($this->bo->check_perms(Acl::EDIT, $event['old']) || $event['extern_organizer'])
|
||||||
|
{
|
||||||
|
if ($event['recurrence'] && !$event['old']['reference'] && ($recur_event = $this->bo->read($event['id'])))
|
||||||
|
{
|
||||||
|
// first we need to add the exception to the recurrence master
|
||||||
|
$recur_event['recur_exception'][] = $event['recurrence'];
|
||||||
|
// check if we need to move the alarms, because they are next on that exception
|
||||||
|
$this->bo->check_move_alarms($recur_event, null, $event['recurrence'], !empty($event['extern_organizer']));
|
||||||
|
unset($recur_event['start']); unset($recur_event['end']); // no update necessary
|
||||||
|
unset($recur_event['alarm']); // unsetting alarms too, as they cant be updated without start!
|
||||||
|
$this->bo->update($recur_event, $ignore_conflicts=true, true, !empty($event['extern_organizer']), true, $msg, true);
|
||||||
|
|
||||||
|
// then we need to create the exception as new event
|
||||||
|
unset($event['id']);
|
||||||
|
$event['reference'] = $event['old']['id'];
|
||||||
|
$event['caldav_name'] = $event['old']['caldav_name'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// keep all EGroupware only values of existing events plus alarms
|
||||||
|
unset($event['alarm'], $event['organizer']);
|
||||||
|
$event = array_merge($event['old'], $event);
|
||||||
|
}
|
||||||
|
unset($event['old']);
|
||||||
|
|
||||||
|
if (($event['id'] = $this->bo->update($event, $ignore_conflicts=true, true, !empty($event['extern_organizer']), true, $msg, true)))
|
||||||
|
{
|
||||||
|
$msg[] = lang('Changed event-data applied');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$msg[] = lang('Error saving the event!');
|
||||||
|
$button = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$event['id'] = $event['old']['id'];
|
||||||
|
// disable "warning" that we have no rights to store any modifications
|
||||||
|
// as that confuses our users, who only want to accept or reject
|
||||||
|
//$msg[] = lang('Not enough rights to update the event!');
|
||||||
|
}
|
||||||
|
}
|
||||||
switch($button)
|
switch($button)
|
||||||
{
|
{
|
||||||
case 'reject':
|
case 'reject':
|
||||||
@ -2291,53 +2388,6 @@ class calendar_uiforms extends calendar_ui
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// do we need to update the event itself (user-status is reset to old in event_changed!)
|
|
||||||
elseif (self::event_changed($event, $event['old']))
|
|
||||||
{
|
|
||||||
// check if we are allowed to update the event
|
|
||||||
if($this->bo->check_perms(Acl::EDIT, $event['old']))
|
|
||||||
{
|
|
||||||
if ($event['recurrence'] && !$event['old']['reference'] && ($recur_event = $this->bo->read($event['id'])))
|
|
||||||
{
|
|
||||||
// first we need to add the exception to the recurrence master
|
|
||||||
$recur_event['recur_exception'][] = $event['recurrence'];
|
|
||||||
// check if we need to move the alarms, because they are next on that exception
|
|
||||||
$this->bo->check_move_alarms($recur_event, null, $event['recurrence']);
|
|
||||||
unset($recur_event['start']); unset($recur_event['end']); // no update necessary
|
|
||||||
unset($recur_event['alarm']); // unsetting alarms too, as they cant be updated without start!
|
|
||||||
$this->bo->update($recur_event, $ignore_conflicts=true, true, false, true, $msg, true);
|
|
||||||
|
|
||||||
// then we need to create the exception as new event
|
|
||||||
unset($event['id']);
|
|
||||||
$event['reference'] = $event['old']['id'];
|
|
||||||
$event['caldav_name'] = $event['old']['caldav_name'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// keep all EGroupware only values of existing events plus alarms
|
|
||||||
unset($event['alarm']);
|
|
||||||
$event = array_merge($event['old'], $event);
|
|
||||||
}
|
|
||||||
unset($event['old']);
|
|
||||||
|
|
||||||
if (($event['id'] = $this->bo->update($event, $ignore_conflicts=true, true, false, true, $msg, true)))
|
|
||||||
{
|
|
||||||
$msg[] = lang('Event saved');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$msg[] = lang('Error saving the event!');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$event['id'] = $event['old']['id'];
|
|
||||||
// disable "warning" that we have no rights to store any modifications
|
|
||||||
// as that confuses our users, who only want to accept or reject
|
|
||||||
//$msg[] = lang('Not enough rights to update the event!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$event['id'] = $event['old']['id'];
|
$event['id'] = $event['old']['id'];
|
||||||
@ -2351,9 +2401,9 @@ class calendar_uiforms extends calendar_ui
|
|||||||
|
|
||||||
case 'apply':
|
case 'apply':
|
||||||
// set status and send notification / meeting response
|
// set status and send notification / meeting response
|
||||||
if ($this->bo->set_status($event['id'], $event['ical_sender_uid'], $event['ical_sender_status'], $event['recurrence']))
|
if (strtolower($event['ics_method']) === 'reply' && $this->bo->set_status($event['id'], $event['ical_sender_uid'], $event['ical_sender_status'], $event['recurrence']))
|
||||||
{
|
{
|
||||||
$msg = lang('Status changed');
|
$msg[] = lang('Status changed');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2361,7 +2411,7 @@ class calendar_uiforms extends calendar_ui
|
|||||||
if ($event['id'] && $this->bo->set_status($event['id'], $user, 'R', $event['recurrence'],
|
if ($event['id'] && $this->bo->set_status($event['id'], $user, 'R', $event['recurrence'],
|
||||||
false, true, true)) // no reply to organizer
|
false, true, true)) // no reply to organizer
|
||||||
{
|
{
|
||||||
$msg = lang('Status changed');
|
$msg[] = lang('Status changed');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2369,7 +2419,7 @@ class calendar_uiforms extends calendar_ui
|
|||||||
if ($event['id'] && $this->bo->delete($event['id'], $event['recurrence'],
|
if ($event['id'] && $this->bo->delete($event['id'], $event['recurrence'],
|
||||||
false, [$event['ical_sender_uid']])) // no reply to organizer
|
false, [$event['ical_sender_uid']])) // no reply to organizer
|
||||||
{
|
{
|
||||||
$msg = lang('Event deleted.');
|
$msg[] = lang('Event deleted.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2378,7 +2428,7 @@ class calendar_uiforms extends calendar_ui
|
|||||||
}
|
}
|
||||||
Framework::message(implode("\n", (array)$msg));
|
Framework::message(implode("\n", (array)$msg));
|
||||||
$readonlys['button[edit]'] = !$event['id'];
|
$readonlys['button[edit]'] = !$event['id'];
|
||||||
$event['ics_method'] = $readonlys['ics_method'] = strtolower($ical_method);
|
$event['ics_method'] = strtolower($ical_method);
|
||||||
switch(strtolower($ical_method))
|
switch(strtolower($ical_method))
|
||||||
{
|
{
|
||||||
case 'reply':
|
case 'reply':
|
||||||
@ -2395,6 +2445,8 @@ class calendar_uiforms extends calendar_ui
|
|||||||
$tpl = new Etemplate('calendar.meeting');
|
$tpl = new Etemplate('calendar.meeting');
|
||||||
$tpl->exec('calendar.calendar_uiforms.meeting', $event, array(), $readonlys, $event+array(
|
$tpl->exec('calendar.calendar_uiforms.meeting', $event, array(), $readonlys, $event+array(
|
||||||
'old' => $existing_event,
|
'old' => $existing_event,
|
||||||
|
'extern_organizer' => $extern_organizer ?? [],
|
||||||
|
'from_extern_organizer' => $from_extern_organizer ?? false,
|
||||||
), 2);
|
), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2413,8 +2465,8 @@ class calendar_uiforms extends calendar_ui
|
|||||||
'recur_type', 'recur_data', 'recur_interval', 'recur_exception');
|
'recur_type', 'recur_data', 'recur_interval', 'recur_exception');
|
||||||
|
|
||||||
// only compare certain fields, taking account unset, null or '' values
|
// only compare certain fields, taking account unset, null or '' values
|
||||||
$event = array_intersect_key($_event+array('recur_exception'=>array()), array_flip($keys_to_check));
|
$event = array_intersect_key(array_diff($_event, [null, ''])+array('recur_exception'=>array()), array_flip($keys_to_check));
|
||||||
$old = array_intersect_key(array_diff($_old, array(null, '')), array_flip($keys_to_check));
|
$old = array_intersect_key(array_diff($_old, [null, '']), array_flip($keys_to_check));
|
||||||
|
|
||||||
// keep the status of existing participants (users)
|
// keep the status of existing participants (users)
|
||||||
foreach($old['participants'] as $uid => $status)
|
foreach($old['participants'] as $uid => $status)
|
||||||
@ -2425,7 +2477,7 @@ class calendar_uiforms extends calendar_ui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ret = $event != $old;
|
$ret = (bool)array_diff_assoc($event, $old);
|
||||||
//error_log(__METHOD__."() returning ".array2string($ret)." diff=".array2string(array_udiff_assoc($event, $old, function($a, $b) { return (int)($a != $b); })));
|
//error_log(__METHOD__."() returning ".array2string($ret)." diff=".array2string(array_udiff_assoc($event, $old, function($a, $b) { return (int)($a != $b); })));
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
@ -3539,4 +3591,4 @@ class calendar_uiforms extends calendar_ui
|
|||||||
|
|
||||||
$etpl->exec('calendar.calendar_uiforms.notify', $content, $sel_options, $readonlys, $preserve,2);
|
$etpl->exec('calendar.calendar_uiforms.notify', $content, $sel_options, $readonlys, $preserve,2);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -29,6 +29,7 @@ add alarm calendar de Alarm zufügen
|
|||||||
add appointments via shortened dialog or complete edit window calendar de Termine hinzufügen über verkürzten Dialog oder komplettes Bearbeiten-Fenster
|
add appointments via shortened dialog or complete edit window calendar de Termine hinzufügen über verkürzten Dialog oder komplettes Bearbeiten-Fenster
|
||||||
add current view as favorite calendar de Ansicht als Favorit zufügen
|
add current view as favorite calendar de Ansicht als Favorit zufügen
|
||||||
add new alarm calendar de Neuen Alarm erstellen
|
add new alarm calendar de Neuen Alarm erstellen
|
||||||
|
add new event calendar de Einen neuen Termin hinzufügen
|
||||||
add new participants or resource calendar de Neue(n) Teilnehmer oder Ressource auswählen
|
add new participants or resource calendar de Neue(n) Teilnehmer oder Ressource auswählen
|
||||||
add timesheet entry calendar de Stundenzettel hinzufügen
|
add timesheet entry calendar de Stundenzettel hinzufügen
|
||||||
added calendar de Neuer Termin
|
added calendar de Neuer Termin
|
||||||
@ -62,6 +63,7 @@ apply the changes calendar de Übernimmt die Änderungen
|
|||||||
appointment settings calendar de Einstellungen der Terminverwaltung
|
appointment settings calendar de Einstellungen der Terminverwaltung
|
||||||
as an alternative you can %1download a mysql dump%2 and import it manually into egw_cal_timezones table. calendar de Als Alternative können Sie auch einen %1MySQL Dump herunterladen%2 und diesen von Hand in die Datenbank Tabelle egw_cal_timezones importieren.
|
as an alternative you can %1download a mysql dump%2 and import it manually into egw_cal_timezones table. calendar de Als Alternative können Sie auch einen %1MySQL Dump herunterladen%2 und diesen von Hand in die Datenbank Tabelle egw_cal_timezones importieren.
|
||||||
at start of the event calendar de am Beginn des Termins
|
at start of the event calendar de am Beginn des Termins
|
||||||
|
attention calendar de Achtung
|
||||||
automatically purge old events after admin de Bereinigt bzw. löscht alte Termine automatisch nach
|
automatically purge old events after admin de Bereinigt bzw. löscht alte Termine automatisch nach
|
||||||
available for the first entry inside each day of week or daily table inside the selected range: calendar de Verfügbar für den ersten Eintrag innerhalb eines jeden Tages oder für die Liste innerhalb des ausgewählten Bereichs:
|
available for the first entry inside each day of week or daily table inside the selected range: calendar de Verfügbar für den ersten Eintrag innerhalb eines jeden Tages oder für die Liste innerhalb des ausgewählten Bereichs:
|
||||||
back half a month calendar de einen halben Monat zurück
|
back half a month calendar de einen halben Monat zurück
|
||||||
@ -578,9 +580,12 @@ the apple ical apps use this color to display events from this calendar. calenda
|
|||||||
the document can contain placeholder like {{%1}}, to be replaced with the data. calendar de Das Dokument kann Platzhalter wie {{%1}} enthalten, die durch die Daten ersetzt werden sollen.
|
the document can contain placeholder like {{%1}}, to be replaced with the data. calendar de Das Dokument kann Platzhalter wie {{%1}} enthalten, die durch die Daten ersetzt werden sollen.
|
||||||
the document can contain placeholder like {{%3}}, to be replaced with the data (%1full list of placeholder names%2). calendar de Das Dokument kann Platzhalter wie {{%3}} enthalten, die mit den Daten ersetzt werden (%1komplette Liste der Platzhalter%2)
|
the document can contain placeholder like {{%3}}, to be replaced with the data (%1full list of placeholder names%2). calendar de Das Dokument kann Platzhalter wie {{%3}} enthalten, die mit den Daten ersetzt werden (%1komplette Liste der Platzhalter%2)
|
||||||
the exceptions are deleted together with the series. calendar de Die Ausnahmen werden mit der Terminserie gelöscht
|
the exceptions are deleted together with the series. calendar de Die Ausnahmen werden mit der Terminserie gelöscht
|
||||||
|
the extern organizer changed the event! calendar de Der externe Organisator hat den Termin geändert!
|
||||||
the following document-types are supported: calendar de Im Moment werden die folgenden Dokumenttypen unterstützt:
|
the following document-types are supported: calendar de Im Moment werden die folgenden Dokumenttypen unterstützt:
|
||||||
the original series will be terminated today and a new series be created. calendar de Der bestehende Serientermin wird heute beendet und eine neue Terminserie erstellt.
|
the original series will be terminated today and a new series be created. calendar de Der bestehende Serientermin wird heute beendet und eine neue Terminserie erstellt.
|
||||||
the resource you selected is already overbooked: calendar de Die Ressource, die Sie buchen möchten, ist bereits überbucht
|
the resource you selected is already overbooked: calendar de Die Ressource, die Sie buchen möchten, ist bereits überbucht
|
||||||
|
the sender "%1" is not the extern organizer "%2", proceed with caution! calendar de Der Absender "%1" ist nicht der externe Organisator "%2", seien Sie vorsichtig!
|
||||||
|
the sender "%1" is not the participant replying "%2", proceed with caution! calendar de Der Absender "%1" ist nicht der antwortende Teilnehmer "%2", seien Sie vorsichtig!
|
||||||
this day is shown as first day in the week or month view. calendar de Dieser Tag wird als erster in der Wochen- oder Monatsansicht angezeigt
|
this day is shown as first day in the week or month view. calendar de Dieser Tag wird als erster in der Wochen- oder Monatsansicht angezeigt
|
||||||
this defines the end of your dayview. events after this time, are shown below the dayview. calendar de Diese Zeit definiert das Ende des Arbeitstags in der Tagesansicht. Alle späteren Einträge werden darunter dargestellt
|
this defines the end of your dayview. events after this time, are shown below the dayview. calendar de Diese Zeit definiert das Ende des Arbeitstags in der Tagesansicht. Alle späteren Einträge werden darunter dargestellt
|
||||||
this defines the start of your dayview. events before this time, are shown above the dayview.<br>this time is also used as a default starttime for new events. calendar de Diese Zeit definiert den Anfang des Arbeitstags in der Tagesansicht. Alle früheren Einträge werden darüber dargestellt
|
this defines the start of your dayview. events before this time, are shown above the dayview.<br>this time is also used as a default starttime for new events. calendar de Diese Zeit definiert den Anfang des Arbeitstags in der Tagesansicht. Alle früheren Einträge werden darüber dargestellt
|
||||||
|
@ -63,6 +63,7 @@ apply the changes calendar en Apply the changes
|
|||||||
appointment settings calendar en Appointment settings
|
appointment settings calendar en Appointment settings
|
||||||
as an alternative you can %1download a mysql dump%2 and import it manually into egw_cal_timezones table. calendar en As an alternative you can %1download a MySQL dump%2 and import it manually into egw_cal_timezones table.
|
as an alternative you can %1download a mysql dump%2 and import it manually into egw_cal_timezones table. calendar en As an alternative you can %1download a MySQL dump%2 and import it manually into egw_cal_timezones table.
|
||||||
at start of the event calendar en at start of the event
|
at start of the event calendar en at start of the event
|
||||||
|
attention calendar en Attention
|
||||||
automatically purge old events after admin en Automatically purge old events after
|
automatically purge old events after admin en Automatically purge old events after
|
||||||
available for the first entry inside each day of week or daily table inside the selected range: calendar en Available for the first entry inside each day of week or daily table inside the selected range:
|
available for the first entry inside each day of week or daily table inside the selected range: calendar en Available for the first entry inside each day of week or daily table inside the selected range:
|
||||||
back half a month calendar en Back half a month
|
back half a month calendar en Back half a month
|
||||||
@ -579,9 +580,12 @@ the apple ical apps use this color to display events from this calendar. calenda
|
|||||||
the document can contain placeholder like {{%1}}, to be replaced with the data. calendar en The document can contain placeholder like {{%1}}, to be replaced with the data.
|
the document can contain placeholder like {{%1}}, to be replaced with the data. calendar en The document can contain placeholder like {{%1}}, to be replaced with the data.
|
||||||
the document can contain placeholder like {{%3}}, to be replaced with the data (%1full list of placeholder names%2). calendar en The document can contain placeholder like {{%3}}, to be replaced with the data (%1full list of placeholder names%2).
|
the document can contain placeholder like {{%3}}, to be replaced with the data (%1full list of placeholder names%2). calendar en The document can contain placeholder like {{%3}}, to be replaced with the data (%1full list of placeholder names%2).
|
||||||
the exceptions are deleted together with the series. calendar en The exceptions are deleted together with the series.
|
the exceptions are deleted together with the series. calendar en The exceptions are deleted together with the series.
|
||||||
|
the extern organizer changed the event! calendar en The extern organizer changed the event!
|
||||||
the following document-types are supported: calendar en The following document-types are supported:
|
the following document-types are supported: calendar en The following document-types are supported:
|
||||||
the original series will be terminated today and a new series be created. calendar en The original series will be terminated today and a new series be created.
|
the original series will be terminated today and a new series be created. calendar en The original series will be terminated today and a new series be created.
|
||||||
the resource you selected is already overbooked: calendar en The resource you selected is already over-booked:
|
the resource you selected is already overbooked: calendar en The resource you selected is already over-booked:
|
||||||
|
the sender "%1" is not the extern organizer "%2", proceed with caution! calendar en The sender "%1" is NOT the extern organizer "%2", proceed with caution!
|
||||||
|
the sender "%1" is not the participant replying "%2", proceed with caution! calendar en The sender "%1" is NOT the participant replying "%2", proceed with caution!
|
||||||
this day is shown as first day in the week or month view. calendar en This day is shown as first day in the week or month view.
|
this day is shown as first day in the week or month view. calendar en This day is shown as first day in the week or month view.
|
||||||
this defines the end of your dayview. events after this time, are shown below the dayview. calendar en This defines the end of your day view. Events after this time, are shown below the day view.
|
this defines the end of your dayview. events after this time, are shown below the dayview. calendar en This defines the end of your day view. Events after this time, are shown below the day view.
|
||||||
this defines the start of your dayview. events before this time, are shown above the dayview.<br>this time is also used as a default starttime for new events. calendar en This defines the start of your day view. Events before this time, are shown above the day view.<br>This time is also used as a default start time for new events.
|
this defines the start of your dayview. events before this time, are shown above the dayview.<br>this time is also used as a default starttime for new events. calendar en This defines the start of your day view. Events before this time, are shown above the day view.<br>This time is also used as a default start time for new events.
|
||||||
|
@ -7,36 +7,49 @@
|
|||||||
<column/>
|
<column/>
|
||||||
<column/>
|
<column/>
|
||||||
<column/>
|
<column/>
|
||||||
<column/>
|
|
||||||
<column/>
|
|
||||||
<column/>
|
|
||||||
</columns>
|
</columns>
|
||||||
<rows>
|
<rows>
|
||||||
|
<row disabled="!@sender_warning">
|
||||||
|
<grid width="100%" class="meetingRequest" span="all">
|
||||||
|
<columns>
|
||||||
|
<column/>
|
||||||
|
</columns>
|
||||||
|
<rows>
|
||||||
|
<row class="th">
|
||||||
|
<description value="Attention"/>
|
||||||
|
</row>
|
||||||
|
<row class="row">
|
||||||
|
<description id="sender_warning" class="meetingRequestError"/>
|
||||||
|
</row>
|
||||||
|
</rows>
|
||||||
|
</grid>
|
||||||
|
</row>
|
||||||
<row disabled="!@ics_method=request">
|
<row disabled="!@ics_method=request">
|
||||||
<description value="This mail contains a meeting request" class="meetingRequestMessage"/>
|
<description value="This mail contains a meeting request" class="meetingRequestMessage"/>
|
||||||
<button label="Accept" id="button[accept]" class="leftPad5"/>
|
<hbox>
|
||||||
<button label="Tentative" id="button[tentativ]" class="leftPad5"/>
|
<button label="Apply" id="button[apply]" class="leftPad5" hideOnReadonly="true"/>
|
||||||
<button label="Reject" id="button[reject]" class="leftPad5"/>
|
<button label="Accept" id="button[accept]" class="leftPad5" background_image="true" image="calendar/accepted"/>
|
||||||
<buttononly statustext="Edit event in calendar" label="Edit" id="button[edit]" onclick="window.open(egw::link('/index.php','menuaction=calendar.calendar_uiforms.edit&cal_id=$cont[id]'),'_blank','dependent=yes,width=750,height=410,scrollbars=yes,status=yes'); return false;" class="leftPad5"/>
|
<button label="Tentative" id="button[tentativ]" class="leftPad5" background_image="true" image="calendar/tentative"/>
|
||||||
|
<button label="Reject" id="button[reject]" class="leftPad5" background_image="true" image="calendar/rejected"/>
|
||||||
|
<button statustext="Edit event in calendar" label="Edit" id="button[edit]" background_image="true" image="edit" hideOnReadonly="true"
|
||||||
|
onclick="window.open(egw::link('/index.php','menuaction=calendar.calendar_uiforms.edit&cal_id=$cont[id]'),'_blank','dependent=yes,width=750,height=410,scrollbars=yes,status=yes'); return false;" class="leftPad5"/>
|
||||||
|
</hbox>
|
||||||
<description id="error" class="meetingRequestError" align="right"/>
|
<description id="error" class="meetingRequestError" align="right"/>
|
||||||
</row>
|
</row>
|
||||||
<row disabled="!@ics_method=reply">
|
<row disabled="!@ics_method=reply">
|
||||||
<description value="This mail contains a reply to a meeting request" class="meetingRequestMessage"/>
|
<description value="This mail contains a reply to a meeting request" class="meetingRequestMessage"/>
|
||||||
<button label="Apply" id="button[apply]" class="leftPad5"/>
|
<button label="Apply" id="button[apply]" class="leftPad5"/>
|
||||||
<description/>
|
|
||||||
<description/>
|
|
||||||
<description/>
|
|
||||||
<description id="error" class="meetingRequestError" align="right"/>
|
<description id="error" class="meetingRequestError" align="right"/>
|
||||||
</row>
|
</row>
|
||||||
<row disabled="!@ics_method=cancel">
|
<row disabled="!@ics_method=cancel">
|
||||||
<description value="This mail cancels a meeting" class="meetingRequestMessage"/>
|
<description value="This mail cancels a meeting" class="meetingRequestMessage"/>
|
||||||
<button label="Apply" statustext="Removes the event from my calendar" id="button[cancel]" class="leftPad5"/>
|
<hbox>
|
||||||
<button label="Delete" statustext="Delete this meeting for all participants" id="button[delete]" class="leftPad5"
|
<button label="Apply" statustext="Removes the event from my calendar" id="button[cancel]" class="leftPad5"/>
|
||||||
onclick="et2_dialog.confirm(widget,'Delete this meeting for all participants','Delete')"/>
|
<button label="Delete" statustext="Delete this meeting for all participants" id="button[delete]" class="leftPad5"
|
||||||
<buttononly statustext="Edit event in calendar" label="Edit" id="button[edit]"
|
onclick="et2_dialog.confirm(widget,'Delete this meeting for all participants','Delete')"/>
|
||||||
onclick="window.open(egw::link('/index.php','menuaction=calendar.calendar_uiforms.edit&cal_id=$cont[id]'),'_blank','dependent=yes,width=750,height=410,scrollbars=yes,status=yes'); return false;" class="leftPad5"/>
|
<button statustext="Edit event in calendar" label="Edit" id="button[edit]" background_image="true" image="edit"
|
||||||
<description/>
|
onclick="window.open(egw::link('/index.php','menuaction=calendar.calendar_uiforms.edit&cal_id=$cont[id]'),'_blank','dependent=yes,width=750,height=410,scrollbars=yes,status=yes'); return false;" class="leftPad5"/>
|
||||||
<description/>
|
</hbox>
|
||||||
<description id="error" class="meetingRequestError" align="right"/>
|
<description id="error" class="meetingRequestError" align="right"/>
|
||||||
</row>
|
</row>
|
||||||
</rows>
|
</rows>
|
||||||
@ -121,4 +134,4 @@ table.meetingRequest {
|
|||||||
|
|
||||||
</styles>
|
</styles>
|
||||||
</template>
|
</template>
|
||||||
</overlay>
|
</overlay>
|
@ -3323,15 +3323,19 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
|
|||||||
$bodyParts = $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part);
|
$bodyParts = $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part);
|
||||||
|
|
||||||
// for meeting requests (multipart alternative with text/calendar part) let calendar render it
|
// for meeting requests (multipart alternative with text/calendar part) let calendar render it
|
||||||
if ($calendar_part && isset($GLOBALS['egw_info']['user']['apps']['calendar']))
|
if ($calendar_part && isset($GLOBALS['egw_info']['user']['apps']['calendar']) && empty($smime))
|
||||||
{
|
{
|
||||||
$charset = $calendar_part->getContentTypeParameter('charset');
|
$charset = $calendar_part->getContentTypeParameter('charset');
|
||||||
// Do not try to fetch raw part content if it's smime signed message
|
// Do not try to fetch raw part content if it's smime signed message
|
||||||
if (!$smime) $this->mail_bo->fetchPartContents($uid, $calendar_part);
|
$this->mail_bo->fetchPartContents($uid, $calendar_part);
|
||||||
|
$headers = $this->mail_bo->getHeaders($mailbox, 0, 1, '', false, null, $uid);
|
||||||
Api\Cache::setSession('calendar', 'ical', array(
|
Api\Cache::setSession('calendar', 'ical', array(
|
||||||
'charset' => $charset ? $charset : 'utf-8',
|
'charset' => $charset ?: 'utf-8',
|
||||||
'attachment' => $calendar_part->getContents(),
|
'attachment' => $calendar_part->getContents(),
|
||||||
'method' => $calendar_part->getContentTypeParameter('method'),
|
'method' => $calendar_part->getContentTypeParameter('method'),
|
||||||
|
'sender' => empty($headers['header'][0]['sender_address']) ? null :
|
||||||
|
(preg_match('/<([^>]+?)>$/', $sender = strtolower($headers['header'][0]['sender_address']), $matches) ?
|
||||||
|
$matches[1] : $sender),
|
||||||
));
|
));
|
||||||
$this->mail_bo->htmlOptions = $bufferHtmlOptions;
|
$this->mail_bo->htmlOptions = $bufferHtmlOptions;
|
||||||
Api\Translation::add_app('calendar');
|
Api\Translation::add_app('calendar');
|
||||||
|
Loading…
Reference in New Issue
Block a user