From 700c20b802a64c8a5d732a69993182b1946d4ff0 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 1 Aug 2004 15:36:04 +0000 Subject: [PATCH] Imported the new calendar from the ralfbecker branch and merged it with the bugfixes and improvments of the 1.0 branch. All further development will be done now in HEAD --- calendar/inc/class.bocal.inc.php | 1054 +++++++++++++++++++ calendar/inc/class.bocalendar.inc.php | 23 +- calendar/inc/class.socalendar.inc.php | 23 +- calendar/inc/class.uialarm.inc.php | 6 + calendar/inc/class.uical.inc.php | 481 +++++++++ calendar/inc/class.uicalendar.inc.php | 163 ++- calendar/inc/class.uicustom_fields.inc.php | 6 + calendar/inc/class.uiholiday.inc.php | 5 + calendar/inc/class.uiicalendar.inc.php | 6 + calendar/inc/class.uiviews.inc.php | 707 +++++++++++++ calendar/inc/gradient.php | 84 ++ calendar/inc/hook_settings.inc.php | 13 +- calendar/inc/hook_sidebox_menu.inc.php | 86 +- calendar/inc/round_corners.php | 88 ++ calendar/setup/phpgw_de.lang | 21 +- calendar/setup/phpgw_en.lang | 19 + calendar/setup/phpgw_fr.lang | 22 +- calendar/setup/setup.inc.php | 2 +- calendar/templates/default/app.css | 221 +++- calendar/templates/default/event_widget.tpl | 47 + calendar/templates/default/header.inc.php | 8 +- 21 files changed, 2960 insertions(+), 125 deletions(-) create mode 100644 calendar/inc/class.bocal.inc.php create mode 100644 calendar/inc/class.uical.inc.php create mode 100644 calendar/inc/class.uiviews.inc.php create mode 100644 calendar/inc/gradient.php create mode 100644 calendar/inc/round_corners.php create mode 100644 calendar/templates/default/event_widget.tpl diff --git a/calendar/inc/class.bocal.inc.php b/calendar/inc/class.bocal.inc.php new file mode 100644 index 0000000000..25fb8d1e10 --- /dev/null +++ b/calendar/inc/class.bocal.inc.php @@ -0,0 +1,1054 @@ + * +* -------------------------------------------- * +* 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$ */ + +/** + * Class to access and manipulate all calendar data + * + * At the moment this class partialy uses the "old" bocalendar class. + * As the calendar rewrite proceeds, these references will be removed. + * + * Note: All new code should only access this class and not bocalendar!!! + * + * If you need a function not already available in bocal, please ask RalfBecker@outdoor-training.de + * + * 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 + * The dates returned by this class are always arrays with the following keys (subset of the array used by datetime): + * year, month, day (not mday!), hour, minute (not min!), second (not sec!), raw (timestamp) and full (Ymd-string) + * + * 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 + */ + +if (!defined('ACL_TYPE_IDENTIFER')) // used to mark ACL-values for the debug_message methode +{ + define('ACL_TYPE_IDENTIFER','***ACL***'); +} + +define('HOUR_s',60*60); +define('DAY_s',24*HOUR_s); +define('WEEK_s',7*DAY_s); + +class bocal +{ + /** + * @var int $debug name of method to debug or level of debug-messages: + * False=Off as higher as more messages you get ;-) + * 1 = function-calls incl. parameters to general functions like search, read, write, delete + * 2 = function-calls to exported helper-functions like check_perms + * 4 = function-calls to exported conversation-functions like date2ts, date2array, ... + * 5 = function-calls to private functions + */ + var $debug=False; + + /** + * @var int $tz_offset_s offset in secconds between user and server-time, + * it need to be add to a server-time to get the user-time or substracted from a user-time to get the server-time + */ + var $tz_offset_s; + + /** + * @var int $now_su timestamp of actual user-time + */ + var $now_su; + + /** + * @var array $cal_prefs calendar-specific prefs + */ + var $cal_prefs; + + /** + * @var array $common_prefs common preferences + */ + var $common_prefs; + + /** + * @var int $user nummerical id of the current user-id + */ + var $user=0; + + /** + * @var array $grants grants of the current user, array with user-id / ored-ACL-rights pairs + */ + var $grants=array(); + + /** + * @var array $verbose_status translated 1-char status values to a verbose name, run through lang() by the constructor + */ + var $verbose_status = array( + 'A' => 'Accepted', + 'R' => 'Rejected', + 'T' => 'Tentative', + 'U' => 'No Response', + ); + + /** + * Constructor + */ + function bocal() + { + if ($this->debug > 0) $this->debug_message('bocal::bocal() started',True,$param); + + foreach(array( +// 'old_bo' => 'calendar.bocalendar', + 'old_so' => 'calendar.socalendar', + 'datetime' => 'phpgwapi.datetime', + ) as $my => $app_class) + { + list(,$class) = explode('.',$app_class); + + if (!is_object($GLOBALS['phpgw']->$class)) + { + $GLOBALS['phpgw']->$class = CreateObject($app_class); + } + $this->$my = &$GLOBALS['phpgw']->$class; + } + $this->common_prefs = $GLOBALS['phpgw_info']['user']['preferences']['common']; + $this->cal_prefs = $GLOBALS['phpgw_info']['user']['preferences']['calendar']; + + $this->tz_offset_s = $this->datetime->tz_offset; + + $this->now_su = time() + $this->tz_offset_s; + + $this->user = $GLOBALS['phpgw_info']['user']['account_id']; + + $this->grants = $GLOBALS['phpgw']->acl->get_grants('calendar'); + + foreach($this->verbose_status as $status => $text) + { + $this->verbose_status[$status] = lang($text); + } + } + + /** + * Searches / lists calendar entries, including repeating ones + * + * As said earlier the new bo-class operates to the outside only in user-time and to the so-class only in server-time. + * The existing old_so-class (socalendar*) still has a mixed approach, values are mostly (!) returned in user-time + * and arguments are sometimes (!) expected in server-time ;-) + * We need to kope with that, til the so-class gets re-written !!! + * The arguments to the list_*events* functions are in user-time (they take only year,month,day) and they return id's. + * The events, fetched with there id's via old_so::read_entry are also in user-time. + * + * @param params array with the following keys + * start date startdate of the search/list, defaults to today + * end date enddate of the search/list, defaults to start + one day + * users mixed integer user-id or array of user-id's to use, defaults to the current user + * cat_id mixed category-id or array of cat-id's, defaults to all if unset, 0 or False + * Please note: only a single cat-id, will include all sub-cats (if the common-pref 'cats_no_subs' is False) + * filter string space delimited filter-names, atm. 'all' or 'private' + * query string pattern so search for, if unset or empty all matching entries are returned (no search) + * Please Note: a search never returns repeating events more then once AND does not honor start+end date !!! + * dayswise boolean on True it returns an array with YYYYMMDD strings as keys and an array with events + * (events spanning multiple days are returned each day again (!)) otherwise it returns one array with + * the events (default), not honored in a search ==> always returns an array of events ! + * @return array of events or array with YYYYMMDD strings / array of events pairs (depending on $daywise param) + */ + function search($params) + { + $params_in = $params; + + if (!isset($params['users']) || !$params['users']) + { + $params['users'] = $this->user; + } + if (!is_array($params['users'])) + { + $params['users'] = array((int) $params['users']); + } + // only query calendars of users, we have READ-grants from + $users = array(); + foreach($params['users'] as $user) + { + if ($this->check_perms(PHPGW_ACL_READ,0,$user)) + { + $users[] = $user; + } + } + $start = $this->date2ts($params['start']); + $end = isset($params['end']) ? $this->date2ts($params['end']) : $start + DAY_s-1; + $daywise = !isset($params['daywise']) ? False : !!$params['daywise']; + $cat_id = isset($params['cat_id']) ? $params['cat_id'] : 0; + $filter = isset($params['filter']) ? $params['filter'] : 'all'; + if ($this->debug && ($this->debug > 1 || $this->debug == 'search')) + { + $this->debug_message('bocal::search(%1) start=%2, end=%3, daywise=%4, cat_id=%5, filter=%6',True, + $params,$start,$end,$daywise,$cat_id,$filter); + } + // some of the params need to be set as class vars in the old so-class + $this->old_so->cat_id = $cat_id; + $this->old_so->filter = $filter; + + if (!isset($params['query']) || empty($params['query'])) + { + $start = $this->date2array($start); + $end = $this->date2array($end); + + $event_ids = $this->old_so->list_events($start['year'],$start['month'],$start['day'], + $end['year'],$end['month'],$end['day'],$users); + + if ($this->debug && ($this->debug > 1 || $this->debug == 'search')) + { + $this->debug_message('socalendar::list_events(start=%1,end=%2,users=%3)=%4',False, + $start,$end,$users,$event_ids); + } + $rep_event_ids = $this->old_so->list_repeated_events($start['year'],$start['month'],$start['day'], + $end['year'],$end['month'],$end['day'],$users); + if ($this->debug > 1) $this->debug_message('socalendar::list_repeated_events(start=%1,end=%2,users=%3)=%4',False,$start,$end,$users,$rep_event_ids); + } + else + { + $daywise = False; + $event_ids = $this->old_so->list_events_keyword($params['query'],$users); + + if ($this->debug && ($this->debug > 1 || $this->debug == 'search')) + { + $this->debug_message('socalendar::list_events_keyword(query=%1,users=%2)=%3',False, + $params['query'],$users,$event_ids); + } + $rep_event_ids = array(); // not used in search + } + // To build the list of events from returned id's, we need go through the $event_ids, read each event + // and put it in the cached_events for each day it's running, if it's NO repeating event. + // Then we need to go through $rep_event_ids, fetch the event and add each event with ALL it's occurences + // within the reported timespan, if there is no recur-exception-event already in the list of events. + // If daywise is True each event need to be added in the array of each day it's running. + // Please note: as we use the old SO class, all returned dates are already in user-time !!! + + $events = $recur_exceptions = $recur_events = Array(); + $events_sorted = True; + + if(count($event_ids)) + { + foreach($event_ids as $id) + { + $event = $this->read($id,True); // = no ACL check, as other entries dont get reported !!! + + // recuring events are handled later, remember them for later use, no new read necessary + if ($event['recur_type']) + { + $recur_events[$id] = $event; + continue; + } + // recur-exceptions have a reference to the original event + // we remember they are their for a certain date and id, to not insert their regular recurence + if ($event['reference']) + { + for($ts = $event['start']['raw']; $ts < $event['end']['raw']; $ts += DAY_s) + { + $recur_exceptions[$event['reference']][(int)$this->date2string($ts)] = True; + } + $recur_exceptions[$event['reference']][$event['end']['full']] = True; + } + $events[] = $event; + } + if ($this->debug && ($this->debug > 2 || $this->debug == 'search')) + { + $this->debug_message('socalendar::search processed event_ids=%1, events=%2',False,$event_ids,$events); + } + } + + if (count($rep_event_ids)) + { + foreach($rep_event_ids as $id) + { + $event = isset($recur_events[$id]) ? $recur_events[$id] : $this->read($id,True); + + $this->insert_all_repetitions($event,$start,$end,$events,$recur_exceptions[$id]); + $events_sorted = False; + } + if ($this->debug && ($this->debug > 2 || $this->debug == 'search')) + { + $this->debug_message('socalendar::search processed rep_event_ids=%1, events=%2',False,$rep_event_ids,$events); + } + } + + if (!$events_sorted) + { + // sort the events by start-date + usort($events,create_function('$e1,$e2',"return \$e1['start']['raw']-\$e2['start']['raw'];")); + if ($this->debug && ($this->debug > 2 || $this->debug == 'search')) + { + $this->debug_message('socalendar::search usort(events)=%1',False,$events); + } + } + if ($daywise) + { + if ($this->debug && ($this->debug > 2 || $this->debug == 'search')) + { + $this->debug_message('socalendar::search daywise sorting from %1 to %2 of %3',False,$start,$end,$events); + } + // create empty entries for each day in the reported time + for($ts = $start['raw']; $ts <= $end['raw']; $ts += DAY_s) + { + $daysEvents[$this->date2string($ts)] = array(); + } + foreach($events as $event) + { + $e_start = max($event['start']['raw'],$start['raw']); + $e_end = min($event['end']['raw'],$end['raw']); + + // add event to each day in the reported time + for($ts = $e_start; $ts <= $e_end; $ts += DAY_s) + { + $daysEvents[$this->date2string($ts)][] = $event; + } + } + $events = $daysEvents; + if ($this->debug && ($this->debug > 2 || $this->debug == 'search')) + { + $this->debug_message('socalendar::search daywise events=%1',False,$events); + } + } + if ($this->debug && ($this->debug > 0 || $this->debug == 'search')) + { + $this->debug_message('bocal::search(%1)=%2',True,$params,$events); + } + return $events; + } + + /** + * Reads a calendar-entry + * + * @param $id int the id of the entry + * @param $ignore_acl boolean should we ignore the acl, default False + * @return array with the event or False if the acl-check went wrong + */ + function read($id,$ignore_acl=False) + { + $event = False; + + if($ignore_acl || $this->check_perms(PHPGW_ACL_READ,$id)) + { + // some minimal cacheing to re-use the event already read in check_perms + static $event = array(); + if (!isset($event['id']) || $event['id'] != $id) + { + $event = $this->old_so->read_entry($id); + + if (!$event['recur_enddate']['year'] && !$event['recur_enddate']['month'] && !$event['recur_enddate']['day']) + { + $event['recur_enddate'] = False; // for easier checking + } + // we run all dates through date2array, to get the new keys (day,minute,second,full,raw) + foreach(array('start','end','modtime','recur_enddate') as $date) + { + // The dates are already in user-time, because of the old so-class !!! + $event[$date] = $event[$date] ? $this->date2array($event[$date]) : $event[$date]; + } + } + } + if ($this->debug && ($this->debug > 1 || $this->debug == 'read')) + { + $this->debug_message('bocal::read(%1,%2)=%3',True,$id,$ignore_acl,$event); + } + return $event; + } + + /** + * Inserts all repetions of $event in the timespan between $start and $end into $events + * + * As events can have recur-exceptions, only those event-date not having one, should get inserted. + * The caller supplies an array with the already inserted exceptions. + * + * The new entries are just appended to $entries, so $events is no longer sorted by startdate !!! + * Unlike the old code the start- and end-date of the events should be adapted here !!! + * + * TODO: This code is mainly copied from bocalendar and need to be rewritten for the changed algorithm: + * We insert now all repetions of one event in one go. It should be possible to calculate the time-difference + * of the used recur-type and add all events in one simple for-loop. Daylightsaving changes need to be taken into Account. + * + * @param $event array repeating event whos repetions should be inserted + * @param $start array start-date + * @param $end array end-date + * @param $events array where the repetions get inserted + * @param $recure_exceptions array with date (in Ymd) as key (and True as values) + */ + function insert_all_repetitions($event,$start,$end,&$events,$recur_exceptions) + { + $start_in = $start; $end_in = $end; + + if ($this->debug && ($this->debug > 3 || $this->debug == 'insert_all_repetions')) + { + $this->debug_message('bocal::insert_all_repetions(%1,start=%2,end=%3,,%4) starting...',True,$event,$start_in,$end_in,$recur_exceptions); + } + $id = $event['id']; + $event_start_daybegin_ts = $this->date2ts($event['start']['full']); + + if($event['recur_enddate']) + { + $recur_end_ymd = $this->date2string($event['recur_enddate']); + } + else + { + $recur_end_ymd = $this->date2string(mktime(0,0,0,1,1,5+date('Y'))); // go max. 5 years from now + } + + // We only need to compute the intersection between our reported time-span and the live-time of the event + // To catch all multiday repeated events (eg. second days), we need to start the length of the even earlier + // then our original report-starttime + $event_length = $event['end']['raw']-$event['start']['raw']; + $start_ts = max($event['start']['raw'],$start['raw']-$event_length); + // we need to add 26*60*60-1 to the recur_enddate as its hour+minute are 0 + $end_ts = $event['recur_enddate'] ? min($event['recur_enddate']['raw']+DAY_s-1,$end['raw']) : $end['raw']; + + for($ts = $start_ts; $ts < $end_ts; $ts += DAY_s) + { + $search_date_ymd = (int)$this->date2string($ts); + + $have_exception = !is_null($recur_exceptions) && isset($recur_exceptions[$search_date_ymd]); + if ($this->debug && ($this->debug > 3 || $this->debug == 'insert_all_repetions')) + { + $this->debug_message('bocal::insert_all_repetions(...,%1) checking recur_exceptions[%2]=%3',False, + $recur_exceptions,$search_date_ymd,$have_exception); + } + if ($have_exception) + { + continue; // we already have an exception for that date + } + $search_date_year = date('Y',$ts); + $search_date_month = date('m',$ts); + $search_date_day = date('d',$ts); + $search_date_dow = date('w',$ts); + $search_beg_day = mktime(0,0,0,$search_date_month,$search_date_day,$search_date_year); + + if ($search_date_ymd == $event['start']['full']) // first occurence + { + $this->add_adjusted_event($events,$event,$search_date_ymd); + continue; + } + $freq = $event['recur_interval']; + $type = $event['recur_type']; + switch($type) + { + case MCAL_RECUR_DAILY: + if($this->debug > 4) + { + echo ''."\n"; + } + if ($freq == 1 && $event['recur_enddate']['month'] != 0 && $event['recur_enddate']['day'] != 0 && $event['recur_enddate']['year'] != 0 && $search_date_ymd <= $recur_end_ymd) + { + $this->add_adjusted_event($events,$event,$search_date_ymd); + } + elseif (floor(($search_beg_day - $event_start_daybegin_ts)/DAY_s) % $freq) + { + continue; + } + else + { + $this->add_adjusted_event($events,$event,$search_date_ymd); + } + break; + case MCAL_RECUR_WEEKLY: + if (floor(($search_beg_day - $event_start_daybegin_ts)/WEEK_s) % $freq) + { + continue; + } + $check = 0; + switch($search_date_dow) + { + case 0: + $check = MCAL_M_SUNDAY; + break; + case 1: + $check = MCAL_M_MONDAY; + break; + case 2: + $check = MCAL_M_TUESDAY; + break; + case 3: + $check = MCAL_M_WEDNESDAY; + break; + case 4: + $check = MCAL_M_THURSDAY; + break; + case 5: + $check = MCAL_M_FRIDAY; + break; + case 6: + $check = MCAL_M_SATURDAY; + break; + } + if ($event['recur_data'] & $check) + { + $this->add_adjusted_event($events,$event,$search_date_ymd); + } + break; + case MCAL_RECUR_MONTHLY_WDAY: + if ((($search_date_year - $event['start']['year']) * 12 + $search_date_month - $event['start']['month']) % $freq) + { + continue; + } + + if (($GLOBALS['phpgw']->datetime->day_of_week($event['start']['year'],$event['start']['month'],$event['start']['day']) == $GLOBALS['phpgw']->datetime->day_of_week($search_date_year,$search_date_month,$search_date_day)) && + (ceil($event['start']['day']/7) == ceil($search_date_day/7))) + { + $this->add_adjusted_event($events,$event,$search_date_ymd); + } + break; + case MCAL_RECUR_MONTHLY_MDAY: + if ((($search_date_year - $event['start']['year']) * 12 + $search_date_month - $event['start']['month']) % $freq) + { + continue; + } + if ($search_date_day == $event['start']['day']) + { + $this->add_adjusted_event($events,$event,$search_date_ymd); + } + break; + case MCAL_RECUR_YEARLY: + if (($search_date_year - $event['start']['year']) % $freq) + { + continue; + } + if (date('dm',$ts) == date('dm',$event_start_daybegin_ts)) + { + $this->add_adjusted_event($events,$event,$search_date_ymd); + } + break; + } // switch(recur-type) + } // for($date = ...) + if ($this->debug && ($this->debug > 2 || $this->debug == 'insert_all_repetions')) + { + $this->debug_message('bocal::insert_all_repetions(%1,start=%2,end=%3,events,exections=%4) events=%5',True,$event,$start_in,$end_in,$recur_exceptions,$events); + } + } + + /** + * Adds one repetion of $event for $date_ymd to the $events array, after adjusting its start- and end-time + * + * @param $events array in which the event gets inserted + * @param $event array event to insert, it has start- and end-date of the first recurence, not of $date_ymd + * @param $date_ymd int/string of the date of the event + */ + function add_adjusted_event(&$events,$event,$date_ymd) + { + $event_in = $event; + // calculate the new start- and end-time + $length_s = $event['end']['raw'] - $event['start']['raw']; + + $date_arr = $this->date2array((string) $date_ymd); + $date_arr['hour'] = $event['start']['hour']; + $date_arr['minute'] = $event['start']['minute']; + $date_arr['second'] = $event['start']['second']; + unset($date_arr['raw']); // else date2ts would use it + $date_arr['raw'] = $this->date2ts($date_arr); + $date_arr['full'] = (int) $date_ymd; + + $event['start'] = $date_arr; + $event['end'] = $this->date2array($date_arr['raw']+$length_s); + + $events[] = $event; + + if ($this->debug && ($this->debug > 2 || $this->debug == 'add_adjust_event')) + { + $this->debug_message('bocal::add_adjust_event(,%1,%2) as %3',True,$event_in,$date_ymd,$event); + } + } + + /** + * Checks if the current user has the necessary ACL rights + * + * The check is performed on an event or generally on the cal of an other user + * + * Note: Participating in an event is considered as haveing read-access on that event, + * even if you have no general read-grant from that user. + * + * @param $needed int necessary ACL right: PHPGW_ACL_{READ|EDIT|DELETE} + * @param $event mixed event as array or the event-id or 0 for a general check + * @param $other int uid to check (if event==0) or 0 to check against $this->user + */ + function check_perms($needed,$event=0,$other=0) + { + $event_in = $event; + if (is_int($event) && $event == 0) + { + $owner = $other > 0 ? $other : $this->user; + } + else + { + if (!is_array($event)) + { + $event = $this->read((int) $event,True); // = no ACL check !!! + } + if (!is_array($event)) + { + if ($this->xmlrpc) + { + $GLOBALS['server']->xmlrpc_error($GLOBALS['xmlrpcerr']['not_exist'],$GLOBALS['xmlrpcstr']['not_exist']); + } + return False; + } + $owner = $event['owner']; + $private = !$event['public']; + } + $user = $GLOBALS['phpgw_info']['user']['account_id']; + $grants = $this->grants[$owner]; + + if (is_array($event) && $needed == PHPGW_ACL_READ) + { + // Check if the $user is one of the participants or has a read-grant from one of them + // + foreach($event['participants'] as $uid => $accept) + { + if ($this->grants[$uid] & PHPGW_ACL_READ || $uid == $user) + { + $grants |= PHPGW_ACL_READ; + break; + } + } + } + + if ($GLOBALS['phpgw']->accounts->get_type($owner) == 'g' && $needed == PHPGW_ACL_ADD) + { + $access = False; // a group can't be the owner of an event + } + else + { + $access = $user == $owner || $grants & $needed && (!$private || $grants & PHPGW_ACL_PRIVATE); + } + if ($this->debug && ($this->debug > 2 || $this->debug == 'check_perms')) + { + $this->debug_message('bocal::check_perms(%1,%2,%3)=%4',True,ACL_TYPE_IDENTIFER.$needed,$event,$other,$access); + } + return $access; + } + + /** + * Converts several date-types to a timestamp and optionaly converts user- to server-time + * + * @param $date mixed date to convert, should be one of the following types + * string (!) in form YYYYMMDD or iso8601 YYYY-MM-DDThh:mm:ss + * int already a timestamp + * array with keys 'second', 'minute', 'hour', 'day' or 'mday' (depricated !), 'month' and 'year' + * @param $user2server_time boolean conversation between user- and server-time default False == Off + */ + function date2ts($date,$user2server=False) + { + $date_in = $date; + + switch(gettype($date)) + { + case 'string': // YYYYMMDD or iso8601 YYYY-MM-DDThh:mm:ss string + if ($date[10] == 'T') + { + $date = array( + 'year' => (int) substr($date,0,4), + 'month' => (int) substr($date,5,2), + 'day' => (int) substr($date,8,2), + 'hour' => (int) substr($date,11,2), + 'minute' => (int) substr($date,14,2), + 'second' => (int) substr($date,17,2), + ); + } + else + { + $date = array( + 'year' => (int) substr($date,0,4), + 'month' => (int) substr($date,4,2), + 'day' => (int) substr($date,6,2), + ); + } + // fall-through + case 'array': // day or mday, month and year keys + if (isset($date['raw']) && $date['raw']) // we already have a timestamp + { + $date = $date['raw']; + break; + } + foreach(array('mday'=>'day','min'=>'minute','sec'=>'second') as $old => $new) + { + if (!isset($date[$new]) && isset($date[$old])) // support the old format too + { + $date[$new] = $date[$old]; + unset($date[$old]); + } + } + $date = mktime((int)$date['hour'],(int)$date['minute'],(int)$date['second'],(int)$date['month'],(int)$date['day'],(int)$date['year']); + break; + case 'integer': // already a timestamp + break; + default: // eg. boolean, means now in user-time (!) + $date = $this->now_us; + break; + } + if ($user2server) + { + $date -= $this->tz_offset_s; + } + if ($this->debug && ($this->debug > 3 || $this->debug == 'date2ts')) + { + $this->debug_message('bocal::date2ts(%1,user2server=%2)=%3)',False,$date_in,$user2server,$date); + } + return $date; + } + + /** + * Converts a date to an array and optionaly converts server- to user-time + * + * @param $date mixed date to convert + * @param $server2user_time boolean conversation between user- and server-time default False == Off + * @return array with keys 'second', 'minute', 'hour', 'day', 'month', 'year', 'ras' (timestamp) and 'full' (Ymd-string) + */ + function date2array($date,$server2user=False) + { + $date_called = $date; + + if (!is_array($date) || count($date) < 8 || $server2user) // do we need a conversation + { + $date = $this->date2ts($date); + + if ($server2user) + { + $date += $this->tz_offset_s; + } + $arr = array(); + foreach(array('second'=>'s','minute'=>'i','hour'=>'H','day'=>'d','month'=>'m','year'=>'Y','full'=>'Ymd') as $key => $frmt) + { + $arr[$key] = (int) date($frmt,$date); + } + $arr['raw'] = $date; + } + if ($this->debug && ($this->debug > 3 || $this->debug == 'date2array')) + { + $this->debug_message('bocal::date2array(%1,server2user=%2)=%3)',False,$date_called,$server2user,$arr); + } + return $arr; + } + + /** + * Converts a date as timestamp or array to a date-string and optionaly converts server- to user-time + * + * @param $date mixed integer timestamp or array with ('year','month',..,'second') to convert + * @param $server2user_time boolean conversation between user- and server-time default False == Off + * @param $iso8601 boolean return a iso8601 date (YYYY-MM-DDThh:ii:ss), default False == Off + * @return YYYYMMDD or iso8601 date as string + */ + function date2string($date,$server2user=False,$iso8601=False) + { + $date_in = $date; + + if (is_array($date) && isset($date['full']) && !$server2user && !$iso8601) + { + $date = $date['full']; + } + else + { + $date = $this->date2ts($date,False); + + if ($server2user) + { + $date += $this->tz_offset_s; + } + $date = date(($iso8601 ? 'Y-m-d\TH:i:s' : 'Ymd'),$date); + } + if ($this->debug && ($this->debug > 3 || $this->debug == 'date2string')) + { + $this->debug_message('bocal::date2string(%1,server2user=%2,iso8601=%3)=%4)',False,$date_in,$server2user,$iso8601,$date); + } + return $date; + } + + /** + * Gives out a debug-message with certain parameters + * + * All permanent debug-messages in the calendar should be done by this function !!! + * (In future they may be logged or sent as xmlrpc-faults back.) + * + * Permanent debug-message need to make sure NOT to give secret information like passwords !!! + * + * This function do NOT honor the setting of the debug variable, you may use it like + * if ($this->debug > N) $this->debug_message('Error ;-)'); + * + * The parameters get formated depending on their type. ACL-values need a ACL_TYPE_IDENTIFER prefix. + * + * @param $msg string message with parameters/variables like lang(), eg. '%1' + * @param $backtrace include a function-backtrace, default True=On + * should only be set to False=Off, if your code ensures a call with backtrace=On was made before !!! + * @param $param mixed a variable number of parameters, to be inserted in $msg + * arrays get serialized with print_r() ! + */ + function debug_message($msg,$backtrace=True) + { + static $acl2string = array( + 0 => 'ACL-UNKNOWN', + PHPGW_ACL_READ => 'ACL_READ', + PHPGW_ACL_WRITE => 'ACL_WRITE', + PHPGW_ACL_ADD => 'ACL_ADD', + PHPGW_ACL_DELETE => 'ACL_DELETE', + PHPGW_ACL_PRIVATE => 'ACL_PRIVATE', + ); + for($i = 2; $i < func_num_args(); ++$i) + { + $param = func_get_arg($i); + + if (is_null($param)) + { + $param='NULL'; + } + else + { + switch(gettype($param)) + { + case 'string': + if (substr($param,0,strlen(ACL_TYPE_IDENTIFER))== ACL_TYPE_IDENTIFER) + { + $param = (int) substr($param,strlen(ACL_TYPE_IDENTIFER)); + $param = isset($acl2string[$param]) ? $acl2string[$param] : $acl2string[0]; + } + else + { + $param = "'$param'"; + } + break; + case 'array': + case 'object': + list(,$content) = @each($param); + $do_pre = is_array($param) ? count($param) > 6 || is_array($content)&&count($content) : True; + $param = ($do_pre ? '
' : '').print_r($param,True).($do_pre ? '
' : ''); + break; + case 'boolean': + $param = $param ? 'True' : 'False'; + break; + } + } + $msg = str_replace('%'.($i-1),$param,$msg); + } + echo '

