From c7c6404500c9a3b36ec1f81e9e8edd356e0f62bc Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 14 Jun 2005 21:54:17 +0000 Subject: [PATCH] next step: - view & edit is now integrated in the views - view has now buttons to edit, edit-series, copy, delete and delete-series - add: participants/resources can now be set via the url - conflict check is coded, but need testing and a conflict display --- calendar/inc/class.bocalupdate.inc.php | 227 +++++++++++++++++++++++++ calendar/templates/default/app.css | 2 + 2 files changed, 229 insertions(+) create mode 100644 calendar/inc/class.bocalupdate.inc.php diff --git a/calendar/inc/class.bocalupdate.inc.php b/calendar/inc/class.bocalupdate.inc.php new file mode 100644 index 0000000000..a50f7b86d6 --- /dev/null +++ b/calendar/inc/class.bocalupdate.inc.php @@ -0,0 +1,227 @@ + * +* -------------------------------------------- * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU General Public License as published by the * +* Free Software Foundation; either version 2 of the License, or (at your * +* option) any later version. * +\**************************************************************************/ + +/* $Id$ */ + +require_once(EGW_INCLUDE_ROOT.'/calendar/inc/class.bocal.inc.php'); + +/** + * Class to access AND manipulate all calendar data + * + * Note: All new code should only access this class or bocal and NOT bocalendar!!! + * + * The new UI, BO and SO classes have a strikt definition, in which time-zone they operate: + * UI only operates in user-time, so there have to be no conversation at all !!! + * BO's functions take and return user-time only (!), they convert internaly everything to servertime, because + * SO operates only in server-time (please note, this is not the case with the socalendar*-classes used atm.) + * + * As this BO class deals with dates/times of several types and timezone, each variable should have a postfix + * appended, telling with type it is: _s = seconds, _su = secs in user-time, _ss = secs in server-time, _h = hours + * + * All new BO code (should be true for eGW in general) NEVER use any $_REQUEST ($_POST or $_GET) vars itself. + * Nor does it store the state of any UI-elements (eg. cat-id selectbox). All this is the task of the UI class !!! + * + * All permanent debug messages of the calendar-code should done via the debug-message method of the class !!! + * + * @package calendar + * @author RalfBecker@outdoor-training.de + * @license GPL + */ + +class bocalupdate extends bocal +{ + /** + * Constructor + */ + function bocalupdate() + { + if ($this->debug > 0) $this->debug_message('bocalupdate::bocalupdate() started',True); + + $this->bocal(); // calling the parent constructor + + if ($this->debug > 0) $this->debug_message('bocalupdate::bocalupdate() finished',True); + } + + /** + * updates or creates an event, it (optionaly) checks for conflicts and sends the necessary notifications + * + * @param array &$event event-array, on return some values might be changed due to set defaults + * @param boolean $ignore_conflicts=false just ignore conflicts or do a conflict check and return the conflicting events + * @param boolean $touch_modified=true touch modificatin time and set modifing user, default true=yes + * @return mixed on success: int $cal_id > 0, on error false or array with conflicting events (only if $check_conflicts) + */ + function update(&$event,$ignore_conflicts=false,$touch_modified=true) + { + // check some minimum requirements: + // - new events need start, end and title + // - updated events cant set start, end or title to empty + if (!$event['id'] && (!$event['start'] || !$event['end'] || !$event['title']) || + $event['id'] && (isset($event['start']) && !$event['start'] || isset($event['end']) && !$event['end'] || isset($event['title']) && !$event['title'])) + { + return false; + } + if (!$event['id']) // some defaults for new entries + { + // if no owner given, set user to owner + if (!$event['owner']) $event['owner'] = $this->user; + // set owner as participant if none is given + if (!$event['id'] && (!is_array($event['participants']) || !count($event['participants']))) + { + $event['participants'][$event['owner']] = 'U'; + } + // set the status of the current user to 'A' = accepted + if (isset($event['participants'][$this->user]) && $event['participants'][$this->user] != 'A') + { + $event['participants'][$this->user] = 'A'; + } + } + // check for conflicts only happens !$ignore_conflicts AND if start + end date are given + if (!$ignore_conflicts && $event['non_blocking'] && isset($event['start']) && isset($event['end'])) + { + $types_with_quantity = array(); + foreach($this->resources as $type => $data) + { + if ($data['use_quantity']) $types_with_quantity[] = $type; + } + // get all NOT rejected participants and evtl. their quantity + $quantity = $users = array(); + foreach($event['participants'] as $uid => $status) + { + if ($status[0] == 'R') continue; // ignore rejected participants + + $users[] = $uid; + if (in_array($uid[0],$types_with_quantity)) + { + $quantity[$uid] = min(1,(int) substr($overlap['participants']['uid'],2)); + } + } + $overlapping_events =& $this->search(array( + 'start' => $event['start'], + 'end' => $event['end'], + 'users' => $users, + 'query' => array('cal_non_blocking != 1'), // ignore non-blocking events + )); + $max_quantity = $possible_quantity_conflicts = $conflicts = array(); + foreach((array) $overlapping_events as $k => $overlap) + { + echo "checking overlaping event"; _debug_array($overlap); + // check if the overlap is with a rejected participant or within the allowed quantity + $common_parts = array_intersect($users,array_keys($overlap['participants'])); + foreach($common_parts as $n => $uid) + { + if ($overlap['participants'][$uid][0] == 'R') + { + unset($common_parts[$uid]); + continue; + } + 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 = ExecMethod($this->resources[$uid[0]]['info']); + $max_quantity[$uid] = $res_info[$this->resources[$uid[0]]['use_quantity']]; + } + $quantity[$uid] += min(1,(int) substr($overlap['participants']['uid'],2)); + + if ($quantity[$uid] <= $max_quantity) + { + $possible_quantity_conflicts[$uid][] =& $overlapping_events[$k]; // an other event can give the conflict + unset($common_parts[$uid]); + continue; + } + // now we have a quantity conflict for $uid + } + if (!count($common_parts)) + { + $conflicts[$overlap['id']-$this->bo->date2ts($overlap['start'])] =& $overlapping_events[$k]; + } + } + // check if we are withing the allowed quantity and if not add all events using that resource + foreach($max_quantity as $uid => $max) + { + if ($quantity[$uid] > $max) + { + foreach($possible_quantity_conflicts[$uid] as $conflict) + { + $conflicts[$conflict['id']-$this->bo->date2ts($conflict['start'])] =& $possible_quantity_conflicts[$k]; + } + } + } + unset($possible_quantity_conflicts); + + if (count($conflicts)) + { + return $conflicts; + } + } + // save the event to the database + if ($touch_modified) + { + $event['modified'] = time() + $this->tz_offset_s; // we are still in user-time + $event['modifier'] = $GLOBALS['egw_info']['user']['account_id']; + } + if (!($cal_id = $this->save($event))) + { + return $cal_id; + } + $event['id'] = $cal_id; + + // TODO send update messages + + return $cal_id; + } + + /** + * saves an event to the database, does NOT do any notifications, see bocalupdate::update for that + * + * This methode converts from user to server time and handles the insertion of users and dates of repeating events + * + * @param array $event + * @return int/boolean $cal_id > 0 or false on error + */ + function save($event) + { + $save_event = $event; + // we run all dates through date2array, to adjust to server-time and get the new keys (day,minute,second,full,raw) + foreach(array('start','end','modified','recur_enddate') as $ts) + { + // we convert here from user-time to timestamps in server-time! + if (isset($event[$ts])) $event[$ts] = $event[$ts] ? $this->date2ts($event[$ts],true) : 0; + } + if (($cal_id = $this->so->save($event,$set_recurrences)) && $set_recurrences && $event['recur_type'] != MCAL_RECUR_NONE) + { + $save_event['id'] = $cal_id; + $this->set_recurrences($save_event); + } + $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,$event['cal_id'] ? 'modify' : 'add',time()); + + return $cal_id; + } + + /** + * deletes an event + * + * @param int $cal_id + * @return boolean true on success, flase on error (usually permission denied) + */ + function delete($cal_id) + { + if (!$this->check_perms(EGW_ACL_DELETE,$cal_id)) return false; + + $this->so->delete($cal_id); + $GLOBALS['egw']->contenthistory->updateTimeStamp('calendar',$cal_id,'delete',time()); + + return true; + } +} diff --git a/calendar/templates/default/app.css b/calendar/templates/default/app.css index fdc7c77ceb..c10ed90e6f 100644 --- a/calendar/templates/default/app.css +++ b/calendar/templates/default/app.css @@ -4,6 +4,8 @@ * CSS settings for the new uiviews code */ +.redItalic { color: red; font-style: italic; } + /* timeGridWidget, contains timeRow's and dayCol's */ .calTimeGrid{