Calendar can now store participants which are no accounts or contacts.

- as required by iCal/CalDAV/GroupDAV/SyncML
- this also fixes problems with LDAP contacts, which have non-numeric
  ids
- iCal code now converts to and from all participant types supported by
  eGroupWare: some types (eg. ressources) require that the clients keeps
  the new X-EGROUPWARE-UID attribute
- calendar UI allows to enter email addresses via the addressbook search
  box (dont type search, but direct add)
This commit is contained in:
Ralf Becker 2008-05-08 15:02:35 +00:00
parent 5d50d41004
commit cb9212e691
9 changed files with 271 additions and 151 deletions

View File

@ -7,7 +7,7 @@
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2004-7 by RalfBecker-At-outdoor-training.de
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
* @version $Id$
*/
require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.socal.inc.php');
@ -128,7 +128,7 @@ class bocal
*/
var $resources;
/**
* @internal
* @internal
* @var array $cached_event here we do some caching to read single events only once
*/
var $cached_event = array();
@ -156,7 +156,7 @@ class bocal
function bocal()
{
if ($this->debug > 0) $this->debug_message('bocal::bocal() started',True,$param);
$this->so = new socal();
$this->datetime = $GLOBALS['egw']->datetime;
@ -185,13 +185,49 @@ class bocal
$this->resources[$data['type']] = $data + array('app' => $app);
}
}
$this->resources['e'] = array(
'type' => 'e',
'info' => 'bocal::email_info',
'app' => 'email',
);
$GLOBALS['egw']->session->appsession('resources','calendar',$this->resources);
}
}
//echo "registered resources="; _debug_array($this->resources);
$this->config = config::read('calendar');
}
/**
* returns info about email addresses as participants
*
* @param int/array $ids single contact-id or array of id's
* @return array
*/
static function email_info($ids)
{
if (!$ids) return null;
$data = array();
foreach(!is_array($ids) ? array($ids) : $ids as $id)
{
$email = $id;
$name = '';
if (preg_match('/^(.*) *<([a-z0-9_.@-]{8,})>$/i',$email,$matches))
{
$name = $matches[1];
$email = $matches[2];
}
$data[] = array(
'res_id' => $id,
'email' => $email,
'rights' => EGW_ACL_READ_FOR_PARTICIPANTS,
'name' => $name,
);
}
//echo "<p>email_info(".print_r($ids,true).")="; _debug_array($data);
return $data;
}
/**
* Add group-members as participants with status 'G'
*
@ -244,14 +280,14 @@ class bocal
* show_rejected if set rejected invitation are shown only when true, otherwise it depends on the cal-pref or a running query
* ignore_acl if set and true no check_perms for a general EGW_ACL_READ grants is performed
* enum_groups boolean if set and true, group-members will be added as participants with status 'G'
* @return array of events or array with YYYYMMDD strings / array of events pairs (depending on $daywise param)
* @return array of events or array with YYYYMMDD strings / array of events pairs (depending on $daywise param)
* or false if there are no read-grants from _any_ of the requested users
*/
function &search($params)
{
$params_in = $params;
if (!isset($params['users']) || !$params['users'] ||
if (!isset($params['users']) || !$params['users'] ||
count($params['users']) == 1 && isset($params['users'][0]) && !$params['users'][0]) // null or '' casted to an array
{
// for a search use all account you have read grants from
@ -288,7 +324,7 @@ class bocal
foreach($members as $member)
{
// use only members which gave the user a read-grant
if (!in_array($member['account_id'],$users) &&
if (!in_array($member['account_id'],$users) &&
($params['ignore_acl'] || $this->check_perms(EGW_ACL_READ,0,$member['account_id'])))
{
$users[] = $member['account_id'];
@ -311,7 +347,7 @@ class bocal
}
}
}
// if we have no grants from the given user(s), we directly return no events / an empty array,
// if we have no grants from the given user(s), we directly return no events / an empty array,
// as calling the so-layer without users would give the events of all users (!)
if (!count($users))
{
@ -340,7 +376,7 @@ class bocal
$users,$cat_id,$filter,$params['query'],$offset,(int)$params['num_rows'],$params['order'],$show_rejected);
$this->total = $this->so->total;
$this->db2data($events,isset($params['date_format']) ? $params['date_format'] : 'ts');
// socal::search() returns rejected group-invitations, as only the user not also the group is rejected
// as we cant remove them efficiantly in SQL, we kick them out here, but only if just one user is displayed
$remove_rejected_by_user = !$show_rejected && count($params['users']) == 1 ? $params['users'][0] : false;
@ -397,7 +433,7 @@ class bocal
if ($ymd != ($last = $this->date2string($e_end)))
{
$daysEvents[$last][] =& $events[$k];
}
}
}
$events =& $daysEvents;
if ($this->debug && ($this->debug > 2 || $this->debug == 'search'))
@ -430,7 +466,7 @@ class bocal
}
return $events;
}
/**
* Clears all non-private info from a privat event
*
@ -452,7 +488,7 @@ class bocal
'non_blocking' => $event['non_blocking'],
);
}
/**
* check and evtl. move the horizont (maximum date for unlimited recuring events) to a new date
*
@ -466,7 +502,7 @@ class bocal
$this->debug_message('bocal::check_move_horizont(%1) horizont=%2',true,$new_horizont,$this->config['horizont']);
}
$new_horizont = $this->date2ts($new_horizont,true); // now we are in server-time, where this function operates
if ($new_horizont > time()+1000*DAY_s) // some user tries to "look" more then 1000 days in the future
{
if ($this->debug == 'check_move_horizont') $this->debug_message('bocal::check_move_horizont(%1) horizont=%2 new horizont more then 1000 days from now --> ignoring it',true,$new_horizont,$this->config['horizont']);
@ -500,7 +536,7 @@ class bocal
// update the horizont
$config =& CreateObject('phpgwapi.config','calendar');
$config->save_value('horizont',$this->config['horizont'],'calendar');
if ($this->debug == 'check_move_horizont') $this->debug_message('bocal::check_move_horizont(%1) new horizont=%2, exiting',true,$new_horizont,$this->config['horizont']);
}
@ -523,7 +559,7 @@ class bocal
$event['participants'] = $event_read['participants'];
}
if (!$start) $start = $event['start'];
$events = array();
$this->insert_all_repetitions($event,$start,$this->date2ts($this->config['horizont'],true),$events,null);
@ -532,7 +568,7 @@ class bocal
$this->so->recurrence($event['id'],$this->date2ts($event['start'],true),$this->date2ts($event['end'],true),$event['participants']);
}
}
/**
* convert data read from the db, eg. convert server to user-time
*
@ -569,7 +605,7 @@ class bocal
}
}
}
/**
* convert a date from server to user-time
*
@ -579,7 +615,7 @@ class bocal
function date2usertime($ts,$date_format='ts')
{
if (empty($ts)) return $ts;
switch ($date_format)
{
case 'ts':
@ -612,7 +648,7 @@ class bocal
if ($ignore_acl || is_array($ids) || ($return = $this->check_perms(EGW_ACL_READ,$ids,0,$date_format,$date)))
{
if (is_array($ids) || !isset($this->cached_event['id']) || $this->cached_event['id'] != $ids ||
if (is_array($ids) || !isset($this->cached_event['id']) || $this->cached_event['id'] != $ids ||
$this->cached_event_date_format != $date_format ||
$this->cached_event['recur_type'] != MCAL_RECUR_NONE && !is_null($date) && (!$date || $this->cached_event['start'] < $date))
{
@ -621,7 +657,7 @@ class bocal
if ($events)
{
$this->db2data($events,$date_format);
if (is_array($ids))
{
$return =& $events;
@ -672,7 +708,7 @@ class bocal
$this->debug_message('bocal::insert_all_repitions(%1,%2,%3,&$event,%4)',true,$event,$start,$end,$recur_exceptions);
}
$start_in = $start; $end_in = $end;
$start = $this->date2ts($start);
$end = $this->date2ts($end);
$event_start_ts = $this->date2ts($event['start']);
@ -684,7 +720,7 @@ class bocal
}
$id = $event['id'];
$event_start_arr = $this->date2array($event['start']);
// to be able to calculate the repetitions as difference to the start-date,
// to be able to calculate the repetitions as difference to the start-date,
// both need to be calculated without daylight saving: mktime(,,,,,,0)
$event_start_daybegin_ts = adodb_mktime(0,0,0,$event_start_arr['month'],$event_start_arr['day'],$event_start_arr['year'],0);
@ -710,7 +746,7 @@ class bocal
$search_date_ymd = (int)$this->date2string($ts);
$have_exception = !is_null($recur_exceptions) && isset($recur_exceptions[$search_date_ymd]);
if (!$have_exception) // no execption by an edited event => check the deleted ones
{
foreach((array)$event['recur_exception'] as $exception_ts)
@ -731,7 +767,7 @@ class bocal
$search_date_month = adodb_date('m',$ts);
$search_date_day = adodb_date('d',$ts);
$search_date_dow = adodb_date('w',$ts);
// to be able to calculate the repetitions as difference to the start-date,
// to be able to calculate the repetitions as difference to the start-date,
// both need to be calculated without daylight saving: mktime(,,,,,,0)
$search_beg_day = adodb_mktime(0,0,0,$search_date_month,$search_date_day,$search_date_year,0);
@ -879,10 +915,32 @@ class bocal
function resource_info($uid)
{
static $res_info_cache = array();
if (!isset($res_info_cache[$uid]))
{
list($res_info_cache[$uid]) = $this->resources[$uid{0}]['info'] ? ExecMethod($this->resources[$uid{0}]['info'],substr($uid,1)) : false;
if (is_numeric($uid))
{
$info = array(
'res_id' => $uid,
'email' => $GLOBALS['egw']->accounts->id2name($uid,'account_email'),
'name' => trim($GLOBALS['egw']->accounts->id2name($uid,'account_firstname'). ' ' .
$GLOBALS['egw']->accounts->id2name($uid,'account_lastname')),
'type' => $GLOBALS['egw']->accounts->get_type($uid),
);
}
else
{
list($info) = $this->resources[$uid[0]]['info'] ? ExecMethod($this->resources[$uid[0]]['info'],substr($uid,1)) : false;
if ($info)
{
$info['type'] = $uid[0];
if (!$info['email'] && $info['responsible'])
{
$info['email'] = $GLOBALS['egw']->accounts->id2name($info['responsible'],'account_email');
}
}
}
$res_info_cache[$uid] = $info;
}
if ($this->debug && ($this->debug > 2 || $this->debug == 'resource_info'))
{
@ -938,7 +996,7 @@ class bocal
}
$user = $GLOBALS['egw_info']['user']['account_id'];
$grants = $this->grants[$owner];
if (is_array($event) && $needed == EGW_ACL_READ)
{
// Check if the $user is one of the participants or has a read-grant from one of them
@ -946,7 +1004,7 @@ class bocal
//
foreach($event['participants'] as $uid => $accept)
{
if ($uid == $user || $uid < 0 && in_array($user,$GLOBALS['egw']->accounts->members($uid,true)))
if ($uid == $user || $uid < 0 && in_array($user,$GLOBALS['egw']->accounts->members($uid,true)))
{
// if we are a participant, we have an implicite READ and PRIVAT grant
$grants |= EGW_ACL_READ | EGW_ACL_PRIVATE;
@ -956,14 +1014,14 @@ class bocal
{
// if we have a READ grant from a participant, we dont give an implicit privat grant too
$grants |= EGW_ACL_READ;
// we cant break here, as we might be a participant too, and would miss the privat grant
// we cant break here, as we might be a participant too, and would miss the privat grant
}
elseif (!is_numeric($uid))
{
// if we have a resource as participant
$resource = $this->resource_info($uid);
$grants |= $resource['rights'];
}
}
}
}
@ -1105,10 +1163,10 @@ class bocal
else
{
$date = $this->date2ts($date,False);
// if timezone is requested, we dont need to convert to user-time
if (($tz_used = substr($format,-1)) == 'O' || $tz_used == 'Z') $server2user = false;
if ($server2user && substr($format,-1) )
{
$date += $this->tz_offset_s;
@ -1249,7 +1307,7 @@ class bocal
if ($display_day)
{
$range = lang(adodb_date('l',$first['raw'])).($this->common_prefs['dateformat']{0} != 'd' ? ' ' : ', ');
$range = lang(adodb_date('l',$first['raw'])).($this->common_prefs['dateformat'][0] != 'd' ? ' ' : ', ');
}
for ($i = 0; $i < 5; $i += 2)
{
@ -1319,7 +1377,7 @@ class bocal
}
return $range;
}
/**
* Displays a timespan, eg. $both ? "10:00 - 13:00: 3h" (10:00 am - 1 pm: 3h) : "10:00 3h" (10:00 am 3h)
*
@ -1334,7 +1392,7 @@ class bocal
$duration = floor($duration/60).lang('h').($duration%60 ? $duration%60 : '');
$timespan = $t = $GLOBALS['egw']->common->formattime(sprintf('%02d',$start_m/60),sprintf('%02d',$start_m%60));
if ($both) // end-time too
{
$timespan .= ' - '.$GLOBALS['egw']->common->formattime(sprintf('%02d',$end_m/60),sprintf('%02d',$end_m%60));
@ -1357,14 +1415,18 @@ class bocal
function participant_name($id,$use_type=false)
{
static $id2lid = array();
if ($use_type && $use_type != 'u') $id = $use_type.$id;
if (!isset($id2lid[$id]))
{
if (!is_numeric($id))
{
$id2lid[$id] = egw_link::title($this->resources[$id{0}]['app'],substr($id,1));
$id2lid[$id] = '#'.$id;
if (($info = $this->resource_info($id)))
{
$id2lid[$id] = $info['name'] ? $info['name'] : $info['email'];
}
}
else
{
@ -1507,12 +1569,12 @@ class bocal
return $users + $groups; // users first and then groups, both alphabeticaly
}
/**
* Convert the recure-information of an event, into a human readable string
*
* @param array $event
* @return string
* @return string
*/
function recure2string($event)
{
@ -1588,7 +1650,7 @@ class bocal
}
$this->holidays->prepare_read_holidays($year);
$this->cached_holidays[$year] = $this->holidays->read_holiday();
// search for birthdays
if ($GLOBALS['egw_info']['server']['hide_birthdays'] != 'yes')
{
@ -1613,7 +1675,7 @@ class bocal
}
}
}
// store holidays and birthdays in the session
// store holidays and birthdays in the session
$this->cached_holidays = $GLOBALS['egw']->session->appsession('holidays','calendar',$this->cached_holidays);
}
if ((int) $this->debug >= 2 || $this->debug == 'read_holidays')
@ -1622,10 +1684,10 @@ class bocal
}
return $this->cached_holidays[$year];
}
/**
* get title for an event identified by $event
*
*
* Is called as hook to participate in the linking
*
* @param int/array $entry int cal_id or array with event
@ -1661,7 +1723,7 @@ class bocal
}
return $result;
}
/**
* Hook called by link-class to include calendar in the appregistry of the linkage
*
@ -1682,7 +1744,7 @@ class bocal
'menuaction' => 'calendar.uiforms.edit',
),
'add_app' => 'link_app',
'add_id' => 'link_id',
'add_id' => 'link_id',
'add_popup' => '750x400',
);
}
@ -1744,7 +1806,7 @@ class bocal
$GLOBALS['egw']->preferences->save_repository(False,'default');
}
}
/**
* Get the freebusy URL of a user
*
@ -1755,7 +1817,7 @@ class bocal
{
if (is_numeric($user)) $user = $GLOBALS['egw']->accounts->id2name($user);
return (!$GLOBALS['egw_info']['server']['webserver_url'] || $GLOBALS['egw_info']['server']['webserver_url']{0} == '/' ?
return (!$GLOBALS['egw_info']['server']['webserver_url'] || $GLOBALS['egw_info']['server']['webserver_url'][0] == '/' ?
($_SERVER['HTTPS'] ? 'https://' : 'http://').$_SERVER['HTTP_HOST'] : '').
$GLOBALS['egw_info']['server']['webserver_url'].'/calendar/freebusy.php?user='.urlencode($user).
($pw ? '&password='.urlencode($pw) : '');

View File

@ -155,7 +155,7 @@ class bocalupdate extends bocal
$users = array_unique($users);
}
$users[] = $uid;
if (in_array($uid{0},$types_with_quantity))
if (in_array($uid[0],$types_with_quantity))
{
$quantity[$uid] = max(1,(int) substr($status,2));
}
@ -188,19 +188,19 @@ class bocalupdate extends bocal
$common_parts = array_intersect($users,array_keys($overlap['participants']));
foreach($common_parts as $n => $uid)
{
if ($overlap['participants'][$uid]{0} == 'R')
if ($overlap['participants'][$uid][0] == 'R')
{
unset($common_parts[$uid]);
continue;
}
if (is_numeric($uid) || !in_array($uid{0},$types_with_quantity))
if (is_numeric($uid) || !in_array($uid[0],$types_with_quantity))
{
continue; // no quantity check: quantity allways 1 ==> conflict
}
if (!isset($max_quantity[$uid]))
{
$res_info = $this->resource_info($uid);
$max_quantity[$uid] = $res_info[$this->resources[$uid{0}]['max_quantity']];
$max_quantity[$uid] = $res_info[$this->resources[$uid[0]]['max_quantity']];
}
$quantity[$uid] += max(1,(int) substr($overlap['participants'][$uid],2));
if ($quantity[$uid] <= $max_quantity[$uid])
@ -741,7 +741,7 @@ class bocalupdate extends bocal
*/
function check_status_perms($uid,$event)
{
if ($uid{0} == 'c') // for contact we use the owner of the event
if ($uid[0] == 'c' || $uid['0'] == 'e') // for contact we use the owner of the event
{
if (!is_array($event) && !($event = $this->read($event))) return false;
@ -774,7 +774,7 @@ class bocalupdate extends bocal
{
return false;
}
if (($Ok = $this->so->set_status($cal_id,is_numeric($uid)?'u':$uid{0},is_numeric($uid)?$uid:substr($uid,1),$status,$recur_date ? $this->date2ts($recur_date,true) : 0)))
if (($Ok = $this->so->set_status($cal_id,is_numeric($uid)?'u':$uid[0],is_numeric($uid)?$uid:substr($uid,1),$status,$recur_date ? $this->date2ts($recur_date,true) : 0)))
{
$GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'modify',time());
@ -850,7 +850,7 @@ class bocalupdate extends bocal
$eventStart_arr = $this->date2array($event['start']); // give this as 'date' to the link to pick the right recurrence for the participants state
$link = $GLOBALS['egw_info']['server']['webserver_url'].'/index.php?menuaction=calendar.uiforms.edit&cal_id='.$event['id'].'&date='.$eventStart_arr['full'].'&no_popup=1';
// if url is only a path, try guessing the rest ;-)
if ($link{0} == '/')
if ($link[0] == '/')
{
$link = ($GLOBALS['egw_info']['server']['enforce_ssl'] || $_SERVER['HTTPS'] ? 'https://' : 'http://').
($GLOBALS['egw_info']['server']['hostname'] ? $GLOBALS['egw_info']['server']['hostname'] : $_SERVER['HTTP_HOST']).

View File

@ -191,14 +191,9 @@
case 'ATTENDEE':
foreach((array)$event['participants'] as $uid => $status)
{
// ToDo, this needs to deal with resources too!!!
if (!is_numeric($uid)) continue;
$mailto = $GLOBALS['egw']->accounts->id2name($uid,'account_email');
$cn = trim($GLOBALS['egw']->accounts->id2name($uid,'account_firstname'). ' ' .
$GLOBALS['egw']->accounts->id2name($uid,'account_lastname'));
if (!($info = $this->resource_info($uid))) continue;
// RB: MAILTO href contains only the email-address, NO cn!
$attributes['ATTENDEE'][] = $mailto ? 'MAILTO:'.$mailto : '';
$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
@ -206,7 +201,7 @@
// 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 (is_numeric($uid) ? $GLOBALS['egw']->accounts->get_type($uid) : $uid{0})
switch ($info['type'])
{
case 'g':
$cutype = 'GROUP';
@ -214,21 +209,22 @@
case 'r':
$cutype = 'RESOURCE';
break;
case 'u':
case 'u': // account
case 'c': // contact
case 'e': // email address
$cutype = 'INDIVIDUAL';
break;
default:
$cutype = 'UNKNOWN';
$cutype = 'INDIVIDUAL';
break;
};
$parameters['ATTENDEE'][] = array(
'CN' => $cn,
'CN' => $info['name'],
'ROLE' => $role,
'PARTSTAT' => $status,
'CUTYPE' => $cutype,
'RSVP' => $rsvp,
);
)+($info['type'] != 'e' ? array('X-EGROUPWARE-UID' => $uid) : array());
}
break;
@ -563,7 +559,7 @@
break;
case 'RRULE':
$recurence = $attributes['value'];
$type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence{0};
$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))
{
@ -768,28 +764,46 @@
}
break;
case 'ATTENDEE':
if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
($uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email')))
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)
{
$event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ?
// 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.bocontacts.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');
}
if (preg_match('/<([@.a-z0-9_-]+)>/i',$attributes['value'],$matches)) {
$uid = '';
$uid = $GLOBALS['egw']->accounts->name2id($matches[1],'account_email');
if(!empty($uid)) {
$event['participants'][$uid] = isset($attributes['params']['PARTSTAT']) ?
$this->status_ical2egw[strtoupper($attributes['params']['PARTSTAT'])] :
($uid == $event['owner'] ? 'A' : 'U');
}
}
if($attributes['value'] == 'Unknown') {
$event['participants'][$GLOBALS['egw_info']['user']['account_id']] = 'A';
}
break;
case 'ORGANIZER': // will be written direct to the event
if (preg_match('/MAILTO:([@.a-z0-9_-]+)/i',$attributes['value'],$matches) &&
@ -1195,7 +1209,7 @@
break;
case 'RRULE':
$recurence = $attributes['value'];
$type = preg_match('/FREQ=([^;: ]+)/i',$recurence,$matches) ? $matches[1] : $recurence{0};
$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))
{

View File

@ -690,8 +690,8 @@ ORDER BY cal_user_type, cal_usre_id
* combines user_type and user_id into a single string or integer (for users)
*
* @param string $user_type 1-char type: 'u' = user, ...
* @param int $user_id id
* @return string/int combined id
* @param string|int $user_id id
* @return string|int combined id
*/
function combine_user($user_type,$user_id)
{
@ -705,9 +705,9 @@ ORDER BY cal_user_type, cal_usre_id
/**
* splits the combined user_type and user_id into a single values
*
* @param string $user_type 1-char type: 'u' = user, ...
* @param int $user_id id
* @return string/int
* @param string|int $uid
* @param string &$user_type 1-char type: 'u' = user, ...
* @param string|int &$user_id id
*/
function split_user($uid,&$user_type,&$user_id)
{
@ -719,7 +719,7 @@ ORDER BY cal_user_type, cal_usre_id
else
{
$user_type = $uid[0];
$user_id = (int) substr($uid,1);
$user_id = substr($uid,1);
}
}

View File

@ -30,7 +30,7 @@ class uical
var $debug=false;
/**
* instance of the bocal or bocalupdate class
*
*
* @var bocalupdate
*/
var $bo;
@ -116,7 +116,7 @@ class uical
* @var string $view menuaction of the selected view
*/
var $view_menuaction;
/**
* @var int $first first day of the shown view
*/
@ -162,11 +162,11 @@ class uical
$this->manage_states($set_states);
$GLOBALS['uical'] = &$this; // make us available for ExecMethod, else it creates a new instance
// calendar does not work with hidden sidebox atm.
unset($GLOBALS['egw_info']['user']['preferences']['common']['auto_hide_sidebox']);
}
/**
* Checks and terminates (or returns for home) with a message if $this->owner include a user/resource we have no read-access to
*
@ -187,7 +187,7 @@ class uical
if (!$this->bo->check_perms(EGW_ACL_READ|EGW_ACL_READ_FOR_PARTICIPANTS,0,$member))
{
$no_access_group[$member] = $this->bo->participant_name($member);
}
}
}
}
elseif (!$this->bo->check_perms(EGW_ACL_READ|EGW_ACL_READ_FOR_PARTICIPANTS,0,$owner))
@ -198,7 +198,7 @@ class uical
if (count($no_access))
{
$msg = '<p class="redItalic" align="center">'.lang('Access denied to the calendar of %1 !!!',implode(', ',$no_access))."</p>\n";
if ($GLOBALS['egw_info']['flags']['currentapp'] == 'home')
{
return $msg;
@ -217,7 +217,7 @@ class uical
}
return false;
}
/**
* show the egw-framework plus possible messages ($_GET['msg'] and $this->group_warning from check_owner_access)
*/
@ -225,7 +225,7 @@ class uical
{
$GLOBALS['egw_info']['flags']['include_xajax'] = true;
$GLOBALS['egw']->common->egw_header();
if ($_GET['msg']) echo '<p class="redItalic" align="center">'.html::htmlspecialchars($_GET['msg'])."</p>\n";
if ($this->group_warning) echo '<p class="redItalic" align="center">'.$this->group_warning."</p>\n";
@ -282,11 +282,11 @@ class uical
}
else // change only the owners of the given type
{
$res_type = is_numeric($set_owners[0]) ? false : $set_owners[0]{0};
$res_type = is_numeric($set_owners[0]) ? false : $set_owners[0][0];
$owners = explode(',',$states['owner'] ? $states['owner'] : $default);
foreach($owners as $key => $owner)
{
if (!$res_type && is_numeric($owner) || $res_type && $owner{0} == $res_type)
if (!$res_type && is_numeric($owner) || $res_type && $owner[0] == $res_type)
{
unset($owners[$key]);
}
@ -342,7 +342,7 @@ class uical
{
if ($this->cal_prefs['planner_start_with_group'] > 0) $this->cal_prefs['planner_start_with_group'] *= -1; // fix old 1.0 pref
if (!$states_session && !$_GET['menuaction']) $this->view = ''; // first call to calendar
if (!$states_session && !$_GET['menuaction']) $this->view = ''; // first call to calendar
if ($func == 'planner' && $this->view != 'planner' && $this->owner == $this->user)
{
@ -390,7 +390,7 @@ class uical
// icons for single user, multiple users or group(s) and resources
foreach($event['participants'] as $uid => $status)
{
if(is_numeric($uid) || $uid{0} == 'c')
if(is_numeric($uid) || !isset($this->bo->resources[$uid[0]]['icon']))
{
if (isset($icons['single']) || $GLOBALS['egw']->accounts->get_type($uid) == 'g')
{
@ -401,12 +401,12 @@ class uical
{
$icons['single'] = html::image('calendar','single');
}
}
elseif(!isset($icons[$uid{0}]) && isset($this->bo->resources[$uid{0}]) && isset($this->bo->resources[$uid{0}]['icon']))
}
elseif(!isset($icons[$uid[0]]) && isset($this->bo->resources[$uid[0]]) && isset($this->bo->resources[$uid[0]]['icon']))
{
$icons[$uid{0}] = html::image($this->bo->resources[$uid{0}]['app'],
($this->bo->resources[$uid{0}]['icon'] ? $this->bo->resources[$uid{0}]['icon'] : 'navbar'),
lang($this->bo->resources[$uid{0}]['app']),
$icons[$uid[0]] = html::image($this->bo->resources[$uid[0]]['app'],
($this->bo->resources[$uid[0]]['icon'] ? $this->bo->resources[$uid[0]]['icon'] : 'navbar'),
lang($this->bo->resources[$uid[0]]['app']),
'width="16px" height="16px"');
}
}
@ -475,7 +475,7 @@ class uical
return html::a_href($content,'/index.php',$vars,' target="_blank" title="'.html::htmlspecialchars(lang('Add')).
'" onclick="'.$this->popup('this.href','this.target').'; return false;"');
}
/**
* returns javascript to open a popup window: window.open(...)
*
@ -487,7 +487,7 @@ class uical
*/
function popup($link,$target='_blank',$width=750,$height=410,$Link_confirm_abort='',$Link_confirm_text='')
{
//Handle Exception for Calandar
//Handle Exception for Calandar
if (($Link_confirm_abort) && ($Link_confirm_text))
{
$returnvalue = 'javascript:var check=confirm(\''.$Link_confirm_text.'\');';
@ -495,21 +495,21 @@ class uical
// open confirm =0kay
$returnvalue .= 'egw_openWindowCentered2('.($link == 'this.href' ? $link : "'".$link."'").','.
($target == 'this.target' ? $target : "'".$target."'").",$width,$height,'yes')";
$returnvalue .= '}';
$returnvalue .= '}';
//open confirm =Abort
$returnvalue .=' else {';
$returnvalue .= 'egw_openWindowCentered2('.($Link_confirm_abort == 'this.href' ? $Link_confirm_abort : "'".$Link_confirm_abort."'").','.
($target == 'this.target' ? $target : "'".$target."'").",$width,$height,'yes')";
$returnvalue .= '}';
return $returnvalue;
}
else {
return 'egw_openWindowCentered2('.($link == 'this.href' ? $link : "'".$link."'").','.
($target == 'this.target' ? $target : "'".$target."'").",$width,$height,'yes')";
}
}
@ -631,7 +631,7 @@ class uical
'week' => 'calendar.uiviews.week',
'month' => 'calendar.uiviews.month') as $view => $menuaction)
{
if ($this->view == 'planner' || $this->view == 'listview')
if ($this->view == 'planner' || $this->view == 'listview')
{
switch($view)
{
@ -743,12 +743,12 @@ function load_cal(url,id) {
)),
'target' => '_blank',
);
}
}
*/
$appname = 'calendar';
$menu_title = lang('Calendar Menu');
display_sidebox($appname,$menu_title,$file);
// resources menu hooks
foreach ($this->bo->resources as $resource)
{

View File

@ -123,16 +123,16 @@ class uiforms extends uical
{
$participants[$uid] = $participant_types['u'][$uid] = $uid == $this->user ? 'A' : 'U';
}
elseif (is_array($this->bo->resources[$uid{0}]))
elseif (is_array($this->bo->resources[$uid[0]]))
{
$res_data = $this->bo->resources[$uid{0}];
$res_data = $this->bo->resources[$uid[0]];
list($id,$quantity) = explode(':',substr($uid,1));
$participants[$uid] = $participant_types[$uid{0}][$id] = ($res_data['new_status'] ? ExecMethod($res_data['new_status'],$id) : 'U').
$participants[$uid] = $participant_types[$uid[0]][$id] = ($res_data['new_status'] ? ExecMethod($res_data['new_status'],$id) : 'U').
((int) $quantity > 1 ? (int)$quantity : '');
// if new_status == 'x', resource is not bookable
if(strpos($participant_types[$uid{0}][$id],'x') !== false)
if(strpos($participant_types[$uid[0]][$id],'x') !== false)
{
unset($participant_types[$uid{0}][$id]);
unset($participant_types[$uid[0]][$id]);
unset($participants[$uid]);
}
}
@ -235,18 +235,40 @@ class uiforms extends uical
{
switch($key)
{
case 'add':
if (!$content['participants']['account'] && !$content['participants']['resource'])
{
$msg = lang('You need to select an account, contact or resource first!');
}
break;
case 'delete': // handled in default
case 'quantity': // handled in new_resource
case 'cal_resources':
break;
case 'add':
// email or rfc822 addresse (eg. "Ralf Becker <ralf@domain.com>") in the search field
// ToDo: get eTemplate to return that field
if (($email = $_POST['exec']['participants']['resource']['query']) &&
(preg_match('/^(.*<)?([a-z0-9_.@-]{8,})>?$/i',$email,$matches)))
{
// check if email belongs to account or contact --> prefer them over just emails
if (($data = $GLOBALS['egw']->accounts->name2id($matches[2],'account_email')))
{
$event['participants'][$data] = $event['participant_types']['u'][$data] = 'U';
}
elseif ((list($data) = ExecMethod2('addressbook.bocontacts.search',array(
'email' => $matches[2],
'email_home' => $matches[2],
),true,'','','',false,'OR')))
{
$event['participants']['c'.$data['id']] = $event['participant_types']['c'][$data['id']] = 'U';
}
else
{
$event['participants']['e'.$email] = $event['participant_types']['e'][$email] = 'U';
}
}
elseif (!$content['participants']['account'] && !$content['participants']['resource'])
{
$msg = lang('You need to select an account, contact or resource first!');
}
break;
case 'resource':
list($app,$id) = explode(':',$data);
foreach($this->bo->resources as $type => $data) if ($data['app'] == $app) break;
@ -288,7 +310,7 @@ class uiforms extends uical
else
{
$id = substr($uid,1);
$type = $uid{0};
$type = $uid[0];
}
if ($data['old_status'] != $status)
{
@ -563,6 +585,7 @@ class uiforms extends uical
function custom_mail($event,$added)
{
$to = array();
foreach($event['participants'] as $uid => $status)
{
if ($status == 'R' || $uid == $this->user) continue;
@ -586,17 +609,9 @@ class uiforms extends uical
$to[] = $firstname.' '.$lastname.' <'.$email.'>';
}
}
elseif ($uid{0} == 'c' )
elseif(($info = $this->bo->resource_info($uid)))
{
if (!is_object($GLOBALS['egw']->contacts))
{
require_once(EGW_API_INC.'/class.contacts.inc.php');
$GLOBALS['egw']->contacts = new contacts();
}
if (($contact = $GLOBALS['egw']->contacts->read(substr($uid,1))) && ($contact['email'] || $contact['email_home']))
{
$to[] = $contact['n_fn'].' <'.($contact['email']?$contact['email']:$contact['email_home']).'>';
}
$to[] = $info['email'];
}
}
list($subject,$body) = $this->bo->get_update_message($event,$added ? MSG_ADDED : MSG_MODIFIED); // update-message is in TZ of the user
@ -767,16 +782,26 @@ class uiforms extends uical
$preserv['participants'][$row] = $content['participants'][$row] = array(
'app' => $name == 'accounts' ? ($GLOBALS['egw']->accounts->get_type($id) == 'g' ? 'Group' : 'User') : $name,
'uid' => $uid,
'status' => $status{0},
'old_status' => $status{0},
'status' => $status[0],
'old_status' => $status[0],
'quantity' => substr($status,1),
);
$readonlys[$row.'[quantity]'] = $type == 'u' || !isset($this->bo->resources[$type]['max_quantity']);
$readonlys[$row.'[status]'] = $readonlys[$row.'[status_recurrence]'] = !$this->bo->check_status_perms($uid,$event);
$readonlys["delete[$uid]"] = !$this->bo->check_perms(EGW_ACL_EDIT,$event);
$content['participants'][$row++]['title'] = $name == 'accounts' ?
$GLOBALS['egw']->common->grab_owner_name($id) : egw_link::title($name,$id);
// todo: make the participants available as links with email as title
if ($name == 'accounts')
{
$content['participants'][$row++]['title'] = $GLOBALS['egw']->common->grab_owner_name($id);
}
elseif (($info = $this->bo->resource_info($uid)))
{
$content['participants'][$row++]['title'] = $info['name'] ? $info['name'] : $info['email'];
}
else
{
$content['participants'][$row++]['title'] = '#'.$uid;
}
// enumerate group-invitations, so people can accept/reject them
if ($name == 'accounts' && $GLOBALS['egw']->accounts->get_type($id) == 'g' &&
($members = $GLOBALS['egw']->accounts->members($id,true)))

View File

@ -12,7 +12,7 @@
/* $Id$ */
$setup_info['calendar']['name'] = 'calendar';
$setup_info['calendar']['version'] = '1.5.001';
$setup_info['calendar']['version'] = '1.5.002';
$setup_info['calendar']['app_order'] = 3;
$setup_info['calendar']['enable'] = 1;
@ -80,3 +80,4 @@

View File

@ -67,10 +67,10 @@
),
'egw_cal_user' => array(
'fd' => array(
'cal_id' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'cal_id' => array('type' => 'int','precision' => '4','nullable' => False),
'cal_recur_date' => array('type' => 'int','precision' => '8','default' => '0'),
'cal_user_type' => array('type' => 'varchar','precision' => '1','nullable' => False,'default' => 'u'),
'cal_user_id' => array('type' => 'int','precision' => '4','nullable' => False,'default' => '0'),
'cal_user_id' => array('type' => 'varchar','precision' => '128','nullable' => False),
'cal_status' => array('type' => 'char','precision' => '1','default' => 'A'),
'cal_quantity' => array('type' => 'int','precision' => '4','default' => '1')
),

View File

@ -1599,4 +1599,22 @@
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.5.001';
}
$test[] = '1.5.001';
function calendar_upgrade1_5_001()
{
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_cal_user','cal_id',array(
'type' => 'int',
'precision' => '4',
'nullable' => False
));
$GLOBALS['egw_setup']->oProc->AlterColumn('egw_cal_user','cal_user_id',array(
'type' => 'varchar',
'precision' => '128',
'nullable' => False
));
return $GLOBALS['setup_info']['calendar']['currentver'] = '1.5.002';
}
?>