'.$msg."
\n".($backtrace ? 'Backtrace: '.function_backtrace(1)."

\n" : ''); + } + + /** + * Formats one or two dates (range) as long date (full monthname) + * + * @param $first mixed first date + * @param $last mixed last date if != 0 (default) + * @return string with formated date + */ + function long_date($first,$last=0) + { + $first = $this->date2array($first); + if ($last) + { + $last = $this->date2array($last); + } + $datefmt = $this->common_prefs['dateformat']; + + $month_before_day = strtolower($datefmt[0]) == 'm' || + strtolower($datefmt[2]) == 'm' && $datefmt[4] == 'd'; + + for ($i = 0; $i < 5; $i += 2) + { + switch($datefmt[$i]) + { + case 'd': + $range .= $first['day'] . ($datefmt[1] == '.' ? '.' : ''); + if ($first['month'] != $last['month'] || $first['year'] != $last['year']) + { + if (!$month_before_day) + { + $range .= ' '.lang(strftime('%B',$first['raw'])); + } + if ($first['year'] != $last['year'] && $datefmt[0] != 'Y') + { + $range .= ($datefmt[0] != 'd' ? ', ' : ' ') . $first['year']; + } + if (!$last) + { + return $range; + } + $range .= ' - '; + + if ($first['year'] != $last['year'] && $datefmt[0] == 'Y') + { + $range .= $last['year'] . ', '; + } + + if ($month_before_day) + { + $range .= lang(strftime('%B',$last['raw'])); + } + } + else + { + $range .= ' - '; + } + $range .= ' ' . $last['day'] . ($datefmt[1] == '.' ? '.' : ''); + break; + case 'm': + case 'M': + $range .= ' '.lang(strftime('%B',$month_before_day ? $first['raw'] : $last['raw'])) . ' '; + break; + case 'Y': + $range .= ($datefmt[0] == 'm' ? ', ' : ' ') . ($datefmt[0] == 'Y' ? $first['year'].($datefmt[2] == 'd' ? ', ' : ' ') : $last['year'].' '); + break; + } + } + return $range; + } + + /** + * Converts participants array of an event into array of (readable) participant-names with status + * + * @param $parts array participants array of an event + * @param $long_status boolean should the long/verbose status or only the one letter shortcut be used + * @return array with id / names with status pairs + */ + function participants($parts,$long_status=False) + { + static $id2lid = array(); + + foreach($parts as $id => $status) + { + $status = $this->verbose_status[$status]; + + if (!$long_status) + { + $status = $status[0]; + } + if (!isset($id2lid[$id])) + { + $id2lid[$id] = $GLOBALS['phpgw']->common->grab_owner_name($id); + } + $names[$id] = $id2lid[$id]." ($status)"; + } + return $names; + } + + /** + * Converts category string of an event into array of (readable) category-names + * + * @param $category string cat-id (multiple id's commaseparated) + * @param $color int color of the category, if multiple cats, the color of the last one with color is returned + * @return array with id / names + */ + function categories($category,&$color) + { + static $id2cat = array(); + $cats = array(); + $color = 0; + if (!isset($this->cat)) + { + $this->cat = $GLOBALS['phpgw']->categories; + $this->cat->categories($this->owner,'calendar'); + } + foreach(explode(',',$category) as $cat_id) + { + if (!$cat_id) continue; + + if (!isset($id2cat[$cat_id])) + { + list($id2cat[$cat_id]) = $this->cat->return_single($cat_id); + $id2cat[$cat_id]['data'] = unserialize($id2cat[$cat_id]['data']); + } + $cat = $id2cat[$cat_id]; + + if ($cat['data']['color'] || preg_match('/(#[0-9A-Fa-f]{6})/',$cat['description'],$parts)) + { + $color = $cat['data']['color'] ? $cat['data']['color'] : $parts[1]; + } + $cats[$cat_id] = stripslashes($cat['name']); + } + return $cats; + } + + /* This is called only by list_cals(). It was moved here to remove fatal error in php5 beta4 */ + function _list_cals_add($id,&$users,&$groups) + { + $name = $GLOBALS['phpgw']->common->grab_owner_name($id); + if (($type = $GLOBALS['phpgw']->accounts->get_type($id)) == 'g') + { + $arr = &$groups; + } + else + { + $arr = &$users; + } + $arr[$name] = Array( + 'grantor' => $id, + 'value' => ($type == 'g' ? 'g_' : '') . $id, + 'name' => $name + ); + } + + /** + * generate list of user- / group-calendars for the selectbox in the header + * @return alphabeticaly sorted array with groups first and then users + */ + function list_cals() + { + $users = $groups = array(); + foreach($this->grants as $id => $rights) + { + $this->_list_cals_add($id,$users,$groups); + } + if ($memberships = $GLOBALS['phpgw']->accounts->membership($GLOBALS['phpgw_info']['user']['account_id'])) + { + foreach($memberships as $group_info) + { + $this->_list_cals_add($group_info['account_id'],$users,$groups); + + if ($account_perms = $GLOBALS['phpgw']->acl->get_ids_for_location($group_info['account_id'],PHPGW_ACL_READ,'calendar')) + { + foreach($account_perms as $id) + { + $this->_list_cals_add($id,$users,$groups); + } + } + } + } + uksort($users,'strnatcasecmp'); + uksort($groups,'strnatcasecmp'); + + return $users + $groups; // users first and then groups, both alphabeticaly + } + + /** + * Read the holidays for a given $year + * + * @param $year integer defaults to 0 + * @return array indexed with Ymd of array of holidays. A holiday is an array with the following fields: + * index: numerica unique id + * locale: string, 2-char short for the nation + * name: string + * day: numerical day in month + * month: numerical month + * occurence: + * dow: day of week, 0=sunday, .., 6= saturday + * observande_rule: boolean + */ + function read_holidays($year=0) + { + if (!isset($this->cached_holidays[$year])) + { + if (!is_object($this->holidays)) + { + $this->holidays = CreateObject('calendar.boholiday'); + } + $this->holidays->prepare_read_holidays($year); + $this->cached_holidays[$year] = $this->holidays->read_holiday(); + } + return $this->cached_holidays[$year]; + } +} diff --git a/calendar/inc/class.bocalendar.inc.php b/calendar/inc/class.bocalendar.inc.php index a463ee86f1..afc8d50084 100755 --- a/calendar/inc/class.bocalendar.inc.php +++ b/calendar/inc/class.bocalendar.inc.php @@ -15,6 +15,14 @@ /* $Id$ */ + /** + * Depricated BO class of the calendar app. + * + * Note: All new code should only access the bocal class and NOT this class !!! + * + * If you need a function not already available in bocal, please ask RalfBecker@outdoor-training.de + */ + class bocalendar { var $public_functions = Array( @@ -234,7 +242,7 @@ $this->printer_friendly = ((int)$friendly == 1?True:False); if(isset($_POST['filter'])) { $this->filter = $_POST['filter']; } - if(isset($_POST['sortby'])) { $this->sortby = $_POST['sortby']; } + if(isset($_REQUEST['sortby'])) { $this->sortby = $_REQUEST['sortby']; } if(isset($_POST['cat_id'])) { $this->cat_id = $_POST['cat_id']; } if(!isset($this->filter)) @@ -1249,7 +1257,11 @@ { if (!is_object($this->jscal)) { - $this->jscal = CreateObject('phpgwapi.jscalendar'); + if (!is_object($GLOBALS['phpgw']->jscalendar)) + { + $GLOBALS['phpgw']->jscalendar = CreateObject('phpgwapi.jscalendar'); + } + $this->jscal = &$GLOBALS['phpgw']->jscalendar; } $time_param += $this->jscal->input2date($time_param['str'],False,'mday'); unset($time_param['str']); @@ -1757,6 +1769,10 @@ return $str; } + /** + * Inserts $event in $this->cached_events, if its not already there, because of a recur-expection + * Note: It maintains the sorting after starttime in the cached_events !!! + */ function sort_event($event,$date) { $inserted = False; @@ -1836,6 +1852,9 @@ } } + /** + * Creates copies of each repeating event in $this->repeating_events in $this->cached_events (via sort_event) + */ function check_repeating_events($datetime) { @reset($this->repeating_events); diff --git a/calendar/inc/class.socalendar.inc.php b/calendar/inc/class.socalendar.inc.php index 0d17e0034c..c676c84d04 100755 --- a/calendar/inc/class.socalendar.inc.php +++ b/calendar/inc/class.socalendar.inc.php @@ -28,7 +28,7 @@ var $filter; var $cat_id; - function socalendar($param) + function socalendar($param=False) { $this->db = $GLOBALS['phpgw']->db; if(!is_object($GLOBALS['phpgw']->datetime)) @@ -75,11 +75,20 @@ //$extra .= ($this->cat_id?"AND phpgw_cal.category like '%".$this->cat_id."%' ":''); if ($this->cat_id) { - if (!is_object($GLOBALS['phpgw']->categories)) + if (!is_array($this->cat_id) && !@$GLOBALS['phpgw_info']['user']['preferences']['common']['cats_no_subs']) { - $GLOBALS['phpgw']->categories = CreateObject('phpgwapi.categories'); + if (!is_object($GLOBALS['phpgw']->categories)) + { + $GLOBALS['phpgw']->categories = CreateObject('phpgwapi.categories'); + } + $cats = $GLOBALS['phpgw']->categories->return_all_children($this->cat_id); } - $cats = $GLOBALS['phpgw']->categories->return_all_children($this->cat_id); + else + { + $cats = is_array($this->cat_id) ? $this->cat_id : array($this->cat_id); + } + array_walk($cats,create_function('&$val,$key','$val = (int) $val;')); + $extra .= "AND (phpgw_cal.category".(count($cats) > 1 ? ' IN ('.implode(',',$cats).')' : '='.(int)$this->cat_id); foreach($cats as $cat) { @@ -97,6 +106,12 @@ } } + /** + * Returns the id's of all repeating events started after s{year,month,day} AND still running at e{year,month,day} + * + * The startdate of an repeating events is the regular event-startdate. + * Events are "still running" if no recur-enddate is set or its after e{year,month,day} + */ function list_repeated_events($syear,$smonth,$sday,$eyear,$emonth,$eday,$owner_id=0) { if(!$owner_id) diff --git a/calendar/inc/class.uialarm.inc.php b/calendar/inc/class.uialarm.inc.php index 6ea37d0e01..dc10abb08b 100755 --- a/calendar/inc/class.uialarm.inc.php +++ b/calendar/inc/class.uialarm.inc.php @@ -52,6 +52,12 @@ $GLOBALS['phpgw']->html = CreateObject('phpgwapi.html'); } $this->html = &$GLOBALS['phpgw']->html; + + // jscalendar is needed by the new navigation-menu AND it need to be loaded befor the header !!! + if (!is_object($GLOBALS['phpgw']->jscalendar)) + { + $GLOBALS['phpgw']->jscalendar = CreateObject('phpgwapi.jscalendar'); + } } function prep_page() diff --git a/calendar/inc/class.uical.inc.php b/calendar/inc/class.uical.inc.php new file mode 100644 index 0000000000..1564db6285 --- /dev/null +++ b/calendar/inc/class.uical.inc.php @@ -0,0 +1,481 @@ + * +* -------------------------------------------- * +* 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$ */ + +/** + * shared base-class of all calendar UserInterfaces + * + * It manages eg. the state of the controls in the UI and generated the calendar navigation (sidebox-menu) + * + * @package calendar + * @author RalfBecker@outdoor-training.de + * @license GPL + */ +class uical +{ + /** + * @var $debug mixed integer level or string function-name + */ + var $debug=False; + + /** + * @var $bo class bocal + */ + var $bo; + + /** + * Constructor + */ + function uical() + { + foreach(array( + 'bo' => 'calendar.bocal', + 'jscal' => 'phpgwapi.jscalendar', // for the sidebox-menu + 'html' => 'phpgwapi.html', + 'datetime' => 'phpgwapi.datetime', + 'cats' => 'phpgwapi.categories', + 'accountsel' => 'phpgwapi.uiaccountsel', + ) as $my => $app_class) + { + list(,$class) = explode('.',$app_class); + + if (!is_object($GLOBALS['phpgw']->$class)) + { + $GLOBALS['phpgw']->$class = CreateObject($app_class); + } + $this->$my = &$GLOBALS['phpgw']->$class; + } + $this->common_prefs = &$GLOBALS['phpgw_info']['user']['preferences']['common']; + $this->cal_prefs = &$GLOBALS['phpgw_info']['user']['preferences']['calendar']; + $this->wd_start = 60*$this->cal_prefs['workdaystarts']; + $this->wd_end = 60*$this->cal_prefs['workdayends']; + $this->interval_m = $this->cal_prefs['interval']; + + $this->user = $GLOBALS['phpgw_info']['user']['account_id']; + + $this->manage_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['phpgw_info']['user']['preferences']['common']['auto_hide_sidebox']); + } + + /** + * Manages the states of certain controls in the UI: date shown, category selected, ... + * + * The state of all these controls is updated if they are set in $_REQUEST or $set_states and saved in the session. + * The following states are used: + * - date or year, month, day: the actual date of the period displayed + * - cat_id: the selected category + * - owner: the owner of the displayed calendar + * - save_owner: the overriden owner of the planner + * - filter: the used filter: no filter / all or only privat + * - num_month: number of month shown in the planner + * - sortby: category or user of planner + * - return_to: the view the dialogs should return to + * @param set_states array to manualy set / change one of the states, default NULL = use $_REQUEST + */ + function manage_states($set_states=NULL) + { + $states = $states_session = $GLOBALS['phpgw']->session->appsession('session_data','calendar'); + + if (is_null($set_states)) + { + $set_states = $_REQUEST; + } + + if (!$states['date'] && $states['year'] && $states['month'] && $states['day']) + { + $states['date'] = $this->bo->date2string($states); + } + + foreach(array( + 'date' => $this->bo->date2string($this->bo->now_su), + 'cat_id' => 0, + 'filter' => 'all', + 'owner' => $this->user, + 'num_month' => 1, + 'save_owner' => 0, + 'sortby' => 'category', + 'multiple' => 0, + ) as $state => $default) + { + if (isset($set_states[$state])) + { + $states[$state] = $set_states[$state]; + } + elseif (!is_array($states) || !isset($states[$state])) + { + $states[$state] = $default; + } + if ($state == 'date') + { + $date_arr = $this->bo->date2array($states['date']); + foreach(array('year','month','day') as $name) + { + $this->$name = $states[$name] = $date_arr[$name]; + } + } + $this->$state = $states[$state]; + } + // set the actual view as return_to + list($app,$class,$func) = explode('.',$_GET['menuaction']); + if ($class == 'uiviews' && $func) + { + $states['return_to'] = $_GET['menuaction']; + } + // deal with group-owners + if (substr($this->owner,0,2) == 'g_' || $GLOBALS['phpgw']->accounts->get_type($this->owner) == 'g') + { + $this->set_owner_to_group($this->owner); + $states['owner'] = $this->owner; + } + $states['multiple'] = $this->multiple = $_GET['multiple'] || count(explode(',',$this->owner)) > 1; + + if ($this->debug > 0 || $this->debug == 'menage_states') $this->bo->debug_message('uical::manage_states(%1) session was %2, states now %3, is_group=%4, g_owner=%5',True,$set_states,$states_session,$states,$this->is_group,$this->g_owner); + // save the states in the session + $GLOBALS['phpgw']->session->appsession('session_data','calendar',$states); + } + + /** + * Sets a group as owner (of the events to show) + * + * It set $this->is_group and $this->g_owner - array with user-id's of the group-members who gave read-grants + * @param group-id or 'g_'+group-id + */ + function set_owner_to_group($owner) + { + $this->owner = (int) (substr($owner,0,2) == 'g_' ? substr($owner,2) : $owner); + $this->is_group = True; + $this->g_owner = Array(); + $members = $GLOBALS['phpgw']->accounts->member($this->owner); + if (is_array($members)) + { + foreach($members as $user) + { + // use only members which gave the user a read-grant + if ($this->bo->check_perms(PHPGW_ACL_READ,0,$user['account_id'])) + { + $this->g_owner[] = $user['account_id']; + } + } + } + if ($this->debug > 2 || $this->debug == 'set_owner_to_group') $this->bo->debug_message('uical::set_owner_to_group(%1): owner=%2, g_owner=%3',True,$owner,$this->owner,$this->g_owner); + } + + /** + * gets the icons displayed for a given event + * + * @param $event array + * @return array of 'img' / 'title' pairs + */ + function event_icons($event) + { + $is_private = !$event['public'] && !$this->bo->check_perms(PHPGW_ACL_READ,$event); + $viewable = !$this->bo->printer_friendly && $this->bo->check_perms(PHPGW_ACL_READ,$event); + + if (!$is_private) + { + if($event['priority'] == 3) + { + $icons[] = $this->html->image('calendar','high',lang('high priority')); + } + if($event['recur_type'] == MCAL_RECUR_NONE) + { + //$icons[] = $this->html->image('calendar','circle',lang('single event')); + } + else + { + $icons[] = $this->html->image('calendar','recur',lang('recurring event')); + } + $icons[] = $this->html->image('calendar',count($event['participants']) > 1 ? 'multi_3' : 'single', + implode(",\n",$this->bo->participants($event['participants']))); + } + if($event['public'] == 0) + { + $icons[] = $this->html->image('calendar','private',lang('private')); + } + if(isset($event['alarm']) && count($event['alarm']) >= 1 && !$is_private) + { + $icons[] = $this->html->image('calendar','alarm',lang('alarm')); + } + return $icons; + } + + /** + * Create a select-box item in the sidebox-menu + * @privat used only by sidebox_menu ! + */ + function _select_box($title,$name,$options,$baseurl='') + { + if ($baseurl) // we append the value to the baseurl + { + $baseurl .= strstr($baseurl,'?') === False ? '?' : '&'; + $onchange="location='$baseurl'+this.value;"; + } + else // we add $name=value to the actual location + { + $onchange="location=location+(location.search.length ? '&' : '?')+'".$name."='+this.value;"; + } + $select = ' \n"; + + return array( + 'text' => $select, + 'no_lang' => True, + 'link' => False + ); + } + + /** + * creates the content for the sidebox-menu, called as hook + */ + function sidebox_menu() + { + $base_hidden_vars = $link_vars = array(); + if (@$_POST['keywords']) + { + $base_hidden_vars['keywords'] = $_POST['keywords']; + } + + $n = 0; // index for file-array + + // Toolbar with the views + $views = ''."\n"; + foreach(array( + 'add' => array('icon'=>'new3','text'=>'add','menuaction'=>'calendar.uicalendar.add'), + 'day' => array('icon'=>'today','text'=>'Today','menuaction' => 'calendar.uiviews.day'), + 'week' => array('icon'=>'week','text'=>'This week','menuaction' => 'calendar.uiviews.week'), + 'month' => array('icon'=>'month','text'=>'This month','menuaction' => 'calendar.uiviews.month'), + 'year' => array('icon'=>'year','text'=>'This year','menuaction' => 'calendar.uicalendar.year'), + 'planner' => array('icon'=>'planner','text'=>'Group Planner','menuaction' => 'calendar.uicalendar.planner'), + 'matrixselect' => array('icon'=>'view','text'=>'Daily Matrix View','menuaction' => 'calendar.uicalendar.matrixselect'), + ) as $view => $data) + { + $vars = $link_vars; + $vars['menuaction'] = $data['menuaction']; + if ($view == 'day') + { + $vars['date'] = $this->bo->date2string($this->bo->now_su); // go to today + } + $views .= ''."\n"; + } + $views .= "
\n"; + $file[++$n] = array('text' => $views,'no_lang' => True,'link' => False,'icon' => False); + + // special views and view-options menu + $options = ''; + foreach(array( + array( + 'text' => lang('select one'), + 'value' => '', + 'selected' => False, + ), + array( + 'text' => lang('dayview'), + 'value' => 'menuaction=calendar.uiviews.day', + 'selected' => $_GET['menuaction'] == 'calendar.uiviews.day' && $_GET['days'] != 2, + ), + /*array( + 'text' => lang('two dayview'), + 'value' => 'menuaction=calendar.uiviews.week&days=2', + 'selected' => $_GET['menuaction'] == 'calendar.uiviews.day' && $_GET['days'] == 2, + ),*/ + array( + 'text' => lang('weekview'), + 'value' => 'menuaction=calendar.uiviews.week&days=7', + 'selected' => $_GET['menuaction'] == 'calendar.uiviews.week' && $this->cal_prefs['days_in_weekview'] != 5, + ), + array( + 'text' => lang('weekview without weekend'), + 'value' => 'menuaction=calendar.uiviews.week&days=5', + 'selected' => $_GET['menuaction'] == 'calendar.uiviews.week' && $this->cal_prefs['days_in_weekview'] == 5, + ), + array( + 'text' => lang('monthview'), + 'value' => 'menuaction=calendar.uiviews.month', + 'selected' => $_GET['menuaction'] == 'calendar.uiviews.month', + ), + array( + 'text' => lang('yearview'), + 'value' => 'menuaction=calendar.uicalendar.year', + 'selected' => $_GET['menuaction'] == 'calendar.uicalendar.year', + ), + array( + 'text' => lang('planner by category'), + 'value' => 'menuaction=calendar.uicalendar.planner&sortby=category', + 'selected' => $_GET['menuaction'] == 'calendar.uicalendar.planner' && $this->sort_by != 'user', + ), + array( + 'text' => lang('planner by user'), + 'value' => 'menuaction=calendar.uicalendar.planner&sortby=user', + 'selected' => $_GET['menuaction'] == 'calendar.uicalendar.planner' && $this->sort_by == 'user', + ), + array( + 'text' => lang('matrixview'), + 'value' => 'menuaction=calendar.uicalendar.matrixselect', + 'selected' => $_GET['menuaction'] == 'calendar.uicalendar.matrixselect' || + $_GET['menuaction'] == 'calendar.uicalendar.viewmatrix', + ), + ) as $data) + { + $options .= '\n"; + } + $file[++$n] = $this->_select_box('displayed view','view',$options,$GLOBALS['phpgw']->link('/index.php')); + + // Search + $blur = addslashes($this->html->htmlspecialchars(lang('Search').'...')); + $value = @$_POST['keywords'] ? $_POST['keywords'] : $blur; + $file[++$n] = array( + 'text' => $this->html->form('', + $base_hidden_vars,'/index.php',array('menuaction'=>'calendar.uicalendar.search')), + 'no_lang' => True, + 'link' => False, + ); + + // Minicalendar + foreach(array( + 'day'=>'calendar.uiviews.day', + 'week'=>'calendar.uiviews.week', + 'month'=>'calendar.uiviews.month') as $view => $menuaction) + { + $link_vars['menuaction'] = $view == 'month' && $_GET['menuaction'] == 'calendar.uicalendar.planner' ? + 'calendar.uicalendar.planner' : $menuaction; // stay in the planner + unset($link_vars['date']); // gets set in jscal + $link[$view] = $GLOBALS['phpgw']->link('/index.php',$link_vars); + } + $jscalendar = $GLOBALS['phpgw']->jscalendar->flat($link['day'],$this->date, + $link['week'],lang('show this week'),$link['month'],lang('show this month')); + $file[++$n] = array('text' => $jscalendar,'no_lang' => True,'link' => False,'icon' => False); + + // Category Selection + $file[++$n] = $this->_select_box('Category','cat_id', + ''. + $this->cats->formated_list('select','all',$this->cat_id,'True')); + + // we need a form for the select-boxes => insert it in the first selectbox + $file[$n]['text'] = $this->html->form(False,$base_hidden_vars,'/index.php',array('menuaction' => $_GET['menuaction'])) . + $file[$n]['text']; + + // Filter all or private + if($this->bo->check_perms(PHPGW_ACL_PRIVATE,0,$this->owner)) + { + $file[] = $this->_select_box('Filter','filter', + ''."\n". + ''."\n"); + } + + // Calendarselection: User or Group + if(count($this->bo->grants) > 0 && (!isset($GLOBALS['phpgw_info']['server']['deny_user_grants_access']) || + !$GLOBALS['phpgw_info']['server']['deny_user_grants_access'])) + { + $grants = array(); + foreach($this->bo->list_cals() as $grant) + { + $grants[] = $grant['grantor']; + } + if ($this->multiple) + { + $file[] = array( + 'text' => " + +". + $this->accountsel->selection('owner','uical_select_owner',$this->owner,'calendar+',3,False, + ' style="width: 85%;" title="'.lang('select a %1',lang('user')).'"','',$grants). + $this->html->submit_button('go','>>',"load_cal('".$GLOBALS['phpgw']->link('/index.php',array( + 'menuaction' => $_GET['menuaction'], + 'date' => $this->date, + ))."','uical_select_owner'); return false;",True,' title="'.lang('Go!').'"'), + 'no_lang' => True, + 'link' => False + ); + + } + else + { + $file[] = array( + 'text' => $this->accountsel->selection('owner','uical_select_owner',$this->owner,'calendar+',0,False, + ' style="width: 85%;" title="'.lang('select a %1',lang('user')).'"', + "location=location+(location.search.length ? '&' : '?')+'owner='+this.value;",$grants). + $this->html->a_href($this->html->image('phpgwapi','users',lang('show the calendar of multiple users')),array( + 'menuaction' => $_GET['menuaction'], + 'multiple' => 1, + 'date' => $this->date, + )), + 'no_lang' => True, + 'link' => False + ); + } + } + + // Import & Export + $file['Export'] = $GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.export'); + $file['Import'] = $GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uiicalendar.import'); + + // we need to set the sidebox-width a bit wider, as idots.css sets it to 147, to small for the jscal + // setting it to auto, uses the smallest possible size, but IE kills the jscal if the width is set to auto !!! + echo ''."\n"; + $menu_title = $GLOBALS['phpgw_info']['apps'][$appname]['title'] . ' '. lang('Menu'); + display_sidebox($appname,$menu_title,$file); + echo "\n"; + + if ($GLOBALS['phpgw_info']['user']['apps']['preferences']) + { + $menu_title = lang('Preferences'); + $file = Array( + 'Calendar preferences'=>$GLOBALS['phpgw']->link('/preferences/preferences.php','appname=calendar'), + 'Grant Access'=>$GLOBALS['phpgw']->link('/index.php','menuaction=preferences.uiaclprefs.index&acl_app=calendar'), + 'Edit Categories' =>$GLOBALS['phpgw']->link('/index.php','menuaction=preferences.uicategories.index&cats_app=calendar&cats_level=True&global_cats=True'), + ); + display_sidebox($appname,$menu_title,$file); + } + + if ($GLOBALS['phpgw_info']['user']['apps']['admin']) + { + $menu_title = lang('Administration'); + $file = Array( + 'Configuration'=>$GLOBALS['phpgw']->link('/index.php','menuaction=admin.uiconfig.index&appname=calendar'), + 'Custom Fields'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicustom_fields.index'), + 'Holiday Management'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uiholiday.admin'), + 'Import CSV-File' => $GLOBALS['phpgw']->link('/calendar/csv_import.php'), + 'Global Categories' =>$GLOBALS['phpgw']->link('/index.php','menuaction=admin.uicategories.index&appname=calendar'), + ); + display_sidebox($appname,$menu_title,$file); + } + } +} diff --git a/calendar/inc/class.uicalendar.inc.php b/calendar/inc/class.uicalendar.inc.php index e5cceb3b11..fe37766d2d 100755 --- a/calendar/inc/class.uicalendar.inc.php +++ b/calendar/inc/class.uicalendar.inc.php @@ -139,6 +139,13 @@ $GLOBALS['phpgw']->html = CreateObject('phpgwapi.html'); } $this->html = &$GLOBALS['phpgw']->html; + + // jscalendar is needed by the new navigation-menu AND it need to be loaded befor the header !!! + if (!is_object($GLOBALS['phpgw']->jscalendar)) + { + $GLOBALS['phpgw']->jscalendar = CreateObject('phpgwapi.jscalendar'); + } + $this->jscal = &$GLOBALS['phpgw']->jscalendar; } /* Public functions */ @@ -2288,7 +2295,12 @@ { $this->index(); } - $participants = $_POST['participants']; + + $participants = get_var("participants", array("GET", "POST")); + $date["year"] = get_var("year", array("GET", "POST")); + $date["month"] = get_var("month", array("GET", "POST")); + $date["day"] = get_var("day", array("GET", "POST")); + $parts = Array(); $acct = CreateObject('phpgwapi.accounts',$this->bo->owner); @@ -2330,18 +2342,26 @@ if ($this->always_app_header) $GLOBALS['phpgw_info']['flags']['app_header'] = $GLOBALS['phpgw_info']['apps']['calendar']['title'].' - '.lang('Matrixview'); $GLOBALS['phpgw']->common->phpgw_header(); - switch($_POST['matrixtype']) + switch( get_var("matrixtype", array("GET", "POST")) ) { case 'free/busy': - $freetime = $GLOBALS['phpgw']->datetime->gmtdate(mktime(0,0,0,$this->bo->month,$this->bo->day,$this->bo->year)); - echo '
'.$this->timematrix( - Array( - 'date' => $freetime, - 'starttime' => $this->bo->splittime('000000',False), - 'endtime' => 0, - 'participants' => $parts - ) - ); + if( get_var("sevendays", array("GET", "POST")) ) + { + $_f_daysend=7; + } + + for($_f_days=0; $_f_days<=$_f_daysend; $_f_days++) + { + $freetime = $GLOBALS['phpgw']->datetime->gmtdate(mktime(0,0,0,$date["month"],( $date["day"] + $_f_days ),$date["year"])); + echo '
'.$this->timematrix( + Array( + 'date' => $freetime, + 'starttime' => $this->bo->splittime('000000',False), + 'endtime' => 0, + 'participants' => $parts + ) + ); + } break; case 'weekly': echo '
'.$this->display_weekly( @@ -2355,10 +2375,10 @@ } echo "\n
\n".'
'."\n"; echo '
'."\n"; - echo ' '."\n"; - echo ' '."\n"; - echo ' '."\n"; - echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; + echo ' '."\n"; foreach($participants as $part) { echo ' '."\n"; @@ -2366,6 +2386,17 @@ echo ' '."\n"; echo ' '."\n"; echo ' '."\n"; + echo ' '."\n"; + + // Seven days + if( get_var("matrixtype", array("GET", "POST")) == "free/busy" ) + { + if( !get_var("sevendays", array("GET", "POST")) ) + echo ' '."\n"; + else + echo ' '."\n"; + } + echo '
'."\n"; echo '
'."\n"; } @@ -2534,19 +2565,22 @@ { $page_app = $GLOBALS['phpgw_info']['flags']['currentapp']; } - if (is_array($params)) - { - $params['menuaction'] = $page_app.'.ui'.$page_app.'.'.$_page; - } - else - { - $params = 'menuaction='.$page_app.'.ui'.$page_app.'.'.$_page.$params; + $page_class = $_page == 'day' || $_page == 'week' || $_page == 'month' ? 'uiviews' : 'uicalendar'; + + if (is_array($params)) + { + $params['menuaction'] = $page_app.'.'.$page_class.'.'.$_page; + } + else + { + $params = 'menuaction='.$page_app.'.'.$page_class.'.'.$_page.$params; } return $GLOBALS['phpgw']->link('/index.php',$params); } function header() { +return; $cols = 8; if($this->bo->check_perms(PHPGW_ACL_PRIVATE) == True) { @@ -2568,6 +2602,7 @@ function footer() { +return; $menuaction = $_GET['menuaction']; list(,,$method) = explode('.',$menuaction); @@ -3714,7 +3749,7 @@ { $participants[$part] = $GLOBALS['phpgw']->common->grab_owner_name($part); // Much better for processor :) - $participants_id[] .= $part; + $participants_id[] = $part; } uasort($participants,'strnatcasecmp'); // sort them after their fullname @@ -3726,27 +3761,74 @@ } $increment = $this->bo->prefs['calendar']['interval']; $interval = (int)(60 / $increment); + $colspan = $this->bo->prefs['calendar']['workdayends'] - $this->bo->prefs['calendar']['workdaystarts']; $pix = $GLOBALS['phpgw']->common->image('calendar','pix'); - $str = '
'.lang($GLOBALS['phpgw']->common->show_date($date['raw'],'l')) - . ', '.$this->bo->long_date($date).'
' - . '' - . '' - . ''; - for($i=0;$i<24;$i++) + /* Make link */ + $url_parts = "&participants[]=" . implode("&participants[]=", array_keys($param["participants"])); + if( get_var("sevendays", array("GET", "POST") ) ) + $sevendays = "&sevendays=yes"; + + $_f_date = $GLOBALS['phpgw']->datetime->makegmttime(0,0,0, $date["month"], ( $date["day"] - 1 ),$date["year"]); + $url_prevday = $GLOBALS['phpgw']->link('/index.php', + "menuaction=calendar.uicalendar.viewmatrix" + . "&year=" . $_f_date["year"] + . "&month=" . $_f_date["month"] + . "&day=" . $_f_date["day"] + . "&matrixtype=" . get_var("matrixtype", array("POST", "GET")) + . $url_parts + . $sevendays + ); + $_f_date = $GLOBALS['phpgw']->datetime->makegmttime(0,0,0, $date["month"], ( $date["day"] + 1 ),$date["year"]); + $url_nextday = $GLOBALS['phpgw']->link('/index.php', + "menuaction=calendar.uicalendar.viewmatrix" + . "&year=" . $_f_date["year"] + . "&month=" . $_f_date["month"] + . "&day=" . $_f_date["day"] + . "&matrixtype=" . get_var("matrixtype", array("POST", "GET")) + . $url_parts + . $sevendays + ); + $str = '
'.lang('Participant').'
' + . '' + . ' ' + . ' ' + . ' ' + . '' + . '
<< ' . lang('previous day') . '' . lang($GLOBALS['phpgw']->common->show_date($date['raw'],'l')) + . ', '.$this->bo->long_date($date) + . ' ' . lang('next day') . ' >>
' + . '' + . '' + . ''; + + // Destroy old variable + unset($_f_date); + unset($url_parts); + unset($url_prevday); + unset($url_nextday); + + + // Show TimeMatrix + for( $i=$this->bo->prefs['calendar']['workdaystarts']; + $i<$this->bo->prefs['calendar']['workdayends']; + $i++ ) { for($j=0;$j<$interval;$j++) { $k = ($j == 0 ? sprintf('%02d',$i).'
':'').sprintf('%02d',$j*$increment); - $str .= '\n"; } } $str .= '' - . ''; + . ''; if(!$endtime) { $endtime = $starttime; @@ -3755,7 +3837,7 @@ foreach($participants as $part => $fullname) { $str .= '' - . ''; + . ''; $this->bo->cached_events = Array(); $this->bo->so->owner = $part; @@ -3773,7 +3855,9 @@ if(!$this->bo->cached_events[$date['full']]) { - for($j=0;$j<24;$j++) + for( $j=$this->bo->prefs['calendar']['workdaystarts']; + $j<$this->bo->prefs['calendar']['workdayends']; + $j++ ) { for($k=0;$k<$interval;$k++) { @@ -3785,7 +3869,9 @@ else { $time_slice = $this->bo->prepare_matrix($interval,$increment,$part,$date['full']); - for($h=0;$h<24;$h++) + for( $h=$this->bo->prefs['calendar']['workdaystarts']; + $h<$this->bo->prefs['calendar']['workdayends']; + $h++ ) { $hour = $h * 10000; for($m=0;$m<$interval;$m++) @@ -3809,7 +3895,7 @@ } } $str .= '' - . ''; + . ''; } $this->bo->owner = $owner; $this->bo->so->owner = $owner; @@ -3877,7 +3963,6 @@ // $sb = CreateObject('phpgwapi.sbox'); $sb = CreateObject('phpgwapi.sbox2'); - $jscal = CreateObject('phpgwapi.jscalendar'); // before phpgw_header() !!! unset($GLOBALS['phpgw_info']['flags']['noheader']); unset($GLOBALS['phpgw_info']['flags']['nonavbar']); @@ -3999,7 +4084,7 @@ $sb->getDays('start[mday]',(int)$GLOBALS['phpgw']->common->show_date($start,'d')) ) */ - 'data' => $jscal->input('start[str]',$start) + 'data' => $this->jscal->input('start[str]',$start) ); // Time @@ -4024,7 +4109,7 @@ $sb->getDays('end[mday]',(int)$GLOBALS['phpgw']->common->show_date($end,'d')) ) */ - 'data' => $jscal->input('end[str]',$end) + 'data' => $this->jscal->input('end[str]',$end) ); // End Time @@ -4211,7 +4296,7 @@ $sb->getDays('recur_enddate[mday]',(int)$GLOBALS['phpgw']->common->show_date($recur_end,'d')) ) */ - $jscal->input('recur_enddate[str]',$recur_end) + $this->jscal->input('recur_enddate[str]',$recur_end) ); $i = 0; $boxes = ''; diff --git a/calendar/inc/class.uicustom_fields.inc.php b/calendar/inc/class.uicustom_fields.inc.php index a24897ac7a..55adb0307a 100644 --- a/calendar/inc/class.uicustom_fields.inc.php +++ b/calendar/inc/class.uicustom_fields.inc.php @@ -36,6 +36,12 @@ $GLOBALS['phpgw']->html = CreateObject('phpgwapi.html'); } $this->html = &$GLOBALS['phpgw']->html; + + // jscalendar is needed by the new navigation-menu AND it need to be loaded befor the header !!! + if (!is_object($GLOBALS['phpgw']->jscalendar)) + { + $GLOBALS['phpgw']->jscalendar = CreateObject('phpgwapi.jscalendar'); + } } function index($error='') diff --git a/calendar/inc/class.uiholiday.inc.php b/calendar/inc/class.uiholiday.inc.php index 3bd06dd91d..f875f0bc1f 100755 --- a/calendar/inc/class.uiholiday.inc.php +++ b/calendar/inc/class.uiholiday.inc.php @@ -43,6 +43,11 @@ $this->template_dir = $GLOBALS['phpgw']->common->get_tpl_dir('calendar'); $this->sb = CreateObject('phpgwapi.sbox'); + // jscalendar is needed by the new navigation-menu AND it need to be loaded befor the header !!! + if (!is_object($GLOBALS['phpgw']->jscalendar)) + { + $GLOBALS['phpgw']->jscalendar = CreateObject('phpgwapi.jscalendar'); + } $GLOBALS['phpgw_info']['flags']['app_header'] = $GLOBALS['phpgw_info']['apps']['calendar']['title'].' - '.lang('Holiday Management'); } diff --git a/calendar/inc/class.uiicalendar.inc.php b/calendar/inc/class.uiicalendar.inc.php index e4ccdbddf7..6fb733aca4 100755 --- a/calendar/inc/class.uiicalendar.inc.php +++ b/calendar/inc/class.uiicalendar.inc.php @@ -31,6 +31,12 @@ $this->bo = CreateObject('calendar.boicalendar'); $this->template = $GLOBALS['phpgw']->template; $GLOBALS['phpgw_info']['flags']['app_header'] = lang('Calendar - [iv]Cal Importer'); + + // jscalendar is needed by the new navigation-menu AND it need to be loaded befor the header !!! + if (!is_object($GLOBALS['phpgw']->jscalendar)) + { + $GLOBALS['phpgw']->jscalendar = CreateObject('phpgwapi.jscalendar'); + } } function print_test($val,$title,$x_pre='') diff --git a/calendar/inc/class.uiviews.inc.php b/calendar/inc/class.uiviews.inc.php new file mode 100644 index 0000000000..a190a599b9 --- /dev/null +++ b/calendar/inc/class.uiviews.inc.php @@ -0,0 +1,707 @@ + * +* -------------------------------------------- * +* 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$ */ + +include_once(PHPGW_INCLUDE_ROOT . '/calendar/inc/class.uical.inc.php'); + +/** + * Class to generate the calendar views and the necesary widgets + * + * @package calendar + * @author RalfBecker@outdoor-training.de + * @license GPL + */ +class uiviews extends uical +{ + var $public_functions = array( + 'day' => True, + 'week' => True, + 'month' => True, + 'test' => True, + ); + /** + * @var $debug mixed integer level or string function- or widget-name + */ + var $debug=False; + + /** + * @var minimum width for an event + */ + var $eventCol_min_width = 80; + + var $timeRow_width = 40; + + /** + * Constructor + */ + function uiviews() + { + $this->width = $this->establish_width(); + + $this->uical(); // call the parent's constructor + + $GLOBALS['phpgw_info']['flags']['nonavbar'] = False; + $app_header = array( + 'calendar.uiviews.day' => lang('Dayview'), + 'calendar.uiviews.week' => lang('Weekview'), + 'calendar.uiviews.month' => lang('Monthview'), + ); + $GLOBALS['phpgw_info']['flags']['app_header'] = $GLOBALS['phpgw_info']['apps']['calendar']['title']. + (isset($app_header[$_GET['menuaction']]) ? ' - '.$app_header[$_GET['menuaction']] : ''); + + // standard params for calling bocal::search for all views + $this->search_params = array( + 'start' => $this->date, + 'cat_id' => $this->cat_id, + 'users' => $this->is_group ? $this->g_owner : explode(',',$this->owner), + 'filter' => $this->filter, + 'daywise' => True, + ); + + $this->holidays = $this->bo->read_holidays($this->year); + } + + /** + * Displays the monthview or a multiple week-view + */ + function month($weeks=0) + { + if ($this->debug > 0) $this->bo->debug_message('uiviews::month(weeks=%1) date=%2',True,$weeks,$this->date); + + $first = $this->datetime->get_weekday_start($this->year,$this->month,$this->day=1); + if ($weeks) + { + $last = $first + $weeks * 7 * DAY_s - 1; + } + else + { + $last = $this->datetime->get_weekday_start($this->year,$this->month, + $days_in_month=$this->datetime->days_in_month($this->month,$this->year)); + $last += WEEK_s - 1; + } + if ($this->debug > 0)$this->bo->debug_message('uiviews::month(%1) date=%2: first=%3, last=%4',False,$weeks,$this->date,$this->bo->date2string($first),$this->bo->date2string($last)); + + $GLOBALS['phpgw_info']['flags']['app_header'] .= ': '.lang(date('F',$this->bo->date2ts($this->date))).' '.$this->year; + $GLOBALS['phpgw']->common->phpgw_header(); + + $search_params = $this->search_params; + + $days = $this->bo->search(array( + 'start' => $first, + 'end' => $last, + )+$this->search_params); + + for ($week_start = $first; $week_start < $last; $week_start += WEEK_s) + { + $week = array(); + for ($i = 0; $i < 7; ++$i) + { + $day_ymd = $this->bo->date2string($week_start+$i*DAY_s); + $week[$day_ymd] = array_shift($days); + } + $week_view = array( + 'menuaction' => 'calendar.uiviews.week', + 'date' => $this->bo->date2string($week_start), + ); + $title = lang('Wk').' '.date('W',$week_start); + $title = $this->html->a_href($title,$week_view,'',' title="'.lang('Weekview').'"'); + + echo $this->timeGridWidget($week,max($this->width,7*$this->eventCol_min_width+$this->timeRow_width),60,5,'',$title); + } + } + + /** + * Displays the weekview, with 5 or 7 days + */ + function week($days=0) + { + if (!$days) + { + $days = isset($_GET['days']) ? $_GET['days'] : $this->cal_prefs['days_in_weekview']; + if ($days != 5) $days = 7; + if ($days != $this->cal_prefs['days_in_weekview']) // save the preference + { + $GLOBALS['phpgw']->preferences->add('calendar','days_in_weekview',$days); + $GLOBALS['phpgw']->preferences->save_repository(); + $this->cal_prefs['days_in_weekview'] = $days; + } + } + if ($this->debug > 0) $this->bo->debug_message('uiviews::week(days=%1) date=%2',True,$days,$this->date); + + $wd_start = $first = $this->datetime->get_weekday_start($this->year,$this->month,$this->day); + if ($days == 5) // no weekend-days + { + switch($this->cal_prefs['weekdaystarts']) + { + case 'Saturday': + $first += DAY_s; + // fall through + case 'Sunday': + $first += DAY_s; + break; + } + } + //echo "

