| [ Index ] |
|
Code source de eGroupWare 1.2.106-2 |
1 <?php 2 /**************************************************************************\ 3 * eGroupWare - Calendar - Views and Widgets * 4 * http://www.egroupware.org * 5 * Written and (c) 2004/5 by Ralf Becker <RalfBecker@outdoor-training.de> * 6 * -------------------------------------------- * 7 * This program is free software; you can redistribute it and/or modify it * 8 * under the terms of the GNU General Public License as published by the * 9 * Free Software Foundation; either version 2 of the License, or (at your * 10 * option) any later version. * 11 \**************************************************************************/ 12 13 /* $Id: class.uiviews.inc.php 23119 2006-12-25 12:58:03Z ralfbecker $ */ 14 15 include_once (EGW_INCLUDE_ROOT . '/calendar/inc/class.uical.inc.php'); 16 17 /** 18 * Class to generate the calendar views and the necesary widgets 19 * 20 * @package calendar 21 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> 22 * @copyright (c) 2004/5 by RalfBecker-At-outdoor-training.de 23 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License 24 */ 25 class uiviews extends uical 26 { 27 var $public_functions = array( 28 'day' => True, 29 'day4' => True, 30 'week' => True, 31 'month' => True, 32 'planner' => True, 33 ); 34 35 /** 36 * integer level or string function- or widget-name 37 * 38 * @var mixed $debug 39 */ 40 var $debug=false; 41 42 /** 43 * minimum width for an event 44 * 45 * @var int $eventCol_min_width 46 */ 47 var $eventCol_min_width = 80; 48 49 /** 50 * extra rows above and below the workday 51 * 52 * @var int $extraRows 53 */ 54 var $extraRows = 1; 55 56 var $timeRow_width = 40; 57 58 /** 59 * how many rows per day get displayed, gets set be the timeGridWidget 60 * 61 * @var int $rowsToDisplay 62 */ 63 var $rowsToDisplay; 64 65 /** 66 * height in percent of one row, gets set be the timeGridWidget 67 * 68 * @var int $rowHeight 69 */ 70 var $rowHeight; 71 72 /** 73 * standard params for calling bocal::search for all views, set by the constructor 74 * 75 * @var array $search_params 76 */ 77 var $search_params; 78 79 /** 80 * should we use a time grid, can be set for week- and month-view to false by the cal_pref no_time_grid 81 * 82 * @var boolean $use_time_grid=true 83 */ 84 var $use_time_grid=true; 85 86 /** 87 * Can we display the whole day in a timeGrid of the size of the workday and just scroll to workday start 88 * 89 * @var boolean $scroll_to_wdstart; 90 */ 91 var $scroll_to_wdstart=false; 92 93 /** 94 * Constructor 95 * 96 * @param array $set_states=null to manualy set / change one of the states, default NULL = use $_REQUEST 97 */ 98 function uiviews($set_states=null) 99 { 100 $this->uical(false,$set_states); // call the parent's constructor 101 102 $GLOBALS['egw_info']['flags']['nonavbar'] = False; 103 $app_header = array( 104 'day' => lang('Dayview'), 105 '4day' => lang('Four days view'), 106 'week' => lang('Weekview'), 107 'month' => lang('Monthview'), 108 'planner' => lang('Group planner'), 109 ); 110 $GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps']['calendar']['title']. 111 (isset($app_header[$this->view]) ? ' - '.$app_header[$this->view] : ''). 112 // for a single owner we add it's name to the app-header 113 (count(explode(',',$this->owner)) == 1 ? ': '.$this->bo->participant_name($this->owner) : ''); 114 115 // standard params for calling bocal::search for all views 116 $this->search_params = array( 117 'start' => $this->date, 118 'cat_id' => $this->cat_id, 119 'users' => explode(',',$this->owner), 120 'filter' => $this->filter, 121 'daywise' => True, 122 ); 123 $this->holidays = $this->bo->read_holidays($this->year); 124 125 $this->check_owners_access(); 126 } 127 128 /** 129 * Show the last view or the default one, if no last 130 */ 131 function index() 132 { 133 if (!$this->view) $this->view = 'week'; 134 135 // handle views in other files 136 if (!isset($this->public_functions[$this->view])) 137 { 138 $GLOBALS['egw']->redirect_link('/index.php',array('menuaction'=>$this->view_menuaction)); 139 } 140 // get manual to load the right page 141 $GLOBALS['egw_info']['flags']['params']['manual'] = array('page' => 'ManualCalendar'.ucfirst($this->view)); 142 143 $this->{$this->view}(); 144 } 145 146 /** 147 * Show the calendar on the home page 148 * 149 * @return string with content 150 */ 151 function &home() 152 { 153 // set some stuff for the home-page 154 $this->uiviews(array( 155 'date' => $this->bo->date2string($this->bo->now_su), 156 'cat_id' => 0, 157 'filter' => 'all', 158 'owner' => substr($this->cal_prefs['defaultcalendar'],0,7) == 'planner' && $this->cal_prefs['planner_start_with_group'] ? 159 $this->cal_prefs['planner_start_with_group'] : $this->user, 160 'multiple' => 0, 161 'view' => $this->bo->cal_prefs['defaultcalendar'], 162 )); 163 164 if (($error = $this->check_owners_access())) 165 { 166 return $error; 167 } 168 if ($this->group_warning) 169 { 170 $group_warning = '<p class="redItalic" align="center">'.$this->group_warning."</p>\n"; 171 } 172 switch($this->cal_prefs['defaultcalendar']) 173 { 174 case 'planner_user': 175 case 'planner_cat': 176 case 'planner': 177 return $group_warning.$this->planner(true); 178 179 case 'month': 180 return $group_warning.$this->month(0,true); 181 182 default: 183 case 'week': 184 return $group_warning.$this->week(0,true); 185 186 case 'day': 187 return $group_warning.$this->day(true); 188 } 189 } 190 191 /** 192 * Displays the planner view 193 * 194 * @param boolean $home=false if true return content suitable for home-page 195 */ 196 function &planner($home=false) 197 { 198 if (!$this->planner_days) // planner monthview 199 { 200 if ($this->day < 15) // show one complete month 201 { 202 $this->_week_align_month($this->first,$this->last); 203 $GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang(adodb_date('F',$this->bo->date2ts($this->date))).' '.$this->year; 204 } 205 else // show 2 half month 206 { 207 $this->_week_align_month($this->first,$this->last,15); 208 $GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang(adodb_date('F',$this->first)).' / '.lang(adodb_date('F',$this->last)).' '.$this->year; 209 } 210 } 211 elseif ($this->planner_days >= 5) // weeekview 212 { 213 $this->first = $this->datetime->get_weekday_start($this->year,$this->month,$this->day); 214 $this->last = $this->bo->date2array($this->first); 215 $this->last['day'] += (int) $this->planner_days - 1; 216 $this->last['hour'] = 23; $this->last['minute'] = $this->last['sec'] = 59; 217 unset($this->last['raw']); 218 $this->last = $this->bo->date2ts($this->last); 219 $GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang('Week').' '.adodb_date('W',$this->first).': '.$this->bo->long_date($this->first,$this->last); 220 } 221 else // dayview 222 { 223 $this->first = $this->bo->date2ts($this->date); 224 $this->last = $this->bo->date2array($this->first); 225 $this->last['day'] += (int) $this->planner_days - 1; 226 $this->last['hour'] = 23; $this->last['minute'] = $this->last['sec'] = 59; 227 unset($this->last['raw']); 228 $this->last = $this->bo->date2ts($this->last); 229 $GLOBALS['egw_info']['flags']['app_header'] .= ': '.($this->planner_days == 1 ? lang(date('l',$this->first)).', ' : ''). 230 $this->bo->long_date($this->first,$this->planner_days > 1 ? $this->last : 0); 231 } 232 233 $search_params = $this->search_params; 234 $search_params['daywise'] = false; 235 $search_params['start'] = $this->first; 236 $search_params['end'] = $this->last; 237 $search_params['enum_groups'] = $this->sortby == 'user'; 238 $events = $this->bo->search($search_params); 239 240 if ($this->debug > 0) $this->bo->debug_message('uiviews::planner() date=%1: first=%2, last=%3',False,$this->date,$this->bo->date2string($this->first),$this->bo->date2string($this->last)); 241 242 $content =& $this->plannerWidget($events,$this->first,$this->last,$this->sortby == 'user' ? false : (int) $this->cat_id); 243 244 if (!$home) 245 { 246 $this->do_header(); 247 248 echo $content; 249 } 250 return $content; 251 } 252 253 /** 254 * Displays the monthview or a multiple week-view 255 * 256 * @param int $weeks=0 number of weeks to show, if 0 (default) all weeks of one month are shows 257 * @param boolean $home=false if true return content suitable for home-page 258 */ 259 function &month($weeks=0,$home=false) 260 { 261 if ($this->debug > 0) $this->bo->debug_message('uiviews::month(weeks=%1) date=%2',True,$weeks,$this->date); 262 263 $this->use_time_grid = $this->cal_prefs['use_time_grid'] == ''; // all views 264 265 if ($weeks) 266 { 267 $this->first = $this->datetime->get_weekday_start($this->year,$this->month,$this->day=1); 268 $this->last = $this->first + $weeks * 7 * DAY_s - 1; 269 } 270 else 271 { 272 $this->_week_align_month($this->first,$this->last); 273 } 274 if ($this->debug > 0) $this->bo->debug_message('uiviews::month(%1) date=%2: first=%3, last=%4',False,$weeks,$this->date,$this->bo->date2string($this->first),$this->bo->date2string($this->last)); 275 276 $GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang(adodb_date('F',$this->bo->date2ts($this->date))).' '.$this->year; 277 278 $days =& $this->bo->search(array( 279 'start' => $this->first, 280 'end' => $this->last, 281 )+$this->search_params); 282 283 $content = ''; 284 // we add DAY_s/2 to $this->first (using 12h), to deal with daylight saving changes 285 for ($week_start = $this->first+DAY_s/2; $week_start < $this->last; $week_start += WEEK_s) 286 { 287 $week = array(); 288 for ($i = 0; $i < 7; ++$i) 289 { 290 $day_ymd = $this->bo->date2string($week_start+$i*DAY_s); 291 $week[$day_ymd] = array_shift($days); 292 } 293 $week_view = array( 294 'menuaction' => 'calendar.uiviews.week', 295 'date' => $this->bo->date2string($week_start), 296 ); 297 $title = lang('Wk').' '.adodb_date('W',$week_start); 298 $title = $this->html->a_href($title,$week_view,'',' title="'.lang('Weekview').'"'); 299 300 $content .= $this->timeGridWidget($week,60,200,'',$title,0,$week_start+WEEK_s >= $this->last); 301 } 302 if (!$home) 303 { 304 $this->do_header(); 305 306 echo $content; 307 } 308 return $content; 309 } 310 311 /** 312 * get start and end of a month aligned to full weeks 313 * 314 * @param int &$first timestamp 0h of first day of week containing the first of the current month 315 * @param int &$last timestamp 23:59:59 of last day of week containg the last day of the current month 316 * @param int $day=1 should the alignment be based on the 1. of the month or an other date, eg. the 15. 317 */ 318 function _week_align_month(&$first,&$last,$day=1) 319 { 320 $first = $this->datetime->get_weekday_start($this->year,$this->month,$this->day=$day); 321 if ($day == 1) 322 { 323 $last = $this->datetime->get_weekday_start($this->year,$this->month, 324 $this->datetime->days_in_month($this->month,$this->year)); 325 } 326 else 327 { 328 $last = $this->datetime->get_weekday_start($this->year,$this->month+1,$day); 329 } 330 // now we need to calculate the end of the last day of that week 331 // as simple $last += WEEK_s - 1; does NOT work, if daylight saving changes in that week!!! 332 $last = $this->bo->date2array($last); 333 $last['day'] += 6; 334 $last['hour'] = 23; 335 $last['min'] = $last['sec'] = 59; 336 unset($last['raw']); // otherwise date2ts does not calc raw new, but uses it 337 $last = $this->bo->date2ts($last); 338 } 339 340 /** 341 * Four days view, everythings done by the week-view code ... 342 * 343 * @param boolean $home=false if true return content suitable for home-page 344 * @return string 345 */ 346 function day4($home=false) 347 { 348 return $this->week(4,$home); 349 } 350 351 /** 352 * Displays the weekview, with 5 or 7 days 353 * 354 * @param int $days=0 number of days to show, if 0 (default) the value from the URL or the prefs is used 355 * @param boolean $home=false if true return content suitable for home-page 356 */ 357 function week($days=0,$home=false) 358 { 359 $this->use_time_grid = $days != 4 && !in_array($this->cal_prefs['use_time_grid'],array('day','day4')) || 360 $days == 4 && $this->cal_prefs['use_time_grid'] != 'day'; 361 362 if (!$days) 363 { 364 $days = isset($_GET['days']) ? $_GET['days'] : $this->cal_prefs['days_in_weekview']; 365 if ($days != 5) $days = 7; 366 if ($days != $this->cal_prefs['days_in_weekview']) // save the preference 367 { 368 $GLOBALS['egw']->preferences->add('calendar','days_in_weekview',$days); 369 $GLOBALS['egw']->preferences->save_repository(); 370 $this->cal_prefs['days_in_weekview'] = $days; 371 } 372 } 373 if ($this->debug > 0) $this->bo->debug_message('uiviews::week(days=%1) date=%2',True,$days,$this->date); 374 375 if ($days == 4) // next 4 days view 376 { 377 $wd_start = $this->first = $this->bo->date2ts($this->date); 378 $this->last = $this->first + $days * DAY_s - 1; 379 $GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang('Four days view').' '.$this->bo->long_date($this->first,$this->last); 380 } 381 else 382 { 383 $wd_start = $this->first = $this->datetime->get_weekday_start($this->year,$this->month,$this->day); 384 if ($days == 5) // no weekend-days 385 { 386 switch($this->cal_prefs['weekdaystarts']) 387 { 388 case 'Saturday': 389 $this->first += DAY_s; 390 // fall through 391 case 'Sunday': 392 $this->first += DAY_s; 393 break; 394 } 395 } 396 $this->last = $this->first + $days * DAY_s - 1; 397 $GLOBALS['egw_info']['flags']['app_header'] .= ': '.lang('Week').' '.adodb_date('W',$this->first).': '.$this->bo->long_date($this->first,$this->last); 398 } 399 //echo "<p>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',$this->first)."</p>\n"; 400 401 $search_params = array( 402 'start' => $this->first, 403 'end' => $this->last, 404 ) + $this->search_params; 405 406 $users = $this->search_params['users']; 407 if (!is_array($users)) $users = array($users); 408 409 if (count($users) == 1 || count($users) > 3) // for more then 3 users, show all in one row 410 { 411 $content =& $this->timeGridWidget($this->bo->search($search_params),$this->cal_prefs['interval']); 412 } 413 else 414 { 415 $content = ''; 416 foreach($this->_get_planner_users(false) as $uid => $label) 417 { 418 $search_params['users'] = $uid; 419 $content .= '<b>'.$label."</b>\n"; 420 $content .= $this->timeGridWidget($this->bo->search($search_params), 421 count($users) * $this->cal_prefs['interval'],400 / count($users),'','',$uid); 422 } 423 } 424 if (!$home) 425 { 426 $this->do_header(); 427 428 echo $content; 429 } 430 return $content; 431 } 432 433 /** 434 * Displays the dayview 435 * 436 * @param boolean $home=false if true return content suitable for home-page 437 */ 438 function &day($home=false) 439 { 440 if ($this->debug > 0) $this->bo->debug_message('uiviews::day() date=%1',True,$this->date); 441 442 $this->last = $this->first = $this->bo->date2ts((string)$this->date); 443 $GLOBALS['egw_info']['flags']['app_header'] .= ': '.$this->bo->long_date($this->first,0,false,true); 444 445 $this->use_time_grid = true; // day-view always uses a time-grid, independent what's set in the prefs! 446 447 $this->search_params['end'] = $this->last = $this->first+DAY_s-1; 448 449 if (!$home) 450 { 451 $this->do_header(); 452 453 $users = $this->search_params['users']; 454 if (!is_array($users)) $users = array($users); 455 456 // for more then 5 users, show all in one row 457 if (count($users) == 1 || count($users) > 5) 458 { 459 $dayEvents =& $this->bo->search($this->search_params); 460 $owner = 0; 461 } 462 else 463 { 464 $dayEvents = $owner = array(); 465 $search_params = $this->search_params; 466 foreach($this->_get_planner_users(false) as $uid => $label) 467 { 468 $search_params['users'] = $uid; 469 list(,$dayEvents['<b>'.$label.'</b>']) = each($this->bo->search($search_params)); 470 $owner[] = $uid; 471 } 472 } 473 $cols = array(); 474 $cols[0] =& $this->timeGridWidget($dayEvents,$this->cal_prefs['interval'],450,'','',$owner); 475 476 // only show todo's for a single user 477 if (count($users) == 1 && ($todos = $this->get_todos($todo_label)) !== false) 478 { 479 if ($GLOBALS['egw_info']['user']['apps']['infolog']) 480 { 481 foreach(array('task','phone','note') as $type) 482 { 483 $todo_label .= ' '.$this->html->a_href( $this->html->image('infolog',$type,lang('Add')),'infolog.uiinfolog.edit',array( 484 'type' => $type, 485 'start_time' => $ts, 486 ),' target="_blank" onclick="window.open(this.href,this.target,\'dependent=yes,width=750,height=590,scrollbars=yes,status=yes\'); return false;"'); 487 } 488 } 489 $cols[1] = $this->html->div( 490 $this->html->div($todo_label,'','calDayTodosHeader th')."\n". 491 $this->html->div($todos,'','calDayTodosTable'),'','calDayTodos'); 492 $cols['.1'] = 'width=30%'; 493 echo $this->html->table(array( 494 0 => $cols, 495 '.0' => 'valign="top"' 496 ),'class="calDayView"'); 497 } 498 else 499 { 500 echo $cols[0]; 501 } 502 } 503 else 504 { 505 return $this->timeGridWidget($this->bo->search($this->search_params),$this->cal_prefs['interval'],300); 506 } 507 } 508 509 /** 510 * Query the open ToDo's via a hook from InfoLog or any other 'calendar_include_todos' provider 511 * 512 * @param array/string $todo_label label for the todo-box or array with 2 values: the label and a boolean show_all 513 * On return $todo_label contains the label for the todo-box 514 * @return string/boolean html with a table of open todo's or false if no hook availible 515 */ 516 function get_todos(&$todo_label) 517 { 518 $todos_from_hook = $GLOBALS['egw']->hooks->process(array( 519 'location' => 'calendar_include_todos', 520 'year' => $this->year, 521 'month' => $this->month, 522 'day' => $this->day, 523 'owner' => $this->owner // num. id of the user, not necessary current user 524 )); 525 526 if(is_array($todo_label)) 527 { 528 list($label,$showall)=$todo_label; 529 } 530 else 531 { 532 $label=$todo_label; 533 $showall=true; 534 } 535 $maxshow = (int)$GLOBALS['egw_info']['user']['preferences']['infolog']['mainscreen_maxshow']; 536 if($maxshow <= 0) 537 { 538 $maxshow=10; 539 } 540 //print_debug("get_todos(): label=$label; showall=$showall; max=$maxshow"); 541 542 $content = $todo_label = ''; 543 if (is_array($todos_from_hook) && count($todos_from_hook)) 544 { 545 foreach($todos_from_hook as $todos) 546 { 547 $i = 0; 548 if (is_array($todos)) 549 { 550 $todo_label = !empty($label) ? $label : lang("open ToDo's:"); 551 552 foreach($todos as $todo) 553 { 554 if(!$showall && ($i++ > $maxshow)) 555 { 556 break; 557 } 558 $icons = ''; 559 foreach($todo['icons'] as $name => $app) 560 { 561 $icons .= ($icons?' ':'').$GLOBALS['egw']->html->image($app,$name,lang($name),'border="0" width="15" height="15"'); 562 } 563 $class = $class == 'row_on' ? 'row_off' : 'row_on'; 564 565 $content .= " <tr class=\"$class\">\n <td valign=\"top\" width=\"15%\" nowrap>". 566 ($this->bo->printer_friendly?$icons:$GLOBALS['egw']->html->a_href($icons,$todo['view'])). 567 "</td>\n <td>".($this->printer_friendly?$todo['title']: 568 $GLOBALS['egw']->html->a_href($todo['title'],$todo['view']))."</td>\n </tr>\n"; 569 /** 570 * ToDo: add delete and closing action 571 */ 572 } 573 } 574 } 575 } 576 if (!empty($content)) 577 { 578 return "<table border=\"0\" width=\"100%\">\n$content</table>\n"; 579 } 580 return $todo_label ? '' : false; 581 } 582 583 /** 584 * Calculates the vertical position based on the time 585 * 586 * workday start- and end-time, is taken into account, as well as timeGrids px_m - minutes per pixel param 587 * 588 * @param int $time in minutes 589 * @return float position in percent 590 */ 591 function time2pos($time) 592 { 593 if ($this->scroll_to_wdstart) // we display the complete day - thought only workday is visible without scrolling 594 { 595 return $this->rowHeight * (1 + $this->extraRows + $time/$this->granularity_m); 596 } 597 // time before workday => condensed in the first $this->extraRows rows 598 if ($this->wd_start > 0 && $time < $this->wd_start) 599 { 600 $pos = (1 + $this->extraRows * $time / $this->wd_start) * $this->rowHeight; // 1 for the header 601 } 602 // time after workday => condensed in the last row 603 elseif ($this->wd_end < 24*60 && $time > $this->wd_end+1*$this->granularity_m) 604 { 605 $pos = 100 - ($this->extraRows * $this->rowHeight * (1 - ($time - $this->wd_end) / (24*60 - $this->wd_end))); 606 } 607 // time during the workday => 2. row on (= + granularity) 608 else 609 { 610 $pos = $this->rowHeight * (1+$this->extraRows+($time-$this->wd_start)/$this->granularity_m); 611 } 612 $pos = round($pos,1); 613 614 if ($this->debug > 3) $this->bo->debug_message('uiviews::time2pos(%1)=%2',False,$time,$pos); 615 616 return $pos; 617 } 618 619 /** 620 * Calculates the height of a differenc between 2 times 621 * 622 * workday start- and end-time, is taken into account, as well as timeGrids px_m - minutes per pixel param 623 * 624 * @param int $start time in minutes 625 * @param int $end time in minutes 626 * @param int $minimum=0 minimum height 627 * @return float height in percent 628 */ 629 function times2height($start,$end,$minimum=0) 630 { 631 $minimum = $this->rowHeight; 632 $height = $this->time2pos($end) - $this->time2pos($start); 633 634 if ($this->debug > 3) $this->bo->debug_message('uiviews::times2height(%1,%2,min=%3)=%4',False,$start,$end,$minimum,$height); 635 636 return $height >= $minimum ? $height : $minimum; 637 } 638 639 /** 640 * Creates a grid with rows for the time, columns for (multiple) days containing events 641 * 642 * Uses the dayColWidget to display each day. 643 * 644 * @param $daysEvents array with subarrays of events for each day to show, day as YYYYMMDD as key 645 * @param int $granularity_m=30 granularity in minutes of the rows 646 * @param int $height=400 height of the widget 647 * @param string $indent='' string for correct indention 648 * @param string $title='' title of the time-grid 649 * @param int/array $owner=0 owner of the calendar (default 0 = $this->owner) or array with owner for each column 650 * @param boolean $last=true last timeGrid displayed, default true 651 */ 652 function &timeGridWidget($daysEvents,$granularity_m=30,$height=400,$indent='',$title='',$owner=0,$last=true) 653 { 654 if ($this->debug > 1 || $this->debug==='timeGridWidget') $this->bo->debug_message('uiviews::timeGridWidget(events=%1,granularity_m=%2,height=%3,,title=%4)',True,$daysEvents,$granularity_m,$height,$title); 655 656 // determine if the browser supports scrollIntoView: IE4+, firefox1.0+ and safari2.0+ does 657 // then show all hours in a div of the size of the workday and scroll to the workday start 658 // still disabled, as things need to be re-aranged first, to that the column headers are not scrolled 659 $this->scroll_to_wdstart = false;/*$this->use_time_grid && ($this->html->user_agent == 'msie' || 660 $this->html->user_agent == 'mozilla' && $this->html->ua_version >= 5.0 || 661 $this->html->user_agent == 'safari' && $this->html->ua_version >= 2.0);*/ 662 663 if ($this->scroll_to_wdstart) 664 { 665 $this->extraRows = 0; // no extra rows necessary 666 $overflow = 'overflow: scroll;'; 667 } 668 $this->granularity_m = $granularity_m; 669 $this->display_start = $this->wd_start - ($this->extraRows * $this->granularity_m); 670 $this->display_end = $this->wd_end + ($this->extraRows * $this->granularity_m); 671 672 if (!$this->wd_end) $this->wd_end = 1440; 673 $totalDisplayMinutes = $this->wd_end - $this->wd_start; 674 $this->rowsToDisplay = ($totalDisplayMinutes/$granularity_m)+2+2*$this->extraRows; 675 $this->rowHeight = round(100/$this->rowsToDisplay,1); 676 677 // ensure a minimum height of each row 678 if ($height < ($this->rowsToDisplay+1) * 12) 679 { 680 $height = ($this->rowsToDisplay+1) * 12; 681 } 682 $html = $indent.'<div class="calTimeGrid" style="height: '.$height.'px;'.$overflow.'">'."\n"; 683 684 $html .= $indent."\t".'<div class="calGridHeader" style="height: '. 685 $this->rowHeight.'%;">'.$title."</div>\n"; 686 687 if ($this->use_time_grid) 688 { 689 $off = false; // Off-row means a different bgcolor 690 $add_links = count($daysEvents) == 1; 691 692 // the hour rows 693 for($t = $this->scroll_to_wdstart ? 0 : $this->wd_start,$i = 1+$this->extraRows; 694 $t <= $this->wd_end || $this->scroll_to_wdstart && $t < 24*60; 695 $t += $this->granularity_m,++$i) 696 { 697 $set_id = ''; 698 if ($t == $this->wd_start) 699 { 700 list($id) = @each($daysEvents); 701 $id = 'wd_start_'.$id; 702 $set_id = ' id="'.$id.'"'; 703 } 704 $html .= $indent."\t".'<div'.$set_id.' class="calTimeRow'.($off ? 'Off row_off' : ' row_on'). 705 '" style="height: '.$this->rowHeight.'%; top:'. $i*$this->rowHeight .'%;">'."\n"; 706 // show time for full hours, allways for 45min interval and at least on every 3 row 707 $time = ''; 708 static $show = array( 709 5 => array(0,15,30,45), 710 10 => array(0,30), 711 15 => array(0,30), 712 45 => array(0,15,30,45), 713 ); 714 if (!isset($show[$this->granularity_m]) ? $t % 60 == 0 : in_array($t % 60,$show[$this->granularity_m])) 715 { 716 $time = $GLOBALS['egw']->common->formattime(sprintf('%02d',$t/60),sprintf('%02d',$t%60)); 717 } 718 if ($add_links) $time = $this->add_link($time,$this->date,(int) ($t/60),$t%60); 719 $html .= $indent."\t\t".'<div class="calTimeRowTime">'.$time."</div>\n"; 720 $html .= $indent."\t</div>\n"; // calTimeRow 721 $off = !$off; 722 } 723 } 724 if (is_array($daysEvents) && count($daysEvents)) 725 { 726 $numberOfDays = count($daysEvents); 727 $dayColWidth = 100/$numberOfDays; 728 729 $dayCols_width = $width - $this->timeRow_width - 1; 730 731 $html .= $indent."\t".'<div id="calDayCols" class="calDayCols'. 732 ($this->use_time_grid ? ($this->bo->common_prefs['timeformat'] == 12 ? '12h' : '') : 'NoTime').'">'."\n"; 733 734 if ($this->html->user_agent == 'msie') // necessary IE hack - stupid thing ... 735 { 736 // Lars Kneschke 2005-08-28 737 // why do we use a div in a div which has the same height and width??? 738 // To make IE6 happy!!! Without the second div you can't use 739 // style="left: 50px; right: 0px;" 740 //$html .= '<div style="width=100%; height: 100%;">'."\n"; 741 742 // Ralf Becker 2006-06-19 743 // Lars original typo "width=100%; height: 100%;" is important ;-) 744 // means you width: 100% does NOT work, you need no width! 745 $html .= '<div style="height: 100%;">'."\n"; 746 } 747 $dayCol_width = $dayCols_width / count($daysEvents); 748 $n = 0; 749 foreach($daysEvents as $day => $events) 750 { 751 $short_title = count($daysEvents) > 1; 752 $col_owner = $owner; 753 if (!is_numeric($day)) 754 { 755 $short_title = $day; 756 $day = $this->date; 757 $col_owner = $owner[$n]; 758 } 759 $html .= $this->dayColWidget($day,$events,$n*$dayColWidth, 760 $dayColWidth,$indent."\t\t",$short_title,++$on_off & 1,$col_owner); 761 ++$n; 762 } 763 if ($this->html->user_agent == 'msie') $html .= "</div>\n"; 764 765 $html .= $indent."\t</div>\n"; // calDayCols 766 } 767 $html .= $indent."</div>\n"; // calTimeGrid 768 769 if ($this->scroll_to_wdstart) 770 { 771 $html .= "<script>\n\tdocument.getElementById('$id').scrollIntoView();\n"; 772 if ($last) // last timeGrid --> scroll whole document back up 773 { 774 $html .= "\tdocument.getElementById('divMain').scrollIntoView();\n"; 775 } 776 $html .= "</script>\n"; 777 } 778 return $html; 779 } 780 781 /** 782 * Creates (if necessary multiple) columns for the events of a day 783 * 784 * Uses the eventColWidget to display each column. 785 * 786 * @param string/int $day_ymd date as Ymd 787 * @param array $events of events to show 788 * @param int $left start of the widget 789 * @param int $width width of the widget 790 * @param string $indent string for correct indention 791 * @param boolean/string $short_title=True should we add a label (weekday, day) with link to the day-view above each day or string with title 792 * @param boolean $on_off=false start with row_on or row_off, default false=row_off 793 * @param int $owner=0 if != 0 owner to add to the add-event link 794 */ 795 function dayColWidget($day_ymd,$events,$left,$width,$indent,$short_title=True,$on_off=False,$owner=0) 796 { 797 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); 798 799 $day_start = $this->bo->date2ts((string)$day_ymd); 800 801 // if daylight saving is switched on or off, correct $day_start 802 // gives correct times after 2am, times between 0am and 2am are wrong 803 if(($daylight_diff = $day_start + 12*HOUR_s - ($this->bo->date2ts($day_ymd."T120000")))) 804 { 805 $day_start -= $daylight_diff; 806 } 807 // sorting the event into columns with none-overlapping events, the events are already sorted by start-time 808 $eventCols = $col_ends = array(); 809 foreach($events as $event) 810 { 811 $event['multiday'] = False; 812 $event['start_m'] = ($event['start'] - $day_start) / 60; 813 if ($event['start_m'] < 0) 814 { 815 $event['start_m'] = 0; 816 $event['multiday'] = True; 817 } 818 $event['end_m'] = ($event['end'] - $day_start) / 60; 819 if ($event['end_m'] >= 24*60) 820 { 821 $event['end_m'] = 24*60-1; 822 $event['multiday'] = True; 823 } 824 if ($this->use_time_grid) 825 { 826 for($c = 0; $event['start_m'] < $col_ends[$c]; ++$c); 827 $col_ends[$c] = $event['end_m']; 828 } 829 else 830 { 831 $c = 0; // without grid we only use one column 832 } 833 $eventCols[$c][] = $event; 834 } 835 836 $html = $indent.'<div id="calColumn'.$this->calColumnCounter++.'" class="calDayCol" style="left: '.$left. 837 '%; width: '.$width.'%;">'."\n"; 838 839 // Creation of the header-column with date, evtl. holiday-names and a matching background-color 840 $ts = $this->bo->date2ts((string)$day_ymd); 841 $title = !is_bool($short_title) ? $short_title : 842 ($short_title ? lang(adodb_date('l',$ts)).' '.adodb_date('d.',$ts) : $this->bo->long_date($ts,0,false,true)); 843 $day_view = array( 844 'menuaction' => 'calendar.uiviews.day', 845 'date' => $day_ymd, 846 ); 847 if ($short_title === true) 848 { 849 $title = $this->html->a_href($title,$day_view,'', 850 !isset($this->holidays[$day_ymd])?' title="'.lang('Dayview').'"':''); 851 } 852 elseif ($short_title === false) 853 { 854 // add arrows to go to the previous and next day (dayview only) 855 $day_view['date'] = $this->bo->date2string($ts -= 12*HOUR_s); 856 $title = $this->html->a_href($this->html->image('phpgwapi','left',$this->bo->long_date($ts)),$day_view).' '.$title; 857 $day_view['date'] = $this->bo->date2string($ts += 48*HOUR_s); 858 $title .= ' '.$this->html->a_href($this->html->image('phpgwapi','right',$this->bo->long_date($ts)),$day_view); 859 } 860 $this->_day_class_holiday($day_ymd,$class,$holidays); 861 // the weekday and date 862 $html .= $indent."\t".'<div style="height: '. $this->rowHeight .'%;" class="calDayColHeader '.$class.'"'.($holidays ? ' title="'.$holidays.'"':'').'>'. 863 $title.(!$short_title && $holidays ? ': '.$holidays : '')."</div>\n"; 864 865 if ($this->use_time_grid) 866 { 867 // adding divs to click on for each row / time-span 868 for($t = $this->scroll_to_wdstart ? 0 : $this->wd_start,$i = 1+$this->extraRows; 869 $t <= $this->wd_end || $this->scroll_to_wdstart && $t < 24*60; 870 $t += $this->granularity_m,++$i) 871 { 872 $linkData = array( 873 'menuaction' =>'calendar.uiforms.edit', 874 'date' => $day_ymd, 875 'hour' => floor($t / 60), 876 'minute' => floor($t % 60), 877 ); 878 if ($owner) $linkData['owner'] = $owner; 879 880 $html .= $indent."\t".'<div style="height:'. $this->rowHeight .'%; top: '. $i*$this->rowHeight . 881 '%;" class="calAddEvent" onclick="'.$this->popup($GLOBALS['egw']->link('/index.php',$linkData)).';return false;"></div>'."\n"; 882 } 883 } 884 // displaying all event columns of the day 885 foreach($eventCols as $n => $eventCol) 886 { 887 $html .= $this->eventColWidget($eventCol,!$n ? 0 : 60-10*(count($eventCols)-$n), 888 count($eventCols) == 1 ? 100 : (!$n ? 80 : 50),$indent."\t"); 889 } 890 $html .= $indent."</div>\n"; // calDayCol 891 892 return $html; 893 } 894 895 /** 896 * get the CSS class and holidays for a given day 897 * 898 * @param string $day_ymd date 899 * @param string &$class class to use 900 * @param string &$holidays commaseparted holidays or empty if none 901 * @param boolean $only_weekend=false show only the weekend in header-color, otherwise every second days is shown too 902 */ 903 function _day_class_holiday($day_ymd,&$class,&$holidays,$only_weekend=false) 904 { 905 $class = $holidays = ''; 906 $bday = false; 907 if (isset($this->holidays[$day_ymd])) 908 { 909 foreach($this->holidays[$day_ymd] as $holiday) 910 { 911 if (isset($holiday['birthyear'])) 912 { 913 $bday = true; 914 } 915 else 916 { 917 $class = 'calHoliday'; 918 } 919 $holidays[] = $holiday['name']; 920 } 921 $holidays = implode(', ',$holidays); 922 } 923 if (!$class) 924 { 925 if ($day_ymd == $this->bo->date2string($this->bo->now_su)) 926 { 927 $class = 'calToday'; 928 } 929 else 930 { 931 $day = (int) date('w',$this->bo->date2ts((string) $day_ymd)); 932 933 if ($only_weekend) 934 { 935 $class = $day == 0 || $day == 6 ? 'th' : 'row_off'; 936 } 937 else 938 { 939 $class = $day & 1 ? 'row_on' : 'th'; 940 } 941 } 942 } 943 if ($bday) $class .= ' calBirthday'; 944 } 945 946 /** 947 * Creates colunm for non-overlaping (!) events 948 * 949 * Uses the eventWidget to display each event. 950 * 951 * @param array $events of events to show 952 * @param int $left start of the widget 953 * @param int $width width of the widget 954 * @param string $indent string for correct indention 955 */ 956 function eventColWidget($events,$left,$width,$indent) 957 { 958 if ($this->debug > 1 || $this->debug==='eventColWidget') $this->bo->debug_message('uiviews::eventColWidget(%1,left=%2,width=%3,)',False,$events,$left,$width); 959 960 $html = $indent.'<div class="calEventCol" style="left: '.$left.'%; width:'.$width.'%;'. 961 (!$this->use_time_grid ? ' top: '.$this->rowHeight.'%;' : '').'">'."\n"; 962 foreach($events as $event) 963 { 964 $html .= $this->eventWidget($event,$width,$indent."\t"); 965 } 966 $html .= $indent."</div>\n"; 967 968 return $html; 969 } 970 971 /** 972 * Shows one event 973 * 974 * The display of the event and it's tooltip is done via the event_widget.tpl template 975 * 976 * @param $event array with the data of event to show 977 * @param $width int width of the widget 978 * @param string $indent string for correct indention 979 * @param boolean $return_array=false should an array with keys(tooltip,popup,html) be returned or the complete widget as string 980 * @param string $block='event_widget' template used the render the widget 981 * @return string/array 982 */ 983 function eventWidget($event,$width,$indent,$return_array=false,$block='event_widget') 984 { 985 if ($this->debug > 1 || $this->debug==='eventWidget') $this->bo->debug_message('uiviews::eventWidget(%1,width=%2)',False,$event,$width); 986 987 static $tpl = False; 988 if (!$tpl) 989 { 990 $tpl = $GLOBALS['egw']->template; 991 $tpl->set_root($GLOBALS['egw']->common->get_tpl_dir('calendar')); 992 $tpl->set_file('event_widget_t','event_widget.tpl'); 993 $tpl->set_block('event_widget_t','event_widget'); 994 $tpl->set_block('event_widget_t','event_tooltip'); 995 $tpl->set_block('event_widget_t','planner_event'); 996 } 997 if (($return_array || $event['start_m'] == 0) && $event['end_m'] >= 24*60-1) 998 { 999 if ($return_array && $event['end_m'] > 24*60) 1000 { 1001 $timespan = $this->bo->format_date($event['start'],false).' - '.$this->bo->format_date($event['end']); 1002 } 1003 else 1004 { 1005 $timespan = lang('all day'); 1006 } 1007 } 1008 else 1009 { 1010 $timespan = $this->bo->timespan($event['start_m'],$event['end_m']); 1011 } 1012 $is_private = !$this->bo->check_perms(EGW_ACL_READ,$event); 1013 1014 $icons = !$is_private ? $this->event_icons($event) : array($this->html->image('calendar','private',lang('private'))); 1015 $cats = $this->bo->categories($event['category'],$color); 1016 1017 // these values control varius aspects of the geometry of the eventWidget 1018 $small_trigger_width = 120 + 20*count($icons); 1019 $corner_radius=$width > $small_trigger_width ? 10 : 5; 1020 $header_height=$width > $small_trigger_width ? 19 : 12; // multi_3 icon has a height of 19=16+2*1padding+1border ! 1021 if (!$return_array) $height = $this->times2height($event['start_m'],$event['end_m'],$header_height); 1022 //$body_height = max(0,$height - $header_height - $corner_radius); 1023 $border=1; 1024 $headerbgcolor = $color ? $color : '#808080'; 1025 // the body-colors (gradient) are calculated from the headercolor, which depends on the cat of an event 1026 $bodybgcolor1 = $this->brighter($headerbgcolor,$headerbgcolor == '#808080' ? 100 : 170); 1027 $bodybgcolor2 = $this->brighter($headerbgcolor,220); 1028 1029 // seperate each participant types 1030 $part_array = array(); 1031 foreach($this->bo->participants($event) as $part_key => $participant) 1032 { 1033 if(is_numeric($part_key)) 1034 { 1035 $part_array[lang('Participants')][$part_key] = $participant; 1036 } 1037 elseif(isset($this->bo->resources[$part_key{0}])) 1038 { 1039 $part_array[((isset($this->bo->resources[$part_key{0}]['participants_header'])) ? $this->bo->resources[$part_key{0}]['participants_header'] : lang($this->bo->resources[$part_key{0}]['app']))][$part_key] = $participant; 1040 } 1041 } 1042 foreach($part_array as $part_group => $participant) 1043 { 1044 $participants .= $this->add_nonempty($participant,$part_group,True); 1045 } 1046 // as we only deal with percentual widht, we consider only the full dayview (1 colum) as NOT small 1047 $small = $this->view != 'day' || $width < 50; 1048 // $small = $width <= $small_trigger_width 1049 1050 $small_height = $this->use_time_grid && ( $event['end_m']-$event['start_m'] < 2*$this->granularity_m || 1051 $event['end_m'] <= $this->wd_start || $event['start_m'] >= $this->wd_end); 1052 1053 $tpl->set_var(array( 1054 // event-content, some of it displays only if it really has content or is needed 1055 'header_icons' => $small ? '' : implode("",$icons), 1056 'body_icons' => $small ? implode("\n",$icons) : '', 1057 'icons' => implode('',$icons), 1058 'timespan' => $timespan, 1059 'title' => ($title = !$is_private ? $this->html->htmlspecialchars($event['title']) : lang('private')), 1060 'header' => $small_height ? $title : $timespan, 1061 'description' => !$is_private ? nl2br($this->html->htmlspecialchars($event['description'])) : '', 1062 'location' => !$is_private ? $this->add_nonempty($event['location'],lang('Location')) : '', 1063 'participants' => $participants, 1064 'times' => !$event['multiday'] ? $this->add_nonempty($this->bo->timespan($event['start_m'],$event['end_m'],true),lang('Time')) : 1065 $this->add_nonempty($this->bo->format_date($event['start']),lang('Start')). 1066 $this->add_nonempty($this->bo->format_date($event['end']),lang('End')), 1067 'multidaytimes' => !$event['multiday'] ? '' : 1068 $this->add_nonempty($this->bo->format_date($event['start']),lang('Start')). 1069 $this->add_nonempty($this->bo->format_date($event['end']),lang('End')), 1070 'category' => !$is_private ? $this->add_nonempty($cats,lang('Category')) : '', 1071 // the tooltip is based on the content of the actual widget, this way it takes no extra bandwidth/volum 1072 // 'tooltip' => $this->html->tooltip(False,False,array('BorderWidth'=>0,'Padding'=>0)), 1073 // various aspects of the geometry or style 1074 'corner_radius' => $corner_radius.'px', 1075 'header_height' => $header_height.'px', 1076 //'body_height' => $body_height.'px', 1077 'height' => $height, 1078 'width' => ($width-20).'px', 1079 'border' => $border, 1080 'bordercolor' => $headerbgcolor, 1081 'headerbgcolor' => $headerbgcolor, 1082 'bodybackground' => ($background = 'url('.$GLOBALS['egw_info']['server']['webserver_url']. 1083 '/calendar/inc/gradient.php?color1='.urlencode($bodybgcolor1).'&color2='.urlencode($bodybgcolor2). 1084 '&width='.$width.') repeat-y '.$bodybgcolor2), 1085 'Small' => $small ? 'Small' : '', // to use in css class-names 1086 'indent' => $indent."\t", 1087 )); 1088 /* not used at the moment 1089 foreach(array( 1090 'upper_left'=>array('width'=>-$corner_radius,'height'=>$header_height,'border'=>0,'bgcolor'=>$headerbgcolor), 1091 'upper_right'=>array('width'=>$corner_radius,'height'=>$header_height,'border'=>0,'bgcolor'=>$headerbgcolor), 1092 'lower_left'=>array('width'=>-$corner_radius,'height'=>-$corner_radius,'border'=>$border,'color'=>$headerbgcolor,'bgcolor'=>$bodybgcolor1), 1093 'lower_right'=>array('width'=>$corner_radius,'height'=>-$corner_radius,'border'=>$border,'color'=>$headerbgcolor,'bgcolor'=>$bodybgcolor2), 1094 ) as $name => $data) 1095 { 1096 $tpl->set_var($name.'_corner',$GLOBALS['egw_info']['server']['webserver_url']. 1097 '/calendar/inc/round_corners.php?width='.$data['width'].'&height='.$data['height']. 1098 '&bgcolor='.urlencode($data['bgcolor']). 1099 (isset($data['color']) ? '&color='.urlencode($data['color']) : ''). 1100 (isset($data['border']) ? '&border='.urlencode($data['border']) : '')); 1101 } 1102 */ 1103 $tooltip = $tpl->fp('tooltip','event_tooltip'); 1104 $tpl->set_var('tooltip',$this->html->tooltip($tooltip,False,array('BorderWidth'=>0,'Padding'=>0))); 1105 $html = $tpl->fp('out',$block); 1106 1107 $view_link = $GLOBALS['egw']->link('/index.php',array('menuaction'=>'calendar.uiforms.view','cal_id'=>$event['id'],'date'=>$this->bo->date2string($event['start']))); 1108 $popup = $is_private ? '' : ' onclick="'.$this->popup($view_link).'; return false;"'; 1109 1110 if ($return_array) 1111 { 1112 return array( 1113 'tooltip' => $tooltip, 1114 'popup' => $popup, 1115 'html' => $html, 1116 'private' => $is_private, 1117 'color' => $color, 1118 ); 1119 } 1120 $ie_fix = ''; 1121 if ($this->html->user_agent == 'msie') // add a transparent image to make the event "opaque" to mouse events 1122 { 1123 $ie_fix = $indent."\t".$this->html->image('calendar','transparent.gif','', 1124 $this->html->tooltip($tooltip,False,array('BorderWidth'=>0,'Padding'=>0)). 1125 ' style="top:0px; left:0px; position:absolute; height:100%; width:100%; z-index:1"') . "\n"; 1126 } 1127 if ($this->use_time_grid) 1128 { 1129 $style = 'top: '.$this->time2pos($event['start_m']).'%; height: '.$height.'%;'; 1130 } 1131 else 1132 { 1133 $style = 'position: relative; margin-top: 3px;'; 1134 } 1135 return $indent.'<div class="calEvent'.($is_private ? 'Private' : ''). 1136 '" style="'.$style.' border-color: '.$headerbgcolor.'; background: '.$background.';"'. 1137 $popup.' '.$this->html->tooltip($tooltip,False,array('BorderWidth'=>0,'Padding'=>0)). 1138 '>'."\n".$ie_fix.$html.$indent."</div>\n"; 1139 } 1140 1141 function add_nonempty($content,$label,$one_per_line=False) 1142 { 1143 if (is_array($content)) 1144 { 1145 $content = implode($one_per_line ? ",\n" : ', ',$content); 1146 } 1147 if (!empty($content)) 1148 { 1149 return '<span class="calEventLabel">'.$label.'</span>:'. 1150 ($one_per_line ? '<br>' : ' '). 1151 nl2br($this->html->htmlspecialchars($content)).'<br>'; 1152 } 1153 return ''; 1154 } 1155 1156 /** 1157 * Calculates a brighter color for a given color 1158 * 1159 * @param $rgb string color as #rrggbb value 1160 * @param $decr int value to add to each component, default 64 1161 * @return string the brighter color 1162 */ 1163 function brighter($rgb,$decr=64) 1164 { 1165 if (!preg_match('/^#?([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/',$rgb,$components)) 1166 { 1167 return '#ffffff'; 1168 } 1169 $brighter = '#'; 1170 for ($i = 1; $i <=3; ++$i) 1171 { 1172 $val = hexdec($components[$i]) + $decr; 1173 if ($val > 255) $val = 255; 1174 $brighter .= sprintf('%02x',$val); 1175 } 1176 //echo "brighter($rgb=".print_r($components,True).")=$brighter</p>\n"; 1177 return $brighter; 1178 } 1179 1180 1181 /** 1182 * Creates a planner view: grid with columns for the time and rows for categories or users 1183 * 1184 * Uses the plannerRowWidget to display rows 1185 * 1186 * @param array $events events to show 1187 * @param mixed $start start-time of the grid 1188 * @param mixed $end end-time of the grid 1189 * @param boolean/int $by_cat rows by sub-categories of $by_cat (cat_id or 0 for upmost level) or by users (false) 1190 * @param string $indent='' string for correct indention 1191 * @return string with widget 1192 */ 1193 function &plannerWidget($events,$start,$end,$by_cat=0,$indent='') 1194 { 1195 $content = $indent.'<div class="plannerWidget">'."\n"; 1196 1197 // display the header, containing a headerTitle and multiple headerRows with the scales 1198 $content .= $indent."\t".'<div class="plannerHeader">'."\n"; 1199 // display the headerTitle 1200 $title = $by_cat === false ? lang('User') : lang('Category'); 1201 $content .= $indent."\t\t".'<div class="plannerHeaderTitle th">'.$title."</div>\n"; 1202 1203 // display the headerRows with the scales 1204 $content .= $indent."\t\t".'<div class="plannerHeaderRows">'."\n"; 1205 // set start & end to timestamp and first & last to timestamp of 12h midday, to avoid trouble with daylight saving 1206 foreach(array('start' => 'first','end' => 'last') as $t => $v) 1207 { 1208 $$t = $this->bo->date2ts($$t); 1209 $$v = $this->bo->date2array($$t); 1210 unset(${$v}['raw']); 1211 ${$v}['hour'] = 12; 1212 ${$v}['minute'] = ${$v}['second'] = 0; 1213 ${$v} = $this->bo->date2ts($$v); 1214 } 1215 $days = 1 + (int) round(($last - $first) / DAY_s); // we have to use round to get the right number if daylight saving changes 1216 if ($days >= 28) // display the month scale 1217 { 1218 $content .= $this->plannerMonthScale($first,$days,$indent."\t\t\t"); 1219 } 1220 if ($days >= 5) // display the week scale 1221 { 1222 $content .= $this->plannerWeekScale($first,$days,$indent."\t\t\t"); 1223 } 1224 $content .= $this->plannerDayScale($first,$days,$indent."\t\t\t"); // day-scale, always displayed 1225 if ($days <= 7) // display the hour scale 1226 { 1227 $content .= $this->plannerHourScale($start,$days,$indent."\t\t\t"); 1228 } 1229 $content .= $indent."\t\t</div>\n"; // end of the plannerHeaderRows 1230 $content .= $indent."\t</div>\n"; // end of the plannerHeader 1231 1232 // sort the events after user or category 1233 $rows = $sort2label = array(); 1234 if ($by_cat === false) // planner by user 1235 { 1236 $sort2label = $this->_get_planner_users(); 1237 } 1238 foreach($events as $key => $event) 1239 { 1240 if ($by_cat === false) // planner by user 1241 { 1242 foreach($event['participants'] as $sort => $status) 1243 { 1244 // only show if participant has not rejected or user wants to see rejections 1245 if (isset($sort2label[$sort]) && ($status != 'R' || $this->bo->cal_prefs['show_rejected'])) 1246 { 1247 $rows[$sort][] =& $events[$key]; 1248 } 1249 } 1250 } 1251 else // planner by cat 1252 { 1253 foreach($this->_get_planner_cats($event['category'],$sort2label,$sort2color) as $sort) 1254 { 1255 if (!is_array($rows[$sort])) $rows[$sort] = array(); 1256 1257 $rows[$sort][] =& $events[$key]; 1258 } 1259 } 1260 } 1261 // display a plannerRowWidget for each row (user or category) 1262 foreach($sort2label as $sort => $label) 1263 { 1264 if (!isset($rows[$sort])) continue; // dont show empty categories (user-rows get all initialised 1265 1266 $class = $class == 'row_on' ? 'row_off' : 'row_on'; 1267 $content .= $this->plannerRowWidget(isset($rows[$sort]) ? $rows[$sort] : array(),$start,$end,$label,$class,$indent."\t"); 1268 } 1269 $content .= $indent."</div>\n"; // end of the plannerWidget 1270 1271 return $content; 1272 } 1273 1274 /** 1275 * get all users to display in the planner_by_user 1276 * 1277 * @param boolean $enum_groups=true should groups be returned as there members (eg. planner) or not (day & week) 1278 * @return array with uid => label pairs, first all users alphabetically sorted, then all resources 1279 */ 1280 function _get_planner_users($enum_groups=true) 1281 { 1282 $users = $resources = array(); 1283 foreach(explode(',',$this->owner) as $user) 1284 { 1285 if (!is_numeric($user)) // resources 1286 { 1287 $resources[$user] = $this->bo->participant_name($user); 1288 } 1289 elseif ($enum_groups && $GLOBALS['egw']->accounts->get_type($user) == 'g') // groups 1290 { 1291 foreach((array) $GLOBALS['egw']->accounts->member($user) as $data) 1292 { 1293 $user = $data['account_id']; 1294 if ($this->bo->check_perms(EGW_ACL_READ,0,$user)) 1295 { 1296 $users[$user] = $this->bo->participant_name($user); 1297 } 1298 } 1299 } 1300 else // users 1301 { 1302 $users[$user] = $this->bo->participant_name($user); 1303 } 1304 } 1305 asort($users); 1306 asort($resources); 1307 1308 return $users+$resources; 1309 } 1310 1311 /** 1312 * get all categories used as sort criteria for the planner by category 1313 * 1314 * the returned cat is as direct sub-category of $this->cat_id or a main (level 1) category if !$this->cat_id 1315 * 1316 * @param string $cats comma-delimited cat_id's or empty for no cat 1317 * @param array &$sort2label labels for the returned cats 1318 * @return array with cat_id's 1319 */ 1320 function _get_planner_cats($cats,&$sort2label) 1321 { 1322 static $cat2sort; 1323 1324 if (!is_array($cat2sort)) 1325 { 1326 $cat2sort = array(); 1327 foreach((array)$this->cats->return_array('all',0,false,'','','',true) as $data) 1328 { 1329 if ($data['parent'] == $this->cat_id || $data['id'] == $this->cat_id) // cat is a direct sub of $this->cat_id 1330 { 1331 $cat2sort[$data['id']] = $data['id']; 1332 $sort2label[$data['id']] = stripslashes($data['name']); 1333 } 1334 elseif(isset($cat2sort[$data['parent']])) // parent is already in the array => add us with same target 1335 { 1336 $cat2sort[$data['id']] = $cat2sort[$data['parent']]; 1337 } 1338 } 1339 } 1340 $ret = array(); 1341 foreach(!is_array($cats) ? explode(',',$cats) : $cats as $cat) 1342 { 1343 if (isset($cat2sort[$cat]) && !in_array($cat2sort[$cat],$ret)) 1344 { 1345 $ret[] = $cat2sort[$cat]; 1346 } 1347 } 1348 if (!count($ret)) 1349 { 1350 $sort2label[0] = lang('none'); 1351 $ret[] = 0; 1352 } 1353 //echo "<p>uiviews::_get_planner_cats($cats=".$this->cats->id2name($cats).") (this->cat_id=$this->cat_id) = ".print_r($ret,true).'='.$this->cats->id2name($ret[0])."</p>\n"; 1354 return $ret; 1355 } 1356 1357 /** 1358 * Creates month scale for the planner 1359 * 1360 * @param int $start start-time (12h) of the scale 1361 * @param int $days number of days to display 1362 * @param string $indent='' string for correct indention 1363 * @return string with scale 1364 */ 1365 function plannerMonthScale($start,$days,$indent) 1366 { 1367 $day_width = round(100 / $days,2); 1368 1369 $content .= $indent.'<div class="plannerScale">'."\n"; 1370 for($t = $start,$left = 0,$i = 0; $i < $days; $t += $days_in_month*DAY_s,$left += $days_in_month*$day_width,$i += $days_in_month) 1371 { 1372 $t_arr = $this->bo->date2array($t); 1373 unset($t_arr['raw']); // force recalculation 1374 unset($t_arr['full']); 1375 $days_in_month = $this->datetime->days_in_month($t_arr['month'],$t_arr['year']) - ($t_arr['day']-1); 1376 if ($i + $days_in_month > $days) 1377 { 1378 $days_in_month = $days - $i; 1379 } 1380 if ($days_in_month > 10) 1381 { 1382 $title = lang(date('F',$t)).' '.$t_arr['year']; 1383 // previous links 1384 $prev = $t_arr; 1385 $prev['day'] = 1; 1386 if ($prev['month']-- <= 1) 1387 { 1388 $prev['month'] = 12; 1389 $prev['year']--; 1390 } 1391 if ($this->bo->date2ts($prev) < $start-20*DAY_s) 1392 { 1393 $prev['day'] = $this->day; 1394 $full = $this->bo->date2string($prev); 1395 if ($this->day >= 15) $prev = $t_arr; // we stay in the same month 1396 $prev['day'] = $this->day < 15 ? 15 : 1; 1397 $half = $this->bo->date2string($prev); 1398 $title = $this->html->a_href($this->html->image('phpgwpai','first',lang('back one month'),$options=' alt="<<"'),array( 1399 'menuaction' => $this->view_menuaction, 1400 'date' => $full, 1401 )) . ' '. 1402 $this->html->a_href($this->html->image('phpgwpai','left',lang('back half a month'),$options=' alt="<"'),array( 1403 'menuaction' => $this->view_menuaction, 1404 'date' => $half, 1405 )) . ' '.$title; 1406 } 1407 // next links 1408 $next = $t_arr; 1409 if ($next['month']++ >= 12) 1410 { 1411 $next['month'] = 1; 1412 $next['year']++; 1413 } 1414 // dont show next scales, if there are more then 10 days in the next month or there is no next month 1415 $days_in_next_month = (int) date('d',$end = $start+$days*DAY_s); 1416 if ($days_in_next_month <= 10 || date('m',$end) == date('m',$t)) 1417 { 1418 if ($this->day >= 15) $next = $t_arr; // we stay in the same month 1419 $next['day'] = $this->day; 1420 $full = $this->bo->date2string($next); 1421 if ($this->day < 15) $next = $t_arr; // we stay in the same month 1422 $next['day'] = $this->day < 15 ? 15 : 1; 1423 $half = $this->bo->date2string($next); 1424 $title .= ' '.$this->html->a_href($this->html->image('phpgwpai','right',lang('forward half a month'),$options=' alt=">>"'),array( 1425 'menuaction' => $this->view_menuaction, 1426 'date' => $half, 1427 )). ' '. 1428 $this->html->a_href($this->html->image('phpgwpai','last',lang('forward one month'),$options=' alt=">>"'),array( 1429 'menuaction' => $this->view_menuaction, 1430 'date' => $full, 1431 )); 1432 } 1433 } 1434 else 1435 { 1436 $title = ' '; 1437 } 1438 $class = $class == 'row_on' ? 'th' : 'row_on'; 1439 $content .= $indent."\t".'<div class="plannerMonthScale '.$class.'" style="left: '.$left.'%; width: '.($day_width*$days_in_month).'%;">'. 1440 $title."</div>\n"; 1441 } 1442 $content .= $indent."</div>\n"; // end of plannerScale 1443 1444 return $content; 1445 } 1446 1447 /** 1448 * Creates a week scale for the planner 1449 * 1450 * @param int $start start-time (12h) of the scale 1451 * @param int $days number of days to display 1452 * @param string $indent='' string for correct indention 1453 * @return string with scale 1454 */ 1455 function plannerWeekScale($start,$days,$indent) 1456 { 1457 $week_width = round(100 / $days * ($days <= 7 ? $days : 7),2); 1458 1459 $content .= $indent.'<div class="plannerScale">'."\n"; 1460 for($t = $start,$left = 0,$i = 0; $i < $days; $t += 7*DAY_s,$left += $week_width,$i += 7) 1461 { 1462 $title = lang('Week').' '.date('W',$t); 1463 if ($days > 7) 1464 { 1465 $title = $this->html->a_href($title,array( 1466 'menuaction' => 'calendar.uiviews.planner', 1467 'planner_days' => 7, 1468 'date' => date('Ymd',$t), 1469 ),false,' title="'.$this->html->htmlspecialchars(lang('Weekview')).'"'); 1470 } 1471 else 1472 { 1473 // prev. week 1474 $title = $this->html->a_href($this->html->image('phpgwpai','first',lang('previous'),$options=' alt="<<"'),array( 1475 'menuaction' => $this->view_menuaction, 1476 'date' => date('Ymd',$t-7*DAY_s), 1477 )) . ' <b>'.$title; 1478 // next week 1479 $title .= '</b> '.$this->html->a_href($this->html->image('phpgwpai','last',lang('next'),$options=' alt=">>"'),array( 1480 'menuaction' => $this->view_menuaction, 1481 'date' => date('Ymd',$t+7*DAY_s), 1482 )); 1483 } 1484 $class = $class == 'row_on' ? 'th' : 'row_on'; 1485 $content .= $indent."\t".'<div class="plannerWeekScale '.$class.'" style="left: '.$left.'%; width: '.$week_width.'%;">'.$title."</div>\n"; 1486 } 1487 $content .= $indent."</div>\n"; // end of plannerScale 1488 1489 return $content; 1490 } 1491 1492 /** 1493 * Creates day scale for the planner 1494 * 1495 * @param int $start start-time (12h) of the scale 1496 * @param int $days number of days to display 1497 * @param string $indent='' string for correct indention 1498 * @return string with scale 1499 */ 1500 function plannerDayScale($start,$days,$indent) 1501 { 1502 $day_width = round(100 / $days,2); 1503 1504 $content .= $indent.'<div class="plannerScale'.($days > 3 ? 'Day' : '').'">'."\n"; 1505 for($t = $start,$left = 0,$i = 0; $i < $days; $t += DAY_s,$left += $day_width,++$i) 1506 { 1507 $this->_day_class_holiday($this->bo->date2string($t),$class,$holidays,$days > 7); 1508 1509 if ($days <= 3) 1510 { 1511 $title = '<b>'.lang(date('l',$t)).', '.date('j',$t).'. '.lang(date('F',$t)).'</b>'; 1512 } 1513 elseif ($days <= 7) 1514 { 1515 $title = lang(date('l',$t)).'<br />'.date('j',$t).'. '.lang(date('F',$t)); 1516 } 1517 else 1518 { 1519 $title = substr(lang(date('D',$t)),0,2).'<br />'.date('j',$t); 1520 } 1521 if ($days > 1) 1522 { 1523 $title = $this->html->a_href($title,array( 1524 'menuaction' => 'calendar.uiviews.planner', 1525 'planner_days' => 1, 1526 'date' => date('Ymd',$t), 1527 ),false,strstr($class,'calHoliday') || strstr($class,'calBirthday') ? '' : ' title="'.$this->html->htmlspecialchars(lang('Dayview')).'"'); 1528 } 1529 if ($days < 5) 1530 { 1531 if (!$i) // prev. day only for the first day 1532 { 1533 $title = $this->html->a_href($this->html->image('phpgwpai','first',lang('previous'),$options=' alt="<<"'),array( 1534 'menuaction' => $this->view_menuaction, 1535 'date' => date('Ymd',$start-DAY_s), 1536 )) . ' '.$title; 1537 } 1538 if ($i == $days-1) // next day only for the last day 1539 { 1540 $title .= ' '.$this->html->a_href($this->html->image('phpgwpai','last',lang('next'),$options=' alt=">>"'),array( 1541 'menuaction' => $this->view_menuaction, 1542 'date' => date('Ymd',$start+DAY_s), 1543 )); 1544 } 1545 } 1546 $content .= $indent."\t".'<div class="plannerDayScale '.$class.'" style="left: '.$left.'%; width: '.$day_width.'%;"'. 1547 ($holidays ? ' title="'.$this->html->htmlspecialchars($holidays).'"' : '').'>'.$title."</div>\n"; 1548 } 1549 $content .= $indent."</div>\n"; // end of plannerScale 1550 1551 return $content; 1552 } 1553 1554 /** 1555 * Creates hour scale for the planner 1556 * 1557 * @param int $start start-time (12h) of the scale 1558 * @param int $days number of days to display 1559 * @param string $indent='' string for correct indention 1560 * @return string with scale 1561 */ 1562 function plannerHourScale($start,$days,$indent) 1563 { 1564 foreach(array(1,2,3,4,6,8,12) as $d) // numbers dividing 24 without rest 1565 { 1566 if ($d > $days) break; 1567 $decr = $d; 1568 } 1569 $hours = $days * 24; 1570 if ($days == 1) // for a single day we calculate the hours of a days, to take into account daylight saving changes (23 or 25 hours) 1571 { 1572 $t_arr = $this->bo->date2array($start); 1573 unset($t_arr['raw']); 1574 $t_arr['hour'] = $t_arr['minute'] = $t_arr['second'] = 0; 1575 $s = $this->bo->date2ts($t_arr); 1576 $t_arr['hour'] = 23; $t_arr['minute'] = $t_arr['second'] = 59; 1577 $hours = ($this->bo->date2ts($t_arr) - $s) / HOUR_s; 1578 } 1579 $cell_width = round(100 / $hours * $decr,2); 1580 1581 $content .= $indent.'<div class="plannerScale">'."\n"; 1582 for($t = $start,$left = 0,$i = 0; $i < $hours; $t += $decr*HOUR_s,$left += $cell_width,$i += $decr) 1583 { 1584 $title = date($this->cal_prefs['timeformat'] == 12 ? 'ha' : 'H',$t); 1585 1586 $class = $class == 'row_on' ? 'th' : 'row_on'; 1587 $content .= $indent."\t".'<div class="plannerHourScale '.$class.'" style="left: '.$left.'%; width: '.($cell_width).'%;">'.$title."</div>\n"; 1588 } 1589 $content .= $indent."</div>\n"; // end of plannerScale 1590 1591 return $content; 1592 } 1593 1594 /** 1595 * Creates a row for one user or category, with a header (user or category name) and (multiple) rows with non-overlapping events 1596 * 1597 * Uses the eventRowWidget to display a row of non-overlapping events 1598 * 1599 * @param array $events to show 1600 * @param int $start start-time of the row 1601 * @param int $end end-time of the row 1602 * @param string $header user or category name for the row-header 1603 * @param string $class additional css class for the row 1604 * @param string $indent='' string for correct indention 1605 * @return string with widget 1606 */ 1607 function plannerRowWidget($events,$start,$end,$header,$class,$indent='') 1608 { 1609 $content = $indent.'<div class="plannerRowWidget '.$class.'">'."\n"; 1610 1611 // display the row-header 1612 $content .= $indent."\t".'<div class="plannerRowHeader">'.$header."</div>\n"; 1613 1614 // sorting the events in non-overlapping rows 1615 $rows = array(array()); 1616 $row_end = array(); 1617 foreach($events as $n => $event) 1618 { 1619 for($row = 0; (int) $row_end[$row] > $event['start']; ++$row); // find a "free" row (no other event) 1620 $rows[$row][] =& $events[$n]; 1621 $row_end[$row] = $event['end']; 1622 } 1623 //echo $header; _debug_array($rows); 1624 // display the rows 1625 $content .= $indent."\t".'<div class="eventRows">'."\n"; 1626 foreach($rows as $row) 1627 { 1628 $content .= $this->eventRowWidget($row,$start,$end,$indent."\t\t"); 1629 } 1630 $content .= $indent."\t</div>\n"; // end of the eventRows 1631 1632 $content .= $indent."</div>\n"; // end of the plannerRowWidget 1633 1634 return $content; 1635 } 1636 1637 /** 1638 * Creates a row with non-overlapping events 1639 * 1640 * Uses the plannerEventWidget to display the events 1641 * 1642 * @param array $events non-overlapping events to show 1643 * @param int $start start-time of the row 1644 * @param int $end end-time of the row 1645 * @param string $indent='' string for correct indention 1646 * @return string with widget 1647 */ 1648 function eventRowWidget($events,$start,$end,$indent='') 1649 { 1650 $content = $indent.'<div class="eventRowWidget">'."\n"; 1651 1652 foreach($events as $event) 1653 { 1654 $content .= $this->plannerEventWidget($event,$start,$end,$indent."\t"); 1655 } 1656 $content .= $indent."</div>\n"; 1657 1658 return $content; 1659 } 1660 1661 /** 1662 * Calculate a time-dependent position in the planner 1663 * 1664 * We use a non-linear scale in the planner monthview, which shows the workday start or end 1665 * as start or end of the whole day. This improves the resolution a bit. 1666 * 1667 * @param int $time 1668 * @param int $start start-time of the planner 1669 * @param int $end end-time of the planner 1670 * @return float percentage position between 0-100 1671 */ 1672 function _planner_pos($time,$start,$end) 1673 { 1674 if ($time <= $start) return 0; // we are left of our scale 1675 if ($time >= $end) return 100; // we are right of our scale 1676 1677 if ($this->planner_days) 1678 { 1679 $percent = ($time - $start) / ($end - $start); 1680 } 1681 else // monthview 1682 { 1683 $t_arr = $this->bo->date2array($time); 1684 $day_start = $this->bo->date2ts((string)$t_arr['full']); 1685 $percent = ($day_start - $start) / ($end - $start); 1686 1687 $time_of_day = 60 * $t_arr['hour'] + $t_arr['minute']; 1688 if ($time_of_day >= $this->wd_start) 1689 { 1690 if ($time_of_day > $this->wd_end) 1691 { 1692 $day_percentage = 1; 1693 } 1694 else 1695 { 1696 $day_percentage = ($time_of_day-$this->wd_start) / ($this->wd_end - $this->wd_start); // between 0 and 1 1697 } 1698 $days = ($end - $start) / DAY_s; 1699 $percent += $day_percentage / $days; 1700 } 1701 } 1702 $percent = round(100 * $percent,2); 1703 1704 //echo "<p>_planner_pos(".date('Y-m-d H:i',$time).', '.date('Y-m-d H:i',$start).', '.date('Y-m-d H:i',$end).") = $percent</p>\n"; 1705 return $percent; 1706 } 1707 1708 /** 1709 * Displays one event for the planner, using the eventWidget of the other views 1710 * 1711 * @param array $event 1712 * @param int $start start-time of the planner 1713 * @param int $end end-time of the planner 1714 * @return string with widget 1715 */ 1716 function plannerEventWidget($event,$start,$end,$indent='') 1717 { 1718 // some fields set by the dayColWidget for the other views 1719 $day_start = $this->bo->date2ts((string)$this->bo->date2string($event['start'])); 1720 $event['start_m'] = ($event['start'] - $day_start) / 60; 1721 $event['end_m'] = round(($event['end'] - $day_start) / 60); 1722 $event['multiday'] = true; 1723 1724 $data = $this->eventWidget($event,200,$indent,true,'planner_event'); 1725 1726 $left = $this->_planner_pos($event['start'],$start,$end); 1727 $width = $this->_planner_pos($event['end'],$start,$end) - $left; 1728 $color = $data['color'] ? $data['color'] : 'gray'; 1729 1730 return $indent.'<div class="plannerEvent'.($data['private'] ? 'Private' : '').'" style="left: '.$left. 1731 '%; width: '.$width.'%; background-color: '.$color.';"'.$data['popup'].' '. 1732 $this->html->tooltip($data['tooltip'],False,array('BorderWidth'=>0,'Padding'=>0)).'>'."\n".$data['html'].$indent."</div>\n"; 1733 } 1734 }
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sun Feb 25 17:20:01 2007 | par Balluche grâce à PHPXref 0.7 |