* * -------------------------------------------- * * 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 UserInterface classes * * It manages eg. the state of the controls in the UI and generated the calendar navigation (sidebox-menu) * * 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 on server-time * * All permanent debug messages of the calendar-code should done via the debug-message method of the bocal class !!! * * @package calendar * @author Ralf Becker * @copyright (c) 2004/5 by RalfBecker-At-outdoor-training.de * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License */ class uical { /** * @var $debug mixed integer level or string function-name */ var $debug=false; /** * @var $bo class bocal */ var $bo,$jscal,$html,$datetime,$cats,$accountsel; /** * @var array $common_prefs reference to $GLOBALS['egw_info']['user']['preferences']['common'] */ var $common_prefs; /** * @var array $cal_prefs reference to $GLOBALS['egw_info']['user']['preferences']['calendar'] */ var $cal_prefs; /** * @var int $wd_start user pref. workday start */ var $wd_start; /** * @var int $wd_start user pref. workday end */ var $wd_end; /** * @var int $interval_m user pref. interval */ var $interval_m; /** * @var int $user account_id of loged in user */ var $user; /** * @var string $date session-state: date (Ymd) of shown view */ var $date; /** * @var int $cat_it session-state: selected category */ var $cat_id; /** * @var int $filter session-state: selected filter, NOT used at the moment (was all or private) */ var $filter; /** * @var int/array $owner session-state: selected owner(s) of shown calendar(s) */ var $owner; /** * @var boolean $multiple session-state: true multiple owners selected, false single user/group */ var $multiple; /** * @var string $sortby session-state: filter of planner: 'category' or 'user' */ var $sortby; /** * @var string $view session-state: selected view */ var $view; /** * @var string $view menuaction of the selected view */ var $view_menuaction; /** * @var int $first first day of the shown view */ var $first; /** * @var int $last last day of the shown view */ var $last; /** * Constructor * * @param boolean $use_bocalupdate use bocalupdate as parenent instead of bocal * @param array $set_states=null to manualy set / change one of the states, default NULL = use $_REQUEST */ function uical($use_bocalupdate=false,$set_states=NULL) { foreach(array( 'bo' => $use_bocalupdate ? 'calendar.bocalupdate' : 'calendar.bocal', 'jscal' => 'phpgwapi.jscalendar', // for the sidebox-menu 'html' => 'phpgwapi.html', 'datetime' => 'phpgwapi.datetime', 'accountsel' => 'phpgwapi.uiaccountsel', ) as $my => $app_class) { list(,$class) = explode('.',$app_class); if (!is_object($GLOBALS['egw']->$class)) { $GLOBALS['egw']->$class =& CreateObject($app_class); } $this->$my = &$GLOBALS['egw']->$class; } if (!is_object($this->cats)) { $this->cats =& CreateObject('phpgwapi.categories','','calendar'); // we need an own instance to get the calendar cats } $this->common_prefs = &$GLOBALS['egw_info']['user']['preferences']['common']; $this->cal_prefs = &$GLOBALS['egw_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['egw_info']['user']['account_id']; $this->manage_states($set_states); $GLOBALS['uical'] = &$this; // make us available for ExecMethod, else it creates a new instance // calendar does not work with hidden sidebox atm. unset($GLOBALS['egw_info']['user']['preferences']['common']['auto_hide_sidebox']); } /** * Checks and terminates (or returns for home) with a message if $this->owner include a user/resource we have no read-access to * * If currentapp == 'home' we return the error instead of terminating with it !!! * * @return boolean/string false if there's no error or string with error-message */ function check_owners_access() { $no_access = $no_access_group = array(); foreach(explode(',',$this->owner) as $owner) { if (is_numeric($owner) && $GLOBALS['egw']->accounts->get_type($owner) == 'g') { foreach($GLOBALS['egw']->accounts->member($owner) as $member) { $member = $member['account_id']; if (!$this->bo->check_perms(EGW_ACL_READ,0,$member)) { $no_access_group[$member] = $this->bo->participant_name($member); } } } elseif (!$this->bo->check_perms(EGW_ACL_READ,0,$owner)) { $no_access[$owner] = $this->bo->participant_name($owner); } } if (count($no_access)) { $msg = '

'.lang('Access denied to the calendar of %1 !!!',implode(', ',$no_access))."

\n"; if ($GLOBALS['egw_info']['flags']['currentapp'] == 'home') { return $msg; } $GLOBALS['egw']->common->egw_header(); if ($GLOBALS['egw_info']['flags']['nonavbar']) parse_navbar(); echo $msg; $GLOBALS['egw']->common->egw_footer(); $GLOBALS['egw']->common->egw_exit(); } if (count($no_access_group)) { $this->group_warning = lang('Groupmember(s) %1 not included, because you have no access.',implode(', ',$no_access_group)); } return false; } /** * show the egw-framework plus possible messages ($_GET['msg'] and $this->group_warning from check_owner_access) */ function do_header() { $GLOBALS['egw']->common->egw_header(); if ($_GET['msg']) echo '

'.$this->html->htmlspecialchars($_GET['msg'])."

\n"; if ($this->group_warning) echo '

'.$this->group_warning."

\n"; } /** * 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, NOT used atm. * - sortby: category or user of planner * - view: the actual view, where dialogs should return to or which they refresh * @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['egw']->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, 'save_owner' => 0, 'sortby' => 'category', 'planner_days'=> 0, // full month 'multiple' => 0, 'view' => $this->bo->cal_prefs['defaultcalendar'], ) 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]; } if (substr($this->view,0,8) == 'planner_') { $states['sortby'] = $this->sortby = $this->view == 'planner_cat' ? 'category' : 'user'; $states['view'] = $this->view = 'planner'; } // set the actual view as return_to if ($_GET['menuaction']) { list($app,$class,$func) = explode('.',$_GET['menuaction']); } else // eg. calendar/index.php { $func = $this->view; $class = $this->view == 'listview' ? 'uilist' : 'uiviews'; } if ($class == 'uiviews' || $class == 'uilist') { // if planner_start_with_group is set in the users prefs: switch owner for planner to planner_start_with_group and back if ($this->cal_prefs['planner_start_with_group']) { if (!$states_session && !$_GET['menuaction']) $this->view = ''; // first call to calendar if ($func == 'planner' && $this->view != 'planner' && $this->owner == $this->user) { //echo "

switched for planner to {$this->cal_prefs['planner_start_with_group']}, view was $this->view, func=$func, owner was $this->owner

\n"; $states['save_owner'] = $this->save_owner = $this->owner; $states['owner'] = $this->owner = $this->cal_prefs['planner_start_with_group']; } elseif ($func != 'planner' && $this->view == 'planner' && $this->owner == $this->cal_prefs['planner_start_with_group'] && $this->save_owner) { //echo "

switched back to $this->save_owner, view was $this->view, func=$func, owner was $this->owner

\n"; $states['owner'] = $this->owner = $this->save_owner; $states['save_owner'] = $this->save_owner = 0; } } $this->view = $states['view'] = $func; } $this->view_menuaction = $this->view == 'listview' ? 'calendar.uilist.listview' : 'calendar.uiviews.'.$this->view; $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',True,$set_states,$states_session,$states); // save the states in the session $GLOBALS['egw']->session->appsession('session_data','calendar',$states); } /** * 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(EGW_ACL_READ,$event); $viewable = !$this->bo->printer_friendly && $this->bo->check_perms(EGW_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','recur',lang('recurring event')); } list($first_part) = each($event['participants']); $icons[] = $this->html->image('calendar',count($event['participants']) > 1 || $GLOBALS['egw']->accounts->get_type($first_part) == 'g' ? 'users' : 'single'); } 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')); } foreach($event['participants'] as $participant => $status) { if(is_numeric($participant)) continue; if(isset($this->bo->resources[$participant{0}]) && isset($this->bo->resources[$participant{0}]['icon']) && !$seticon[$participant{0}]) { $seticon[$participant{0}] = true; $icons[] = $this->html->image($this->bo->resources[$participant{0}]['app'], ($this->bo->resources[$participant{0}]['icon'] ? $this->bo->resources[$participant{0}]['icon'] : 'navbar'), lang($this->bo->resources[$participant{0}]['app']), 'width="16px" height="16px"'); } } 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 ); } /** * Generate a link to add an event, incl. the necessary popup * * @param string $content content of the link * @param string $date=null which date should be used as start- and end-date, default null=$this->date * @param int $hour=null which hour should be used for the start, default null=$this->hour * @param int $minute=0 start-minute * @return string the link incl. content */ function add_link($content,$date=null,$hour=null,$minute=0) { $vars = array( 'menuaction'=>'calendar.uiforms.edit', 'date' => $date ? $date : $this->date, ); if (!is_null($hour)) { $vars['hour'] = $hour; $vars['minute'] = $minute; } return $this->html->a_href($content,'/index.php',$vars,' target="_blank" title="'.$this->html->htmlspecialchars(lang('Add')). '" onclick="'.$this->popup('this.href','this.target').'; return false;"'); } /** * returns javascript to open a popup window: window.open(...) * * @param string $link link or this.href * @param string $target='_blank' name of target or this.target * @param int $width=750 width of the window * @param int $height=400 height of the window * @return string javascript (using single quotes) */ function popup($link,$target='_blank',$width=750,$height=410) { return 'egw_openWindowCentered2('.($link == 'this.href' ? $link : "'".$link."'").','. ($target == 'this.target' ? $target : "'".$target."'").",$width,$height,'yes')"; return 'window.open('.($link == 'this.href' ? $link : "'".$link."'").','. ($target == 'this.target' ? $target : "'".$target."'").','. "'dependent=yes,width=$width,height=$height,scrollbars=yes,status=yes')"; } /** * 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 $planner_days_for_view = false; switch($this->view) { case 'month': $planner_days_for_view = 0; break; case 'week': $planner_days_for_view = $this->cal_prefs['days_in_weekview'] == 5 ? 5 : 7; break; case 'day': $planner_days_for_view = 1; break; } // Toolbar with the views $views = ''."\n"; foreach(array( 'add' => array('icon'=>'new','text'=>'add'), 'day' => array('icon'=>'today','text'=>'Today','menuaction' => 'calendar.uiviews.day','date' => $this->bo->date2string($this->bo->now_su)), 'week' => array('icon'=>'week','text'=>'Weekview','menuaction' => 'calendar.uiviews.week'), 'month' => array('icon'=>'month','text'=>'Monthview','menuaction' => 'calendar.uiviews.month'), 'planner' => array('icon'=>'planner','text'=>'Group planner','menuaction' => 'calendar.uiviews.planner','sortby' => $this->sortby), 'list' => array('icon'=>'list','text'=>'Listview','menuaction'=>'calendar.uilist.listview'), ) as $view => $data) { $icon = array_shift($data); $title = array_shift($data); $vars = array_merge($link_vars,$data); $icon = $this->html->image('calendar',$icon,lang($title)); $link = $view == 'add' ? $this->add_link($icon) : $this->html->a_href($icon,'/index.php',$vars); $views .= '\n"; } $views .= "
'.$link."
\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('dayview'), 'value' => 'menuaction=calendar.uiviews.day', 'selected' => $this->view == 'day', ), array( 'text' => lang('weekview with weekend'), 'value' => 'menuaction=calendar.uiviews.week&days=7', 'selected' => $this->view == 'week' && $this->cal_prefs['days_in_weekview'] != 5, ), array( 'text' => lang('weekview without weekend'), 'value' => 'menuaction=calendar.uiviews.week&days=5', 'selected' => $this->view == 'week' && $this->cal_prefs['days_in_weekview'] == 5, ), array( 'text' => lang('monthview'), 'value' => 'menuaction=calendar.uiviews.month', 'selected' => $this->view == 'month', ), array( 'text' => lang('planner by category'), 'value' => 'menuaction=calendar.uiviews.planner&sortby=category'. ($planner_days_for_view !== false ? '&planner_days='.$planner_days_for_view : ''), 'selected' => $this->view == 'planner' && $this->sortby != 'user', ), array( 'text' => lang('planner by user'), 'value' => 'menuaction=calendar.uiviews.planner&sortby=user'. ($planner_days_for_view !== false ? '&planner_days='.$planner_days_for_view : ''), 'selected' => $this->view == 'planner' && $this->sortby == 'user', ), array( 'text' => lang('listview'), 'value' => 'menuaction=calendar.uilist.listview', 'selected' => $this->view == 'listview', ), ) as $data) { $options .= '\n"; } $file[++$n] = $this->_select_box('displayed view','view',$options,$GLOBALS['egw']->link('/index.php')); // Search $blur = addslashes($this->html->htmlspecialchars(lang('Search').'...')); $value = @$_POST['keywords'] ? $this->html->htmlspecialchars($_POST['keywords']) : $blur; $file[++$n] = array( 'text' => $this->html->form('', '','/index.php',array('menuaction'=>'calendar.uilist.listview')), 'no_lang' => True, 'link' => False, ); // Minicalendar $link = array(); foreach(array( 'day' => 'calendar.uiviews.day', 'week' => 'calendar.uiviews.week', 'month' => 'calendar.uiviews.month') as $view => $menuaction) { if ($this->view == 'planner') { switch($view) { case 'day': $link_vars['planner_days'] = 1; break; case 'week': $link_vars['planner_days'] = $this->cal_prefs['days_in_weekview'] == 5 ? 5 : 7; break; case 'month': $link_vars['planner_days'] = 0; break; } $link_vars['menuaction'] = $this->view_menuaction; // stay in the planner } elseif ($this->view == 'listview') { $link_vars['menuaction'] = $this->view_menuaction; // stay in the listview } else { $link_vars['menuaction'] = $menuaction; } unset($link_vars['date']); // gets set in jscal $link[$view] = $l = $GLOBALS['egw']->link('/index.php',$link_vars); } $jscalendar = $GLOBALS['egw']->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->formatted_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 /* NOT used at the moment if(is_numeric($this->owner) && $this->bo->check_perms(EGW_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['egw_info']['server']['deny_user_grants_access']) || !$GLOBALS['egw_info']['server']['deny_user_grants_access'])) { $grants = array(); foreach($this->bo->list_cals() as $grant) { $grants[] = $grant['grantor']; } // exclude non-accounts from the account-selection $accounts = array(); foreach(explode(',',$this->owner) as $owner) { if (is_numeric($owner)) $accounts[] = $owner; } $file[] = array( 'text' => " ". $this->accountsel->selection('owner','uical_select_owner',$accounts,'calendar+',$this->multiple ? 4 : 1,False, ' style="width: '.($this->multiple && $this->common_prefs['account_selection']=='selectbox' ? 185 : 165).'px;"'. ' title="'.lang('select a %1',lang('user')).'" onchange="load_cal(\''. $GLOBALS['egw']->link('/index.php',array( 'menuaction' => $this->view_menuaction, 'date' => $this->date, )).'\',\'uical_select_owner\');"','',$grants), 'no_lang' => True, 'link' => False ); } // Import & Export $file[] = array( 'text' => lang('Export').': '.$this->html->a_href(lang('iCal'),'calendar.uiforms.export',$this->first ? array( 'start' => $this->bo->date2string($this->first), 'end' => $this->bo->date2string($this->last), ) : false), 'no_lang' => True, 'link' => False, ); $file[] = array( 'text' => lang('Import').': '.$this->html->a_href(lang('iCal'),'calendar.uiforms.import'). ' & '.$this->html->a_href(lang('CSV'),'/calendar/csv_import.php'), 'no_lang' => True, 'link' => False, ); /* $print_functions = array( 'calendar.uiviews.day' => 'calendar.pdfcal.day', 'calendar.uiviews.week' => 'calendar.pdfcal.week', ); if (isset($print_functions[$_GET['menuaction']])) { $file[] = array( 'text' => 'pdf-export / print', 'link' => $GLOBALS['egw']->link('/index.php',array( 'menuaction' => $print_functions[$_GET['menuaction']], 'date' => $this->date, )), 'target' => '_blank', ); } */ // 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 !!! $width = 203; echo ''."\n"; $appname = 'calendar'; $menu_title = lang('Calendar Menu'); display_sidebox($appname,$menu_title,$file); echo "\n"; // resources menu hooks foreach ($this->bo->resources as $resource) { if(!is_array($resource['cal_sidebox'])) continue; $menu_title = $resource['cal_sidebox']['menu_title'] ? $resource['cal_sidebox']['menu_title'] : lang($resource['app']); $file = ExecMethod($resource['cal_sidebox']['file'], $this->view_menuaction, $this->date); display_sidebox($appname,$menu_title,$file); } if ($GLOBALS['egw_info']['user']['apps']['preferences']) { $menu_title = lang('Preferences'); $file = Array( 'Calendar preferences'=>$GLOBALS['egw']->link('/index.php','menuaction=preferences.uisettings.index&appname=calendar'), 'Grant Access'=>$GLOBALS['egw']->link('/index.php','menuaction=preferences.uiaclprefs.index&acl_app=calendar'), 'Edit Categories' =>$GLOBALS['egw']->link('/index.php','menuaction=preferences.uicategories.index&cats_app=calendar&cats_level=True&global_cats=True'), ); display_sidebox($appname,$menu_title,$file); } if ($GLOBALS['egw_info']['user']['apps']['admin']) { $menu_title = lang('Administration'); $file = Array( 'Configuration'=>$GLOBALS['egw']->link('/index.php','menuaction=admin.uiconfig.index&appname=calendar'), 'Custom Fields'=>$GLOBALS['egw']->link('/index.php','menuaction=admin.customfields.edit&appname=calendar'), 'Holiday Management'=>$GLOBALS['egw']->link('/index.php','menuaction=calendar.uiholiday.admin'), 'Global Categories' =>$GLOBALS['egw']->link('/index.php','menuaction=admin.uicategories.index&appname=calendar'), ); display_sidebox($appname,$menu_title,$file); } } }