weekdaystarts='".$this->cal_prefs['weekdaystarts']."', get_weekday_start($this->year,$this->month,$this->day)=".date('l Y-m-d',$wd_start).", first=".date('l Y-m-d',$first)."

\n"; + $last = $first + $days * DAY_s - 1; + + $GLOBALS['phpgw_info']['flags']['app_header'] .= ': '.lang('Wk').' '.date('W',$first). + ': '.$this->bo->long_date($first,$last); + $GLOBALS['phpgw']->common->phpgw_header(); + + $search_params = $this->search_params; + + echo $this->timeGridWidget($this->bo->search(array( + 'start' => $first, + 'end' => $last, + )+$this->search_params),max($this->width,$days*$this->eventCol_min_width+$this->timeRow_width)); + } + + /** + * Displays the dayview + */ + function day() + { + if ($this->debug > 0) $this->bo->debug_message('uiviews::day() date=%1',True,$this->date); + + $ts = $this->bo->date2ts((string)$this->date); + $GLOBALS['phpgw_info']['flags']['app_header'] .= ': '.lang(date('l',$ts)).', '.$this->bo->long_date($ts); + $GLOBALS['phpgw']->common->phpgw_header(); + + $todos = $this->get_todos(&$todo_label); + + echo $this->html->table(array(0 => array( + $this->timeGridWidget($this->bo->search($this->search_params),$this->width-250,$this->cal_prefs['interval'],1.5), + $this->html->div( + $this->html->div($todo_label,'','calDayTodosHeader th')."\n". + $this->html->div($todos,'','calDayTodosTable'), + '','calDayTodos') + ),'.0' => 'valign="top"'),'width="100%"'); + } + + /** + * Query the open ToDo's via a hook from InfoLog or any other 'calendar_include_todos' provider + * + * @param $todo_label array/string label for the todo-box or array with 2 values: the label and a boolean show_all + * On return $todo_label contains the label for the todo-box + * @return string html with a table of open todo's + */ + function get_todos(&$todo_label) + { + $todos_from_hook = $GLOBALS['phpgw']->hooks->process(array( + 'location' => 'calendar_include_todos', + 'year' => $this->year, + 'month' => $this->month, + 'day' => $this->day, + 'owner' => $this->owner // num. id of the user, not necessary current user + )); + + if(is_array($todo_label)) + { + list($label,$showall)=$todo_label; + } + else + { + $label=$todo_label; + $showall=true; + } + $maxshow = (int)$GLOBALS['phpgw_info']['user']['preferences']['infolog']['mainscreen_maxshow']; + if($maxshow <= 0) + { + $maxshow=10; + } + //print_debug("get_todos(): label=$label; showall=$showall; max=$maxshow"); + + $content = $todo_label = ''; + if (is_array($todos_from_hook) && count($todos_from_hook)) + { + $todo_label = !empty($label) ? $label : lang("open ToDo's:"); + + foreach($todos_from_hook as $todos) + { + $i = 0; + if (is_array($todos) && count($todos)) + { + foreach($todos as $todo) + { + if(!$showall && ($i++ > $maxshow)) + { + break; + } + $icons = ''; + foreach($todo['icons'] as $name => $app) + { + $icons .= ($icons?' ':'').$GLOBALS['phpgw']->html->image($app,$name,lang($name),'border="0" width="15" height="15"'); + } + $class = $class == 'row_on' ? 'row_off' : 'row_on'; + + $content .= " \n \n \n \n"; + } + } + } + } + if (!empty($content)) + { + return "

