diff --git a/calendar/inc/class.calendar_hooks.inc.php b/calendar/inc/class.calendar_hooks.inc.php index 83970b3aa1..2111e382bd 100644 --- a/calendar/inc/class.calendar_hooks.inc.php +++ b/calendar/inc/class.calendar_hooks.inc.php @@ -153,6 +153,7 @@ class calendar_hooks 'week' => lang('Weekview'), 'weekN' => lang('Multiple week view'), 'month' => lang('Monthview'), + 'year' => lang('yearview'), 'planner_cat' => lang('Planner by category'), 'planner_user' => lang('Planner by user'), 'listview' => lang('Listview'), diff --git a/calendar/inc/class.calendar_ui.inc.php b/calendar/inc/class.calendar_ui.inc.php index a0f854a9d0..60a04cd28f 100644 --- a/calendar/inc/class.calendar_ui.inc.php +++ b/calendar/inc/class.calendar_ui.inc.php @@ -545,6 +545,7 @@ class calendar_ui 'week' => array('icon'=>'week','text'=>'Weekview','menuaction' => 'calendar.calendar_uiviews.week'), 'weekN' => array('icon'=>'multiweek','text'=>'Multiple week view','menuaction' => 'calendar.calendar_uiviews.weekN'), 'month' => array('icon'=>'month','text'=>'Monthview','menuaction' => 'calendar.calendar_uiviews.month'), + //'year' => array('icon'=>'year','text'=>'yearview','menuaction' => 'calendar.calendar_uiviews.year'), 'planner' => array('icon'=>'planner','text'=>'Group planner','menuaction' => 'calendar.calendar_uiviews.planner','sortby' => $this->sortby), 'list' => array('icon'=>'list','text'=>'Listview','menuaction'=>'calendar.calendar_uilist.listview'), ) as $view => $data) @@ -610,6 +611,11 @@ class calendar_ui 'value' => 'menuaction=calendar.calendar_uiviews.month', 'selected' => $this->view == 'month', ), + array( + 'text' => lang('yearview'), + 'value' => 'menuaction=calendar.calendar_uiviews.year', + 'selected' => $this->view == 'year', + ), array( 'text' => lang('planner by category'), 'value' => 'menuaction=calendar.calendar_uiviews.planner&sortby=category'. diff --git a/calendar/inc/class.calendar_uiviews.inc.php b/calendar/inc/class.calendar_uiviews.inc.php index 3736afdafe..e6721ab775 100644 --- a/calendar/inc/class.calendar_uiviews.inc.php +++ b/calendar/inc/class.calendar_uiviews.inc.php @@ -32,6 +32,7 @@ class calendar_uiviews extends calendar_ui 'week' => True, 'weekN' => True, 'month' => True, + 'year' => True, 'planner' => True, 'index' => True, ); @@ -67,14 +68,14 @@ class calendar_uiviews extends calendar_ui var $timeRow_width = 40; /** - * how many rows per day get displayed, gets set be the timeGridWidget + * how many rows per day get displayed, gets set by the timeGridWidget * * @var int */ var $rowsToDisplay; /** - * height in percent of one row, gets set be the timeGridWidget + * height in percent of one row, gets set by the timeGridWidget * * @var int */ @@ -137,6 +138,7 @@ class calendar_uiviews extends calendar_ui '4day' => lang('Four days view'), 'week' => lang('Weekview'), 'month' => lang('Monthview'), + 'year' => lang('yearview'), 'planner' => lang('Group planner'), ); $GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps']['calendar']['title']. @@ -217,6 +219,9 @@ class calendar_uiviews extends calendar_ui case 'planner': return $group_warning.$this->planner(true); + case 'year': + return $group_warning.$this->year(true); + case 'month': return $group_warning.$this->month(0,true); @@ -320,6 +325,230 @@ class calendar_uiviews extends calendar_ui return $this->month($num,$home); } + /** Month column width (usually 3 or 4) in year view */ + const YEARVIEW_COLS = 3; + + /** + * Displays a year view + * + * @param boolean $home=false if true return content suitable for home-page + */ + function &year($home=false) + { + if ($this->debug > 0) $this->bo->debug_message('uiviews::year date=%2',True,$this->date); + + $content = $this->edit_series(); + + $this->_month_align_year($this->first,$this->last); + + $GLOBALS['egw_info']['flags']['app_header'] .= ': '.$this->year; + + $days =& $this->bo->search(array( + 'start' => $this->first, + 'end' => $this->last, + ) + $this->search_params); + + /* Loop through the week-aligned months. */ + for ($month = 1; $month <= 12; $month++) + { + // The first date entry in the view may be in the last month. + if (($month - 1) % self::YEARVIEW_COLS == 0) + { + $content .= '
'."\n"; + $content .= "\t".'
'."\n"; + } + + $month_start = $this->datetime->get_weekday_start($this->year,$month,1); + // Beginning of the last week in the month + $month_end = $this->datetime->get_weekday_start( + $this->year, + $month, + $this->datetime->days_in_month($month,$this->year)); + // End of the last week in month + $month_end = strtotime("+6 days",$month_end); + + $content .= "\t\t".'
'."\n"; + + // Year Header + $content .= "\t\t\t".'
'."\n"; + if (($month) == 1) + { + $content .= ''; + $content .= html::a_href(html::image('phpgwapi','first',lang('previous'),$options=' alt="<<"'),array( + 'menuaction' => $this->view_menuaction, + 'date' => date('Ymd',strtotime('-1 year',strtotime($this->date))), + )); + $content .= ''."\n"; + } + $content .= "\t\t\t\t".''.lang(adodb_date('F',strtotime("+1 week",$month_start))).''."\n"; + if ($month == self::YEARVIEW_COLS) + { + $content .= ''; + $content .= html::a_href(html::image('phpgwapi','last',lang('next'),$options=' alt=">>"'),array( + 'menuaction' => $this->view_menuaction, + 'date' => date('Ymd',strtotime('+1 year',strtotime($this->date))), + )); + $content .= ''."\n"; + } + $content .= "\t\t\t".'
'."\n"; + + $content .= "\t\t\t".''."\n"; + $content .= "\t\t\t\t".'
'.lang('Wk').'
'."\n"; + // Day Columns, Legend + for ($i = 0; $i <= 6; $i++) + { + $day_date = ($i ? strtotime("+$i days",$month_start) : $month_start); + if (adodb_date('w',$day_date) % 6 == 0) + { + $style = 'cal_year_legend_weekend'; + } + else + { + $style = 'cal_year_legend'; + } + + $content .= "\t\t\t\t".'
'. + lang(adodb_date('D',$day_date)).'
'."\n"; + } + $content .= "\t\t\t".'
'."\n"; + + // Week rows in month + $week_start = $month_start; + for ($week_in_month = 1; $week_in_month <= 6; $week_in_month ++) + { + $content .= "\t\t\t".''."\n"; + + $content .= "\t\t\t\t".'
'."\n"; + $content .= "\t\t\t\t\t". + ''.adodb_date('W',$week_start)."\n"; + $content .= "\t\t\t\t".'
'."\n"; + // Day columns in week row + for ($i = 0; $i <= 6; $i++) + { + $day_date = $i ? strtotime("+$i days",$week_start) : $week_start; + $day_ymd = $this->bo->date2string($day_date); + $eventcount = count($days[$day_ymd]); + $in_month = true; + $css_class = ""; + $this->_day_class_holiday($day_ymd,$class,$holidays,false,false); + if (adodb_date('n',$day_date) != $month) + { + $css_class .= 'cal_year_legend'; + $in_month = false; + } + else + { + $css_class .= 'calEvent calEventAllAccepted'; + if (adodb_date('w',$day_date) % 6 == 0) + { + $css_class .= ' cal_year_weekend'; + } + else + { + if ($holidays) + { + $css_class .= ' calHoliday'; + } + else + { + $css_class .= ' cal_year_free'; + } + } + + if ($day_ymd == $this->bo->date2string($this->bo->now_su)) + { + $css_class .= ' cal_year_today'; + } + } + $content .= "\t\t\t\t".''."\n"; + $content .= "\t\t\t\t".'
'."\n"; + + + if (($in_month) && (count($days[$day_ymd]))) + { + $eventCols = $this->getEventCols($day_ymd,$days[$day_ymd]); + // displaying all event columns of the day + $row_height = 100 / count($eventCols); + $space_left = 4; //% + $space_right = 1; //% + $row_width = 11.5 - $space_left - $space_right; + // settings for time2pos + $this->scroll_to_wdstart = false; + $this->wd_start = 0; + $this->wd_end = 24*60; + $this->granularity_m = 24 * 60; + $this->extraRows = -1; + $this->rowHeight = $row_width; + foreach($eventCols as $n => $eventCol) + { + foreach ($eventCol as $event) + { + $indent = "\t\t\t\t"; + // some fields set by the dayColWidget for the other views + unset($event['whole_day_on_top']); + $data = $this->eventWidget($event,25,$indent,$this->owner,true,'planner_event'); + + $left = ((($i + 1) * 12.5) + 0.5 + $space_left + $this->time2pos($event['start_m'])); + $width = $this->time2pos($event['end_m'] - $event['start_m']); + $color = $data['color'] ? $data['color'] : 'gray'; + + $content .= $indent.'
0,'Padding'=>0)). + '>'."\n".$data['html'].$indent."
\n"; + } + } + } + } + $week_start = strtotime("+1 week",$week_start); + $content .= "\t\t\t".'
'."\n"; + } + $content .= "\t\t".'
'."\n"; + + if (($month) % self::YEARVIEW_COLS == 0) + { + $content .= "\t
\n"; + $content .= "\n"; + } + } + + if (!$home) + { + $this->do_header(); + + echo $content; + } + + return $content; + } + /** * Displays the monthview or a multiple week-view * @@ -414,6 +643,27 @@ class calendar_uiviews extends calendar_ui $last = $this->bo->date2ts($last); } + /** + * Get start and end of a year aligned to full months + * + * @param int &$first timestamp 0h of first day of week containing the first of the current year + * @param int &$last timestamp 23:59:59 of last day of week containg the last day of the current year + */ + function _month_align_year(&$first,&$last) + { + $first = $this->datetime->get_weekday_start($this->year,$this->month=1,$this->day=1); + $last = $this->datetime->get_weekday_start($this->year,$this->month+12, + $this->datetime->days_in_month($this->month+12,$this->year)); + // now we need to calculate the end of the last day of that week + // as simple $last += WEEK_s - 1; does NOT work, if daylight saving changes in that week!!! + $last = $this->bo->date2array($last); + $last['day'] += 6; + $last['hour'] = 23; + $last['min'] = $last['sec'] = 59; + unset($last['raw']); // otherwise date2ts does not calc raw new, but uses it + $last = $this->bo->date2ts($last); + } + /** * Four days view, everythings done by the week-view code ... * @@ -763,7 +1013,7 @@ function open_edit(series) } /** - * Calculates the height of a differenc between 2 times + * Calculates the height of a difference between 2 times * * workday start- and end-time, is taken into account, as well as timeGrids px_m - minutes per pixel param * @@ -927,23 +1177,14 @@ function open_edit(series) } /** - * Creates (if necessary multiple) columns for the events of a day - * - * Uses the eventColWidget to display each column. + * Sorts the events of a day into columns with non-overlapping events, the events + * are already sorted by start-time * * @param string/int $day_ymd date as Ymd - * @param array $events of events to show - * @param int $left start of the widget - * @param int $width width of the widget - * @param string $indent string for correct indention - * @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 - * @param boolean $on_off=false start with row_on or row_off, default false=row_off - * @param int $owner=0 if != 0 owner to add to the add-event link + * @param array &$events events to split into non-overlapping groups */ - function dayColWidget($day_ymd,$events,$left,$width,$indent,$short_title=True,$on_off=False,$owner=0) + function getEventCols($day_ymd, &$events) { - 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); // if daylight saving is switched on or off, correct $day_start @@ -952,7 +1193,7 @@ function open_edit(series) { $day_start -= $daylight_diff; } - // 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) { @@ -980,6 +1221,26 @@ function open_edit(series) } $eventCols[$c][] = $event; } + return $eventCols; + } + + /** + * Creates (if necessary multiple) columns for the events of a day + * + * Uses the eventColWidget to display each column. + * + * @param string/int $day_ymd date as Ymd + * @param array $events of events to show + * @param int $left start of the widget + * @param int $width width of the widget + * @param string $indent string for correct indention + * @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 + * @param boolean $on_off=false start with row_on or row_off, default false=row_off + * @param int $owner=0 if != 0 owner to add to the add-event link + */ + function dayColWidget($day_ymd,$events,$left,$width,$indent,$short_title=True,$on_off=False,$owner=0) + { + 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); $html = $indent.'
'."\n"; @@ -995,7 +1256,7 @@ function open_edit(series) ); $this->_day_class_holiday($day_ymd,$class,$holidays); // the weekday and date - if (!$short_title && $holidays) $title .= ': '.$holidays; + if (!$short_title && $holidays) $title .= html::htmlspecialchars(': '.$holidays); if ($short_title === true) { @@ -1027,8 +1288,10 @@ function open_edit(series) $title .= '   '.$day_view; } } - $html .= $indent."\t".'
'. - $title."
\n"; + if (is_bool($short_title) || ($short_title != "")) { + $html .= $indent."\t".'
'.$title."
\n"; + } if ($this->use_time_grid) { @@ -1045,7 +1308,7 @@ function open_edit(series) } } // adding divs to click on for each row / time-span - for($t = $this->scroll_to_wdstart ? 0 : $this->wd_start,$i = 1+$this->extraRows; + for($t = $this->scroll_to_wdstart ? 0 : $this->wd_start,$i = 1 + $this->extraRows; $t <= $this->wd_end || $this->scroll_to_wdstart && $t < 24*60; $t += $this->granularity_m,++$i) { @@ -1079,6 +1342,8 @@ function open_edit(series) } } } + + $eventCols = $this->getEventCols($day_ymd,$events); // displaying all event columns of the day foreach($eventCols as $n => $eventCol) { @@ -1098,26 +1363,35 @@ function open_edit(series) * @param string &$class class to use * @param string &$holidays commaseparted holidays or empty if none * @param boolean $only_weekend=false show only the weekend in header-color, otherwise every second days is shown too + * @param boolean $show_bdays=true If available, also show birthdays (or hide Bdays) + * Note that this is not the place to disable a preference. + * If the preferences allow birthdays to be displayed, they are cached within the holidays structure. + * This setting just suppressing the available data in the view. */ - function _day_class_holiday($day_ymd,&$class,&$holidays,$only_weekend=false) + function _day_class_holiday($day_ymd,&$class,&$holidays,$only_weekend=false,$show_bdays=true) { $class = $holidays = ''; $bday = false; if (isset($this->holidays[$day_ymd])) { + $h = array(); foreach($this->holidays[$day_ymd] as $holiday) { if (isset($holiday['birthyear'])) { - $bday = true; + if ($show_bdays) + { + $bday = true; + $h[] = $holiday['name']; + } } else { $class = 'calHoliday'; + $h[] = $holiday['name']; } - $holidays[] = $holiday['name']; } - $holidays = implode(', ',$holidays); + $holidays = implode(', ',$h); } if (!$class) { @@ -1243,7 +1517,10 @@ function open_edit(series) $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 ! - if (!$return_array) $height = $this->times2height($event['start_m'],$event['end_m'],$header_height); + if (!$return_array) + { + $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'; diff --git a/calendar/templates/default/app.css b/calendar/templates/default/app.css index 6bf013edc9..a81101a260 100644 --- a/calendar/templates/default/app.css +++ b/calendar/templates/default/app.css @@ -483,6 +483,27 @@ e.g. the div with class calTimeGrid is generated by the timeGridWidget method of .plannerEvent:hover{ cursor: pointer; } + +/* Special colors for the year view */ +.cal_year_legend_weekend { + background-color: #CCCCCC; +} +.cal_year_legend { + background-color: #EFEFEF; +} +.cal_year_free { + background-color: #FFFFCC; + z-index: 0; +} +.cal_year_weekend { + background-color: #F9F9CC; + z-index: 0; +} +.cal_year_today { + border-color: #EE0000; + border-width: 2px; +} + /** * edit series or exception popup used in eventWidget and * delete series and exceptions popup used in edit event