' + . '' + . lang('Participant').'' + . '' + $str .= '' . '" . $k." 

'.$fullname.''.$fullname.'

". + ($this->bo->printer_friendly?$icons:$GLOBALS['phpgw']->html->a_href($icons,$todo['view'])). + "".($this->printer_friendly?$todo['title']: + $GLOBALS['phpgw']->html->a_href($todo['title'],$todo['view']))."
\n$content
\n"; + } + return False; + } + + /** + * Calculates the vertical position based on the time + * + * workday start- and end-time, is taken into account, as well as timeGrids px_m - minutes per pixel param + */ + function time2pos($time) + { + // time before workday => condensed in the first row + if ($this->wd_start > 0 && $time < $this->wd_start) + { + $pos = round($time / $this->px_m / $this->wd_start); + } + // time after workday => condensed in the last row + elseif ($this->wd_end < 24*60 && $time > $this->wd_end+2*$this->granularity_m) + { + $pos = $this->time2pos($this->wd_end+2*$this->granularity_m) + + round(($time - ($this->wd_end+2*$this->granularity_m)) / $this->px_m / + (24*60 - ($this->wd_end+2*$this->granularity_m))); + } + // time during the workday => 2. row on (= + granularity) + else + { + $pos = round(($time - $this->wd_start + $this->granularity_m) / $this->px_m); + } + if ($this->debug > 3) $this->bo->debug_message('uiviews::time2pos(%1)=%2',False,$time,$pos); + + return $pos; + } + + /** + * Calculates the height of a differenc between 2 times + * + * workday start- and end-time, is taken into account, as well as timeGrids px_m - minutes per pixel param + */ + function times2height($start,$end,$minimum=0) + { + $height = $this->time2pos($end) - $this->time2pos($start); + + if ($this->debug > 3) $this->bo->debug_message('uiviews::times2height(%1,%2,min=%3)=%4',False,$start,$end,$minimum,$height); + + return $height >= $minimum ? $height : $minimum; + } + + /** + * Creates a grid with rows for the time, columns for (multiple) days containing events + * + * Uses the dayColWidget to display each day. + * + * @param $daysEvents array with subarrays of events for each day to show, day as YYYYMMDD as key + * @param $width int width of the widget + * @param $granularity int granularity in minutes of the rows + * @param $px_m param int/float minutes per pixel - pixel in minutest ;-) + */ + function timeGridWidget($daysEvents,$width,$granularity_m=30,$px_m=1.7,$indent='',$title='') + { + if ($this->debug > 1 || $this->debug==='timeGridWidget') $this->bo->debug_message('uiviews::timeGridWidget(events=%1,width=%2,granularity_m=%3,px_m=%4,)',True,$daysEvents,$width,$granularity_m,$px_m); + + $this->px_m = $px_m; // for time2pos() + $this->granularity_m = $granularity_m; + + $html = $indent.'
'."\n"; + + if ($title) + { + $html .= $indent."\t".'
'.$title."
\n"; + } + $off = True; // Off-row means a different bgcolor + for ($t = 0; $t < 24*60; $t += $inc) + { + $inc = $granularity_m; + if (!$t) + { + $inc = $this->wd_start; + } + elseif ($t > $this->wd_end) + { + $inc = 24*60 - $this->wd_end; + } + $html .= $indent."\t".'
'."\n"; + + $add_links = count($daysEvents) == 1 && $this->bo->check_perms(PHPGW_ACL_ADD,0,$this->owner); + $add = array( + 'menuaction' => 'calendar.uicalendar.add', + 'date' => $this->date, + ); + if ($t >= $this->wd_start && $t <= $this->wd_end) + { + $time = $GLOBALS['phpgw']->common->formattime(sprintf('%02d',$t/60),sprintf('%02d',$t%60)); + if ($add_links) + { + $add['hour'] = (int) ($t/60); + $add['minute'] = $t%60; + $time = $this->html->a_href($time,$add,'',' title="'.lang('Add').'"'); + } + $html .= $indent."\t\t".'
'.$time."
\n"; + } + $html .= $indent."\t
\n"; // calTimeRow + + $off = !$off; + } + + if (is_array($daysEvents) && count($daysEvents)) + { + $dayCols_width = $width - $this->timeRow_width - 1; + $html .= $indent."\t".'
'."\n"; + $dayCol_width = $dayCols_width / count($daysEvents); + $n = 0; + foreach($daysEvents as $day => $events) + { + $html .= $this->dayColWidget($day,$events,(int)($n*$dayCol_width),(int)$dayCol_width,$indent."\t\t",count($daysEvents) != 1,++$on_off & 1); + ++$n; + } + $html .= $indent."\t
\n"; // calDayCols + } + $html .= $indent."
\n"; // calTimeGrid + + return $html; + } + + /** + * Creates (if necessary multiple) columns for the events of a day + * + * Uses the eventColWidget to display each column. + * + * @param $day_ymd string/int date as Ymd + * @param $events array of events to show + * @param $left int start of the widget + * @param $width int width of the widget + * @param $short_title boolean should we add a label (weekday, day) with link to the day-view above each day + */ + function dayColWidget($day_ymd,$events,$left,$width,$indent,$short_title=True,$on_off=False) + { + if ($this->debug > 1 || $this->debug==='dayColWidget') $this->bo->debug_message('uiviews::dayColWidget(%1,%2,left=%3,width=%4,)',False,$day_ymd,$events,$left,$width); + + $day_start = $this->bo->date2ts((string)$day_ymd); + + // sorting the event into columns with none-overlapping events, the events are already sorted by start-time + $eventCols = $col_ends = array(); + foreach($events as $event) + { + $event['multiday'] = False; + $event['start_m'] = ($this->bo->date2ts($event['start']) - $day_start) / 60; + if ($event['start_m'] < 0) + { + $event['start_m'] = 0; + $event['multiday'] = True; + } + $event['end_m'] = ($this->bo->date2ts($event['end']) - $day_start) / 60; + if ($event['end_m'] >= 24*60) + { + $event['end_m'] = 24*60-1; + $event['multiday'] = True; + } + for($c = 0; $event['start_m'] < $col_ends[$c]; ++$c); + $eventCols[$c][] = $event; + $col_ends[$c] = $event['end_m']; + } + if (count($eventCols)) + { + $eventCol_dist = $eventCol_width = round($width / count($eventCols)); + $eventCol_min_width = 80; + if ($eventCol_width < $eventCol_min_width) + { + $eventCol_width = $eventCol_dist = $eventCol_min_width; + if (count($eventCols) > 1) + { + $eventCol_dist = round(($width - $eventCol_min_width) / (count($eventCols)-1)); + } + } + } + + $html = $indent.'
'."\n"; + + // Creation of the header-column with date, evtl. holiday-names and a matching background-color + $ts = $this->bo->date2ts((string)$day_ymd); + $title = lang(date('l',$ts)).', '.($short_title ? date('d.',$ts) : $this->bo->long_date($ts)); + $day_view = array( + 'menuaction' => 'calendar.uiviews.day', + 'date' => $day_ymd, + ); + if ($short_title) + { + $title = $this->html->a_href($title,$day_view,'', + !isset($this->holidays[$day_ymd])?' title="'.lang('Dayview').'"':''); + } + if (isset($this->holidays[$day_ymd])) + { + $class = 'calHoliday'; + foreach($this->holidays[$day_ymd] as $holiday) + { + $holidays[] = $holiday['name']; + } + $holidays = implode(', ',$holidays); + } + elseif ($day_ymd == $this->bo->date2string($this->bo->now_su)) + { + $class = 'calToday'; + } + else + { + $class = $on_off ? 'row_on' : 'row_off'; + } + $html .= $indent."\t".'
'. + $title.(!$short_title && $holidays ? ': '.$holidays : '')."
\n"; + + foreach($eventCols as $n => $eventCol) + { + $html .= $this->eventColWidget($eventCol,$n*$eventCol_dist,$eventCol_width,$indent."\t"); + } + $html .= $indent."
\n"; // calDayCol + + return $html; + } + + /** + * Creates colunm for non-overlaping (!) events + * + * Uses the eventWidget to display each event. + * + * @param $events array of events to show + * @param $left int start of the widget + * @param $width int width of the widget + */ + function eventColWidget($events,$left,$width,$indent) + { + if ($this->debug > 1 || $this->debug==='eventColWidget') $this->bo->debug_message('uiviews::eventColWidget(%1,left=%2,width=%3,)',False,$events,$left,$width); + + $html = $indent.'
'."\n"; + foreach($events as $event) + { + $html .= $this->eventWidget($event,$width,$indent."\t"); + } + $html .= $indent."
\n"; + + return $html; + } + + /** + * Shows one event + * + * @param $event array with the data of event to show + * @param $width int width of the widget + */ + function eventWidget($event,$width,$indent) + { + if ($this->debug > 1 || $this->debug==='eventWidget') $this->bo->debug_message('uiviews::eventWidget(%1)',False,$event); + + static $tpl = False; + if (!$tpl) + { + $tpl = $GLOBALS['phpgw']->template; + $tpl->set_file('event_widget_t','event_widget.tpl'); + $tpl->set_block('event_widget_t','event_widget'); + $tpl->set_block('event_widget_t','event_tooltip'); + } + + if ($event['start_m'] == 0 && $event['end_m'] == 24*60-1) + { + $timespan = lang('all day'); + } + else + { + foreach(array($event['start_m'],$event['end_m']) as $minutes) + { + $timespan[] = $GLOBALS['phpgw']->common->formattime(sprintf('%02d',$minutes/60),sprintf('%02d',$minutes%60)); + } + $timespan = implode(' - ',$timespan); + } + $icons = $this->event_icons($event); + $cats = $this->bo->categories($event['category'],$color); + + // these values control varius aspects of the geometry of the eventWidget + $small_trigger_width = 120 + 20*count($icons); + $corner_radius=$width > $small_trigger_width ? 10 : 5; + $header_height=$width > $small_trigger_width ? 19 : 12; // multi_3 icon has a height of 19=16+2*1padding+1border ! + $height = $this->times2height($event['start_m'],$event['end_m'],$header_height); + $body_height = max(0,$height - $header_height - $corner_radius); + $border=1; + $headerbgcolor = $color ? $color : '#808080'; + // the body-colors (gradient) are calculated from the headercolor, which depends on the cat of an event + $bodybgcolor1 = $this->brighter($headerbgcolor,170); + $bodybgcolor2 = $this->brighter($headerbgcolor,220); + + $tpl->set_var(array( + // event-content, some of it displays only if it really has content or is needed + 'header_icons' => $width > $small_trigger_width ? implode("",$icons) : '', + 'body_icons' => $width > $small_trigger_width ? '' : implode("\n",$icons).'
', + 'icons' => implode("\n",$icons), + 'header' => $timespan, + 'title' => $this->html->htmlspecialchars($event['title']), + 'description' => nl2br($this->html->htmlspecialchars($event['description'])), + 'location' => $this->add_nonempty($event['location'],lang('Location')), + 'participants' => count($event['participants']) == 1 && isset($event['participants'][$this->user]) ? '' : + $this->add_nonempty($this->bo->participants($event['participants']),lang('Participants'),True), + 'multidaytimes' => !$event['multiday'] ? '' : + $this->add_nonempty($GLOBALS['phpgw']->common->show_date($this->bo->date2ts($event['start'])),lang('Start Date/Time')). + $this->add_nonempty($GLOBALS['phpgw']->common->show_date($this->bo->date2ts($event['end'])),lang('End Date/Time')), + 'category' => $this->add_nonempty($cats,lang('Category')), + // the tooltip is based on the content of the actual widget, this way it takes no extra bandwidth/volum +// 'tooltip' => $this->html->tooltip(False,False,array('BorderWidth'=>0,'Padding'=>0)), + // various aspects of the geometry or style + 'corner_radius' => $corner_radius.'px', + 'header_height' => $header_height.'px', + 'body_height' => $body_height.'px', + 'height' => $height, + 'width' => ($width-20).'px', + 'border' => $border, + 'bordercolor' => $headerbgcolor, + 'headerbgcolor' => $headerbgcolor, + 'bodybackground' => 'url('.$GLOBALS['phpgw_info']['server']['webserver_url']. + '/calendar/inc/gradient.php?color1='.urlencode($bodybgcolor1).'&color2='.urlencode($bodybgcolor2). + '&width='.$width.') repeat-y '.$bodybgcolor2, + 'Small' => $width > $small_trigger_width ? '' : 'Small', // to use in css class-names + )); + foreach(array( + 'upper_left'=>array('width'=>-$corner_radius,'height'=>$header_height,'border'=>0,'bgcolor'=>$headerbgcolor), + 'upper_right'=>array('width'=>$corner_radius,'height'=>$header_height,'border'=>0,'bgcolor'=>$headerbgcolor), + 'lower_left'=>array('width'=>-$corner_radius,'height'=>-$corner_radius,'border'=>$border,'color'=>$headerbgcolor,'bgcolor'=>$bodybgcolor1), + 'lower_right'=>array('width'=>$corner_radius,'height'=>-$corner_radius,'border'=>$border,'color'=>$headerbgcolor,'bgcolor'=>$bodybgcolor2), + ) as $name => $data) + { + $tpl->set_var($name.'_corner',$GLOBALS['phpgw_info']['server']['webserver_url']. + '/calendar/inc/round_corners.php?width='.$data['width'].'&height='.$data['height']. + '&bgcolor='.urlencode($data['bgcolor']). + (isset($data['color']) ? '&color='.urlencode($data['color']) : ''). + (isset($data['border']) ? '&border='.urlencode($data['border']) : '')); + } + $tooltip = $tpl->fp('tooltip','event_tooltip'); + $tpl->set_var('tooltip',$this->html->tooltip($tooltip,False,array('BorderWidth'=>0,'Padding'=>0))); + $html = $tpl->fp('out','event_widget'); + + $view_link = $GLOBALS['phpgw']->link('/index.php',array('menuaction'=>'calendar.uicalendar.view','cal_id'=>$event['id'],'date'=>$event['start']['full'])); + + return $indent.'
'."\n". + $html.$indent."
\n"; + } + + function add_nonempty($content,$label,$one_per_line=False) + { + if (is_array($content)) + { + $content = implode($one_per_line ? ",\n" : ', ',$content); + } + if (!empty($content)) + { + return ''.$label.':'. + ($one_per_line ? '
' : ' '). + nl2br($this->html->htmlspecialchars($content)).'
'; + } + return ''; + } + + /** + * Calculates a brighter color for a given color + * + * @param $rgb string color as #rrggbb value + * @param $decr int value to add to each component, default 64 + * @return string the brighter color + */ + function brighter($rgb,$decr=64) + { + if (!preg_match('/^#?([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/',$rgb,$components)) + { + return '#ffffff'; + } + $brighter = '#'; + for ($i = 1; $i <=3; ++$i) + { + $val = hexdec($components[$i]) + $decr; + if ($val > 255) $val = 255; + $brighter .= sprintf('%02x',$val); + } + //echo "brighter($rgb=".print_r($components,True).")=$brighter

\n"; + return $brighter; + } + + /** + * trys to figure out the inner window width from the browser and installs an onResize handler + * + * If no $_REQUEST[windowInnerWidth] is present, the pages get reloaded with some javascript, which sets + * the width in the URL. If a width could be determined, an onResize handler gets installed, which refreshes + * the page, with the new width set in the URL. + * If we are allowed to set cookies, we additionaly set a cookie with the width, to save the additonal reload. + * An onLoad handler checks if we operate with the correct width, or reloads the page if not. + */ + function establish_width() + { + if (!isset($_REQUEST['windowInnerWidth'])) // we neither have a cookie nor an url-var, get one ... + { +?> + + + + + + + + + +common->phpgw_exit(); + } + + $width = (int) $_GET['windowInnerWidth'] ? $_GET['windowInnerWidth'] : $_COOKIE['windowInnerWidth']; + + if ($GLOBALS['phpgw_info']['server']['usecookies']) + { + $GLOBALS['phpgw']->session->phpgw_setcookie('windowInnerWidth',$width); + } + if ($width) + { + $GLOBALS['phpgw_info']['flags']['java_script'] = ' + +'; + if (!is_object($GLOBALS['phpgw']->js)) + { + $GLOBALS['phpgw']->js = CreateObject('phpgwapi.javascript'); + } + $GLOBALS['phpgw']->js->set_onresize('reload_inner_width();'); + $GLOBALS['phpgw']->js->set_onload('if ('.$width.'!=(window.innerWidth ? window.innerWidth : document.body.offsetWidth)) reload_inner_width();'); + } + else + { + $width = 1000; // default, if the browser does not report it + } + return $width - 250; // 180 for sidebox-menu, TODO: this need to come from the template + } +} diff --git a/calendar/inc/gradient.php b/calendar/inc/gradient.php new file mode 100644 index 0000000000..14a24a440b --- /dev/null +++ b/calendar/inc/gradient.php @@ -0,0 +1,84 @@ +1,'height'=>1,'color1'=>'000080','color2'=>'ffffff') as $name => $default) +{ + $$name = isset($_GET[$name]) ? $_GET[$name] : $default; +} + +foreach(array('color1','color2') as $name) +{ + preg_match('/^#?([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/',$$name,$rgb) or + die("Wrong value '".$$name."' for $name, should be something like #80FFFF' !!!"); + + $$name = array('r'=>hexdec($rgb[1]),'g'=>hexdec($rgb[2]),'b'=>hexdec($rgb[3])); +} +$image = @imagecreate(abs($width),abs($height)) + or die("Cannot Initialize new GD image stream"); + +$length = max($width,$height); +$dist = $length / 256; +if ($dist < 1) $dist = 1; +$anz = round($length / $dist); +foreach ($color1 as $c => $val) +{ + $c_step[$c] = ($color2[$c] - $val) / $anz; +} + +$rgb = $color1; +for ($l = 0; $l < $length; $l += $dist) +{ + $color = imagecolorallocate($image,(int)$rgb['r'],(int)$rgb['g'],(int)$rgb['b']); + foreach($rgb as $c => $val) + { + $rgb[$c] += $c_step[$c]; + } + if ($width > $height) + { + imagefilledrectangle($image,(int)$l,0,(int) ($l+$dist),$height-1,$color); + } + else + { + imagefilledrectangle($image,0,(int)$l,$width-1,(int) ($l+$dist),$color); + } +} + +session_cache_limiter('public'); // allow caching +if (function_exists('imagegif')) +{ + header("Content-type: image/gif"); + imagegif($image); +} +else +{ + header("Content-type: image/png"); + imagepng($image); +} +imagedestroy($image); +?> diff --git a/calendar/inc/hook_settings.inc.php b/calendar/inc/hook_settings.inc.php index e3a8cecfce..8bd2061926 100644 --- a/calendar/inc/hook_settings.inc.php +++ b/calendar/inc/hook_settings.inc.php @@ -28,6 +28,15 @@ create_select_box('default calendar view','defaultcalendar',$default, 'Which of calendar view do you want to see, when you start calendar ?'); + + /* Select list with number of day by week */ + $week_view = array( + '5' => lang('Weekview without weekend'), + '7' => lang('Weekview including weekend'), + ); + create_select_box('default week view', 'days_in_weekview', $week_view, 'Do you want a weekview with or without weekend?'); + + /* Selection of list for home page is different from default calendar, since the decision for the front page is different for the decision for the main calendar page. But the list could be added to the @@ -184,7 +193,7 @@ $freebusy_url = ($_SERVER['HTTPS'] ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].$freebusy_url; } $freebusy_help = lang('Should not loged in persons be able to see your freebusy information? You can set an extra password, different from your normal password, to protect this informations. The freebusy information is in iCal format and only include the times when you are busy. It does not include the event-name, description or locations. The URL to your freebusy information is %1.',''.$freebusy_url.''); - create_check_box('Make freebusy information availible to not loged in persons?','freebusy', + create_check_box('Make freebusy information available to not loged in persons?','freebusy', $freebusy_help,'',False); create_input_box('Password for not loged in users to your freebusy information?','freebusy_pw', - 'If you dont set a password here, the information is availible to everyone, who knows the URL!!!'); + 'If you dont set a password here, the information is available to everyone, who knows the URL!!!'); diff --git a/calendar/inc/hook_sidebox_menu.inc.php b/calendar/inc/hook_sidebox_menu.inc.php index 1a55288094..21645a9217 100644 --- a/calendar/inc/hook_sidebox_menu.inc.php +++ b/calendar/inc/hook_sidebox_menu.inc.php @@ -1,65 +1,29 @@ * - * -------------------------------------------- * - * 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. * - \**************************************************************************/ +/**************************************************************************\ +* eGroupWare - Calendar's Sidebox-Menu for idots-template * +* http://www.egroupware.org * +* Written by Pim Snel * +* -------------------------------------------- * +* 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$ */ +/* $Id$ */ + +/** +* File is depricated, the hook moved into the class uical !!! +*/ + +// register the new hooks, if the admin missed it ;-) + +if (!is_object($GLOBALS['phpgw']->hooks)) { - - /* - This hookfile is for generating an app-specific side menu used in the idots - template set. - - $menu_title speaks for itself - $file is the array with link to app functions - - display_sidebox can be called as much as you like - */ - - $menu_title = $GLOBALS['phpgw_info']['apps'][$appname]['title'] . ' '. lang('Menu'); - $file = Array( - 'New Entry' => $GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.add'), - '_NewLine_', // give a newline - 'Today'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.day'), - 'This week'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.week'), - 'This month'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.month'), - 'This year'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.year'), - 'Group Planner'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.planner'), - 'Daily Matrix View'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.matrixselect'), - 'Export'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicalendar.export'), - 'Import'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uiicalendar.import') - ); - display_sidebox($appname,$menu_title,$file); - - if ($GLOBALS['phpgw_info']['user']['apps']['preferences']) - { - $menu_title = lang('Preferences'); - $file = Array( - 'Calendar preferences'=>$GLOBALS['phpgw']->link('/preferences/preferences.php','appname=calendar'), - 'Grant Access'=>$GLOBALS['phpgw']->link('/index.php','menuaction=preferences.uiaclprefs.index&acl_app=calendar'), - 'Edit Categories' =>$GLOBALS['phpgw']->link('/index.php','menuaction=preferences.uicategories.index&cats_app=calendar&cats_level=True&global_cats=True'), - ); - display_sidebox($appname,$menu_title,$file); - } - - if ($GLOBALS['phpgw_info']['user']['apps']['admin']) - { - $menu_title = lang('Administration'); - $file = Array( - 'Configuration'=>$GLOBALS['phpgw']->link('/index.php','menuaction=admin.uiconfig.index&appname=calendar'), - 'Custom Fields'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uicustom_fields.index'), - 'Holiday Management'=>$GLOBALS['phpgw']->link('/index.php','menuaction=calendar.uiholiday.admin'), - 'Import CSV-File' => $GLOBALS['phpgw']->link('/calendar/csv_import.php'), - 'Global Categories' =>$GLOBALS['phpgw']->link('/index.php','menuaction=admin.uicategories.index&appname=calendar'), - ); - display_sidebox($appname,$menu_title,$file); - } + $GLOBALS['phpgw']->hooks = CreateObject('phpgwapi.hooks'); } -?> +include(PHPGW_INCLUDE_ROOT . '/calendar/setup/setup.inc.php'); + +$GLOBALS['phpgw']->hooks->register_hooks('calendar',$setup_info['calendar']['hooks']); + +ExecMethod($setup_info['calendar']['hooks']['sidebox_menu']); diff --git a/calendar/inc/round_corners.php b/calendar/inc/round_corners.php new file mode 100644 index 0000000000..c8bd42491d --- /dev/null +++ b/calendar/inc/round_corners.php @@ -0,0 +1,88 @@ +-20,'height'=>40,'border'=>1,'color'=>'000080','bgcolor'=>'0000FF') as $name => $default) +{ + $$name = isset($_GET[$name]) ? $_GET[$name] : $default; +} + +$image = @imagecreate(abs($width),abs($height)) + or die("Cannot Initialize new GD image stream"); + +$white = imagecolorallocate($image, 254, 254, 254); +imagecolortransparent($image, $white); + +foreach(array('color','bgcolor') as $name) +{ + preg_match('/^#?([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/',$$name,$rgb) or + die("Wrong value '".$$name."' for $name, should be something like #80FFFF' !!!"); + + $$name = imagecolorallocate($image,hexdec($rgb[1]),hexdec($rgb[2]),hexdec($rgb[3])); +} +$radius = min(abs($width),abs($height)); +$center_x = $width > 0 ? abs($width)-$radius-1 : $radius; +$center_y = $height < 0 ? abs($height)-$radius-1 : $radius; +//echo "width=$width, height=$height => radius=$radius: center_x=$center_x, center_y=$center_y"; +if ($border) imagefilledellipse($image,$center_x,$center_y,2*$radius,2*$radius,$color); +imagefilledellipse($image,$center_x,$center_y,2*($radius-$border),2*($radius-$border),$bgcolor); + +if (abs($height) > abs($width)) +{ + if ($height < 0) // lower corners + { + $y1 = 0; + $y2 = abs($height)-$radius-1; + } + else + { + $y1 = $radius; + $y2 = abs($height)-1; + } + imagefilledrectangle($image,0,$y1,abs($width),$y2,$bgcolor); + if ($border) + { + $x1 = $width < 0 ? 0 : abs($width)-$border; + $x2 = $width < 0 ? $border-1 : abs($width)-1; + imagefilledrectangle($image,$x1,$y1,$x2,$y2,$color); + } +} + +session_cache_limiter('public'); // allow caching +if (function_exists('imagegif')) +{ + header("Content-type: image/gif"); + imagegif($image); +} +else +{ + header("Content-type: image/png"); + imagepng($image); +} +imagedestroy($image); +?> diff --git a/calendar/setup/phpgw_de.lang b/calendar/setup/phpgw_de.lang index efa6616cef..24b71a954b 100644 --- a/calendar/setup/phpgw_de.lang +++ b/calendar/setup/phpgw_de.lang @@ -20,6 +20,7 @@ alarm for %1 at %2 in %3 calendar de Alarm f alarm management calendar de Alarm Management alarm-management calendar de Alarm Management alarms calendar de Alarme +all categories calendar de Alle Kategorien all day calendar de ganztägig are you sure you want to delete this country ? calendar de Sind Sie sicher, dass Sie dieses Land löschen wollen ? are you sure you want to delete this holiday ? calendar de Sind Sie sicher, dass Sie diesen Feiertag löschen wollen ? @@ -68,6 +69,7 @@ default calendar filter calendar de Standard-Filter des Kalenders default calendar view calendar de Standard-Ansicht des Kalenders default length of newly created events. the length is in minutes, eg. 60 for 1 hour. calendar de Vorgabe für die Länge von neuen Kalendareinträgen. Die Länge ist in Minuten, zb. 60 für 1 Stunde. default view of appointments calendar de Standard-Ansicht der Termine +default week view calendar de Vorgabe Wochenansicht defines the size in minutes of the lines in the day view. calendar de Bestimmt die Zeitskala in Minuten der Tagesansicht des Kalendars. delete a single entry by passing the id. calendar de Lösche einen einzelnen Eintrag über seine Id. delete an entire users calendar. calendar de Lösche den kompletten Kalender eines Benutzers. @@ -76,12 +78,15 @@ delete series calendar de Serie l delete single calendar de Einzelevent löschen deleted calendar de Abgesagt description calendar de Beschreibung +determining window width ... calendar de Bestimme die Fensterbreite ... disable calendar de Ausschalten disabled calendar de ausgeschaltet display interval in day view calendar de Intervall der Tagesansicht display mini calendars when printing calendar de zeige einen kleinen Kalender beim drucken display status of events calendar de Status von Terminen anzeigen +displayed view calendar de Ansicht displays your default calendar view on the startpage (page you get when you enter egroupware or click on the homepage icon)? calendar de Zeigt Ihre Standard-Kalendar-Ansicht auf der Startseite angezeigt werden (die Seite die sich nach dem Login öffnet oder wenn Sie auf Home klicken)? +do you want a weekview with or without weekend? calendar de Wollen Sie eine Wochenansicht mit oder ohne Wochenende? do you want to be notified about new or changed appointments? you be notified about changes you make yourself.
you can limit the notifications to certain changes only. each item includes all the notification listed above it. all changes include changes of title, description, participants and the acceptions and rejections of the appointment. calendar de Möchten Sie über neue oder geänderte Termine benachrichtigt werden? Sie werden benachrichtigt werden über Änderungen welche Sie selbst durchführen.
Sie können die benachrichtigungen beschränken auf bestimmte Änderungen. Jeder auszuwählende Punkt schließt auch die darüber aufgeführten Benachrichtigungen mit ein. Alle Änderungen beinhaltet Änderungen des Titels, der Beschreibung, Teilnehmer so wie Zusagen so wie Absagen von Terminen. do you want to be notified about new or changed appointments? you be notified about changes you make yourself.
you can limit the notifications to certain changes only. each item includes all the notification listed above it. all modifications include changes of title, description, participants, but no participant responses. if the owner of an event requested any notifcations, he will always get the participant responses like acceptions and rejections too. calendar de Wollen Sie über neue oder geänderte Termine benachrichtigt werden? Sie werden nie über eigene Änderungen benachrichtig.
Sie können die Benachrichtigungen auf verschiedene Änderungen begrenzen. Jede Zeile enthält die Benachrichtigungen der darüberliegenden. Alle Änderungen umfasst den Title, die Beschreibung, Teilnehmer, aber keine Antworten der Teilnehmer. Wenn der Ersteller eines Event irgendeine Benachrichtigung will, erhält er automatisch auch die Antworten der Teilnehmer, wie Zusagen und Absagen. do you want to receive a regulary summary of your appointsments via email?
the summary is sent to your standard email-address on the morning of that day or on monday for weekly summarys.
it is only sent when you have any appointments on that day or week. calendar de Möchten Sie eine regelmäßige Zusammenfassung Ihrer Termine via E-Mail erhalten?
Die Zusammenfassung wird täglich (jeden Morgen), oder für eine wöchentliche Zusammenfassung Montags an Ihre standard E-Mail Adresse gesendet.
Die Benachrichtigung wird nur versendet wenn Sie am nächsten Tag oder in der nächsten Woche auch einen Termin haben. @@ -167,8 +172,11 @@ monthly calendar de Monatlich monthly (by date) calendar de Monatlich (nach Datum) monthly (by day) calendar de Monatlich (nach Wochentag) monthview calendar de Monatsansicht +needs javascript to be enabled !!! calendar de Dafür muss Javascript aktiviert sein !!! new entry calendar de Neuer Eintrag new name must not exist and not be empty!!! calendar de Neuer Name darf nicht exisitieren und nicht leer sein!!! +next day calendar de nächster Tag +no filter calendar de Kein Filter no matches found calendar de Keine Treffer gefunden no matches found. calendar de Keine Treffer gefunden. no response calendar de Keine Antwort @@ -210,6 +218,7 @@ planner by user calendar de Planer nach Benutzern please confirm,accept,reject or examine changes in the corresponding entry in your calendar calendar de Bitte den entsprechenden Eintrag im Ihrem Kalendar bestätigen, Zusagen, Absagen oder die Änderungen beachten please enter a filename !!! calendar de Bitte geben Sie einen Dateinamen an !!! preselected group for entering the planner calendar de vorausgewählte Gruppe beim Planeraufruf +previous day calendar de vorheriger Tag print calendars in black & white calendar de Kalender in schwarz und weiß drucken print the mini calendars calendar de Mini- Kalender drucken printer friendly calendar de Drucker-freundlich @@ -242,6 +251,7 @@ sa calendar de Sa sat calendar de Sa scheduling conflict calendar de Terminüberschneidung search results calendar de Suchergebnisse +select a %1 calendar de %1 auswählen selected contacts (%1) calendar de Ausgewählte Kontakte (%1) send updates via email common de Updates via E-Mail senden send/receive updates via email calendar de Senden/Empfangen von Aktualisierungen via EMail @@ -258,6 +268,11 @@ show default view on main screen calendar de Standardansicht auf der Startseite show high priority events on main screen calendar de Termine mit hoher Priorität auf der Startseite anzeigen show invitations you rejected calendar de Zeige Einladungen welche abgelehnt wurden an show list of upcoming events calendar de Zeige eine Liste der kommenden Termine +show next seven days calendar de Nächsten sieben Tage anzeigen +show only one day calendar de Nur einen Tag anzeigen +show the calendar of multiple users calendar de Kalender von mehreren Benutzeren anzeigen +show this month calendar de Diesen Monat anzeigen +show this week calendar de Diese Woche anzeigen single event calendar de Einzelner Termin sorry, the owner has just deleted this event calendar de Der Eigentümer hat diesen Termin gerade gelöscht sorry, this event does not exist calendar de Dieser Termin existiert nicht @@ -305,6 +320,7 @@ today calendar de Heute translation calendar de Übersetzung tu calendar de Di tue calendar de Di +two dayview calendar de Zweitagesansicht update a single entry by passing the fields. calendar de Einen einzelnen Termin über seine Felder updaten. updated calendar de Aktualisiert use end date calendar de Enddatum benutzen @@ -315,15 +331,18 @@ week calendar de Woche weekday starts on calendar de Arbeitswoche beginnt am weekly calendar de Wöchentlich weekview calendar de Wochenansicht +weekview including weekend calendar de Wochenansicht mit Wochenende +weekview without weekend calendar de Wochenansicht ohne Wochenende when creating new events default set to private calendar de neue Termine sind privat which events do you want to see when you enter the calendar. calendar de Welche Termine möchten Sie angezeigt bekommen wenn Sie den Kalender öffnen? which of calendar view do you want to see, when you start calendar ? calendar de Welche der möglichen Ansichten des Kalenders möchten Sie als Standard sehen wenn der Kalender geöffnet wird? +wk calendar de KW work day ends on calendar de Arbeitstag endet um work day starts on calendar de Arbeitstag beginnt um workdayends calendar de Arbeitstag endet year calendar de Jahr yearly calendar de Jährlich -yearview calendar de Jährliche Ansicht +yearview calendar de Jahresansicht you can either set a year or a occurence, not both !!! calendar de Sie können nur entweder das Jahr oder die Wiederholung angeben, nicht beides! you can only set a year or a occurence !!! calendar de Sie können nur ein Jahr oder eine Wiederholung angeben ! you do not have permission to add alarms to this event !!! calendar de Sie haben keine Berechtigung Alarme zu diesem Termin zuzufügen !!! diff --git a/calendar/setup/phpgw_en.lang b/calendar/setup/phpgw_en.lang index bb995a6192..15f0f5c128 100644 --- a/calendar/setup/phpgw_en.lang +++ b/calendar/setup/phpgw_en.lang @@ -20,6 +20,7 @@ alarm for %1 at %2 in %3 calendar en Alarm for %1 at %2 in %3 alarm management calendar en Alarm Management alarm-management calendar en Alarm-Management alarms calendar en Alarms +all categories calendar en All categories all day calendar en All Day are you sure you want to delete this country ? calendar en Are you sure you want to delete this Country ? are you sure you want to delete this holiday ? calendar en Are you sure you want to delete this holiday ? @@ -61,6 +62,7 @@ default appointment length (in minutes) calendar en default appointment length ( default calendar filter calendar en Default calendar filter default calendar view calendar en Default calendar view default length of newly created events. the length is in minutes, eg. 60 for 1 hour. calendar en Default length of newly created events. The length is in minutes, eg. 60 for 1 hour. +default week view calendar en Default week view defines the size in minutes of the lines in the day view. calendar en Defines the size in minutes of the lines in the day view. delete a single entry by passing the id. calendar en Delete a single entry by passing the id. delete an entire users calendar. calendar en Delete an entire users calendar. @@ -69,12 +71,15 @@ delete series calendar en Delete Series delete single calendar en Delete Single deleted calendar en Deleted description calendar en Description +determining window width ... calendar en Determining window width ... disable calendar en Disable disabled calendar en disabled display interval in day view calendar en Display interval in Day View display mini calendars when printing calendar en Display mini calendars when printing display status of events calendar en Display Status of Events +displayed view calendar en displayed view displays your default calendar view on the startpage (page you get when you enter egroupware or click on the homepage icon)? calendar en Displays your default calendar view on the startpage (page you get when you enter eGroupWare or click on the homepage icon)? +do you want a weekview with or without weekend? calendar en Do you want a weekview with or without weekend? do you want to be notified about new or changed appointments? you be notified about changes you make yourself.
you can limit the notifications to certain changes only. each item includes all the notification listed above it. all modifications include changes of title, description, participants, but no participant responses. if the owner of an event requested any notifcations, he will always get the participant responses like acceptions and rejections too. calendar en Do you want to be notified about new or changed appointments? You be notified about changes you make yourself.
You can limit the notifications to certain changes only. Each item includes all the notification listed above it. All modifications include changes of title, description, participants, but no participant responses. If the owner of an event requested any notifcations, he will always get the participant responses like acceptions and rejections too. do you want to receive a regulary summary of your appointsments via email?
the summary is sent to your standard email-address on the morning of that day or on monday for weekly summarys.
it is only sent when you have any appointments on that day or week. calendar en Do you want to receive a regulary summary of your appointsments via email?
The summary is sent to your standard email-address on the morning of that day or on Monday for weekly summarys.
It is only sent when you have any appointments on that day or week. do you wish to autoload calendar holidays files dynamically? admin en Do you wish to autoload calendar holidays files dynamically? @@ -157,8 +162,11 @@ monthly calendar en Monthly monthly (by date) calendar en Monthly (by date) monthly (by day) calendar en Monthly (by day) monthview calendar en Month View +needs javascript to be enabled !!! calendar en Needs javascript to be enabled !!! new entry calendar en New Entry new name must not exist and not be empty!!! calendar en New name must not exist and not be empty!!! +next day calendar en next day +no filter calendar en No filter no matches found calendar en No matches found no response calendar en No Response notification messages for added events calendar en Notification messages for added events @@ -195,6 +203,7 @@ planner by user calendar en Planner by user please confirm,accept,reject or examine changes in the corresponding entry in your calendar calendar en Please confirm, accept, reject or examine changes in the corresponding entry in your calendar please enter a filename !!! calendar en please enter a filename !!! preselected group for entering the planner calendar en Preselected group for entering the planner +previous day calendar en previous day print calendars in black & white calendar en Print calendars in black & white print the mini calendars calendar en Print the mini calendars printer friendly calendar en Printer Friendly @@ -225,6 +234,7 @@ sa calendar en Sa sat calendar en Sat scheduling conflict calendar en Scheduling Conflict search results calendar en Search Results +select a %1 calendar en Select a %1 selected contacts (%1) calendar en Selected contacts (%1) send updates via email common en Send updates via EMail send/receive updates via email calendar en Send/Receive updates via EMail @@ -241,6 +251,11 @@ show default view on main screen calendar en Show default view on main screen show high priority events on main screen calendar en Show high priority events on main screen show invitations you rejected calendar en Show invitations you rejected show list of upcoming events calendar en Show list of upcoming events +show next seven days calendar en Show next seven days +show only one day calendar en Show only one day +show the calendar of multiple users calendar en show the calendar of multiple users +show this month calendar en show this month +show this week calendar en show this week single event calendar en single event sorry, the owner has just deleted this event calendar en Sorry, the owner has just deleted this event sorry, this event does not exist calendar en Sorry, this event does not exist @@ -286,6 +301,7 @@ today calendar en Today translation calendar en Translation tu calendar en T tue calendar en Tue +two dayview calendar en two dayview update a single entry by passing the fields. calendar en Update a single entry by passing the fields. updated calendar en Updated use end date calendar en Use End date @@ -296,9 +312,12 @@ week calendar en Week weekday starts on calendar en Weekday starts on weekly calendar en Weekly weekview calendar en Week View +weekview including weekend calendar en Weekview including weekend +weekview without weekend calendar en weekview without weekend when creating new events default set to private calendar en When creating new events default set to private which events do you want to see when you enter the calendar. calendar en Which events do you want to see when you enter the calendar. which of calendar view do you want to see, when you start calendar ? calendar en Which of calendar view do you want to see when you start calendar ? +wk calendar en Wk work day ends on calendar en Work day ends on work day starts on calendar en Work day starts on workdayends calendar en workdayends diff --git a/calendar/setup/phpgw_fr.lang b/calendar/setup/phpgw_fr.lang index bd52259116..a64e43f348 100644 --- a/calendar/setup/phpgw_fr.lang +++ b/calendar/setup/phpgw_fr.lang @@ -106,7 +106,7 @@ firstname of person to notify calendar fr Pr format of event updates calendar fr Format des mises à jour d'événement fr calendar fr V free/busy calendar fr Libre/occupé -freebusy: unknow user '%1', wrong password or not availible to not loged in users !!! calendar fr +freebusy: unknow user '%1', wrong password or not available to not loged in users !!! calendar fr frequency calendar fr Fréquence fri calendar fr Ven full description calendar fr Description complète @@ -129,7 +129,7 @@ hours calendar fr Heures i participate calendar fr Je participe ical / rfc2445 calendar fr iCal / rfc2445 if checked holidays falling on a weekend, are taken on the monday after. calendar fr Si activé les vacances tombant sur un Week-End, sont prises sur le lundi d'après. -if you dont set a password here, the information is availible to everyone, who knows the url!!! calendar fr Si vous ne mettez pas de mot de passe ici, l'information sera accessible par n'importe quelle personne connaissant l'URL! +if you dont set a password here, the information is available to everyone, who knows the url!!! calendar fr Si vous ne mettez pas de mot de passe ici, l'information sera accessible par n'importe quelle personne connaissant l'URL! ignore conflict calendar fr Ignorer le conflit import calendar fr Import import csv-file common fr Importer un fichier CSV @@ -148,7 +148,6 @@ list all categories. calendar fr Lister toutes les cat load [iv]cal calendar fr Charger [iv]Cal location calendar fr Emplacement location to autoload from admin fr Emplacement depuis lequel auto-charger -make freebusy information availible to not loged in persons? calendar fr matrixview calendar fr Vue matricielle minutes calendar fr minutes mo calendar fr L @@ -194,7 +193,7 @@ password for not loged in users to your freebusy information? calendar fr people holiday calendar fr Vacances des personnes permission denied calendar fr Permission refusée planner calendar fr Planificateur -planner by category calendar fr Planificateur par catégrie +planner by category calendar fr Planificateur par catégorie planner by user calendar fr Planificateur par utilisateur please confirm,accept,reject or examine changes in the corresponding entry in your calendar calendar fr SVP confirmez, acceptez, rejetez ou examinez les modifications dans l'entrée correspondante de votre calendrier please enter a filename !!! calendar fr veuillez entrer un nom de fichier !!! @@ -329,3 +328,18 @@ you need to set either a day or a occurence !!! calendar fr Vous devez r your meeting scheduled for %1 has been canceled calendar fr Votre réunion planifiée pour %1 a été annulée your meeting that had been scheduled for %1 has been rescheduled to %2 calendar fr Votre réunion qui avait été planifiée pour %1 a été replanifiée pour %2 your suggested time of %1 - %2 conflicts with the following existing calendar entries: calendar fr L'heure que vous avez suggéré %1 - %2 entre en conflit avec les entrés suivantes du calendrier: +weekview without weekend calendar fr Vue par semaine sans Week-end +No filter calendar fr Pas de filtre +All categories calendar fr Pas de catégories +default week view calendar fr Vue par défaut pour les semaines +How much day do you want to see per week ? calendar fr Combien de jour voulez-vous afficher par semaine ? +previous day calendar fr précédent jour +next day calendar fr suivant jour +show next seven days calendar fr Voir les 7 prochains jours +show only one day calendar fr Voir un seul jour +show this week calendar fr Voir cette semaine +show the calendar of multiple users calendar fr Montrer le calendrier en multi-utilisateurs +make freebusy information available to not loged in persons? calendar fr Rends l'information de disponibilité accessible aux personnes non loggées +should not loged in persons be able to see your freebusy information? You can set an extra password, different from your normal password, to protect this informations. The freebusy information is in iCal format and only include the times when you are busy. It does not include the event-name, description or locations. The URL to your freebusy information is %1. calendar fr Permet aux personnes non connectés de disposer des informations de disponibilités. Vous pouvez définir un « extra password » différent de votre mot de passe habituel, afin de protéger cette information. Les informations disposées sont aux format iCal et inclus le temps ou vous êtes occupés. Il n'include pas le nom de l'évènement en lui-même, ni même sa description et ni sa localisation. Le lien pour voir cette information est celui ci : %1 +password for not loged in users to your freebusy information? calendar fr Le mot de passe « informations de disponibilité » pour les utilisateurs non-loggués +if you dont set a password here, the information is available to everyone, who knows the url!!! calendar fr Si vous ne définissez pas de mot de passe ici, vos informations de disponibilitées seront accessibles par tous connaissant l'adresse !!! diff --git a/calendar/setup/setup.inc.php b/calendar/setup/setup.inc.php index d3ab1746e2..78ebd22152 100755 --- a/calendar/setup/setup.inc.php +++ b/calendar/setup/setup.inc.php @@ -47,7 +47,7 @@ $setup_info['calendar']['hooks'][] = 'manual'; $setup_info['calendar']['hooks'][] = 'preferences'; $setup_info['calendar']['hooks'][] = 'settings'; - $setup_info['calendar']['hooks'][] = 'sidebox_menu'; + $setup_info['calendar']['hooks']['sidebox_menu'] = 'calendar.uical.sidebox_menu'; /* Dependencies for this app to work */ $setup_info['calendar']['depends'][] = array( diff --git a/calendar/templates/default/app.css b/calendar/templates/default/app.css index e7881b11d9..09954de131 100644 --- a/calendar/templates/default/app.css +++ b/calendar/templates/default/app.css @@ -1,5 +1,212 @@ /* CSS Document */ +/* + * CSS settings for the new uiviews code + */ + +/* timeGridWidget, contains timeRow's and dayCol's + */ +.calTimeGrid{ + position: relative; + top: 0px; + left: 0px; + border:1px solid gray; + margin-top: 20px; /* contains the dayColHeader */ +/* set via inline style on runtime: + * width: + */ +} + +/* single row in the time-line, always used in conjunction with row_{on|off}, you dont need to set a bgcolor, but you can + */ +.calTimeRow,.calTimeRowOff{ + position: relative; + width: 100%; +/* set via inline style on runtime: + * height: + */ +} +.calTimeRow{ +/* background-color: silver; */ +} + +/* time in a timeRow + */ +.calTimeRowTime{ + padding-left: 5px; + height: 100%; +} + +/* contains (multiple) dayCol's + */ +.calDayCols{ + position: absolute; + top: 0px; + height: 100%; +/* set via inline style on runtime: + * left: width(calTimeRowTime) + * width: 100% - width(calTimeRowTime) + */ +} + +/* contains (multiple) eventCol's + */ +.calDayCol{ + position: absolute; + top: 0px; + height: 100%; +/* set via inline style on runtime: + * left: + * width: + */ + border-left: 1px solid gray; +} + +/* header for the dayCol + */ +.calDayColHeader,.calGridHeader{ + position: absolute; + top: -20px; + width: 100%; + text-align: center; + font-size: 100%; + white-space: nowrap; + border: 1px solid gray; + height: 16px; + left: -1px; + padding-top: 2px; +} +.calToday{ + background: #ffffcc; +} +.calHoliday{ + background: #dac0c0; +} + +.calViewUserNameBox { + position: absolute; + top: -1px; + width: 95%; + text-align: left; + font-size: 120%; + white-space: nowrap; + border: 1px solid gray; + height: 17px; + left: -1px; + padding-top: 0px; + padding-left: 10px; + background: #dac0c0; +} +.calViewUserName { + font-weight: normal; +} +.calViewUserName:first-letter { + text-transform:uppercase; +} +.calViewUserNameFirst { +} +.calViewUserNameFirst:after { + content: ", "; +} + +/* header of the time-grid, eg. for the weeks in the month-view (leftmost of the day-col-headers) + */ +.calGridHeader{ + text-align: left; + padding-left: 3px; +} + +/* contains (multiple) events's + */ +.calEventCol{ + position: absolute; + top: 0px; + height: 100%; +/* set via inline style on runtime: + * left: + * width: + */ +/* border: 1px dotted red; */ +} + +/* contains one event: header-row & -body + */ +.calEvent,.calEvent:hover{ + position: absolute; +/* background-color: #ffffC0;*/ + left: 0px; + width: 100%; + overflow: hidden; +/* set via inline style on runtime: + * top: depending on startime + * top: depending on length + */ +} +.calEvent:hover{ +/* background-color: #ffff80;*/ + cursor: pointer; + cursor: hand; +} + +/* header-row of the event + */ +.calEventHeader,.calEventHeaderSmall{ + font-weight: bold; + font-size: 14px; + background-color: #0000ff; + color: white; + text-align: center; + white-space: nowrap; +} +.calEventHeaderSmall{ + font-size: 10px; +} + +/* body of the event + */ +.calEventBody,.calEventBodySmall{ + padding: 0px 3px 0px; +} + +.calEventBodySmall{ + font-size: 95%; +} + +.calEventLabel +{ + font-weight: bold; + font-size: 90%; +} + +.calEventTitle +{ + font-weight: bold; + font-size: 110%; +} + +/* cal day-view's todo column + */ +.calDayToDos { + width: 250px; + text-align: center; +} +/* header row +*/ +.calDayTodos .calDayTodosHeader { + margin: 0px; + padding: 2px; + font-weight: bold; +} +/* div containing the table with the ToDo's +*/ +.calDayTodos .calDayTodosTable { + overflow: auto; + max-height: 400px; +} + +/* + * From here on, settings for the "old" uicalendar code + */ .to_continue { font-size: 9px; @@ -17,7 +224,7 @@ A.bminicalendar color: #336699; font-style: italic; font-weight: bold; - font-size: 9px; + font-size: 9px; } A.minicalendargrey @@ -31,7 +238,7 @@ A.bminicalendargrey { color: #336699; font-style: italic; - font-size:10px; + font-size:10px; } A.minicalhol @@ -40,7 +247,7 @@ A.minicalhol padding-right:3px; background: #dab0b0; color: #000000; - font-size: 10px; + font-size: 10px; } A.bminicalhol @@ -49,7 +256,7 @@ A.bminicalhol padding-right:3px; background: #dab0b0; color: #336699; - font-size: 10px; + font-size: 10px; } A.minicalgreyhol @@ -58,7 +265,7 @@ A.minicalgreyhol padding-right:3px; background: #dab0b0; color: #999999; - font-size: 10px; + font-size: 10px; } A.bminicalgreyhol @@ -67,7 +274,7 @@ A.bminicalgreyhol padding-right:3px; background: #dab0b0; color: #999999; - font-size: 10px; + font-size: 10px; } @@ -259,4 +466,4 @@ A.event_entry font-size: 10px; font-weight: bold; font-style: italic; -} \ No newline at end of file +} diff --git a/calendar/templates/default/event_widget.tpl b/calendar/templates/default/event_widget.tpl new file mode 100644 index 0000000000..0cd7696905 --- /dev/null +++ b/calendar/templates/default/event_widget.tpl @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + +
{header_icons} {header}
+

{body_icons} + {title} +

+ + + + + + + + + + + + + + + + + +
{header_icons} {header}
+

{body_icons} + {title}
+ {description}

+

{multidaytimes} + {location} + {category} + {participants}

+
+ + diff --git a/calendar/templates/default/header.inc.php b/calendar/templates/default/header.inc.php index c7086cf105..2cc683d5e8 100755 --- a/calendar/templates/default/header.inc.php +++ b/calendar/templates/default/header.inc.php @@ -35,7 +35,7 @@ $tpl->set_block('head_tpl','head_table','head_table'); $tpl->set_block('head_tpl','head_col','head_col'); $tpl->set_block('form_button_script','form_button'); - +return; if(floor(phpversion()) >= 4) { $tpl->set_var('cols',8); @@ -131,7 +131,7 @@ $form_options = ''."\n"; $form_options .= ' '."\n"; - + $var = Array( 'form_width' => '28', 'form_link' => $this->page($referrer), @@ -151,7 +151,7 @@ $remainder -= 28; $form_options = ''."\n"; $form_options .= ' '."\n"; - + $var = Array( 'form_width' => '28', 'form_link' => $this->page($referrer), @@ -174,7 +174,7 @@ { $form_options .= ' '."\n"; } - + $var = Array( 'form_width' => $remainder, 'form_link' => $this->page($referrer),