diff options
Diffstat (limited to 'includes/classes/Calendar.php')
| -rw-r--r-- | includes/classes/Calendar.php | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/includes/classes/Calendar.php b/includes/classes/Calendar.php new file mode 100644 index 0000000..f62f907 --- /dev/null +++ b/includes/classes/Calendar.php @@ -0,0 +1,483 @@ +<?php +/** + * @version $Header$ + * @package calendar + * + * @copyright Copyright (c) 2004-2006, bitweaver.org + * All Rights Reserved. See below for details and a complete list of authors. + * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See http://www.gnu.org/copyleft/lesser.html for details. + */ + +/** + * Required setup + */ +namespace Bitweaver\Calendar; +use Bitweaver\BitDate; +use Bitweaver\KernelTools; +use Bitweaver\Liberty\LibertyContent; + +// set week offset - start with a day other than monday +define( 'WEEK_OFFSET', !empty( $gBitUser->mUserPrefs['calendar_week_offset'] ) ? $gBitUser->mUserPrefs['calendar_week_offset'] : $gBitSystem->getConfig( 'calendar_week_offset', 0 ) ); + +/** + * @package calendar + */ +class Calendar extends LibertyContent { + + public $display_offset; + public $mDate; + + public function Calendar() { + parent::__construct(); + global $gBitUser; + $this->mDate = new BitDate(0); + $this->display_offset = $this->mDate->get_display_offset(); + } + + /** + * get a full list of content for a given time period + * return array of items + * + * The output array will be a set of UTC tagged pages covering the period + * defined in the $pListHash. Items identified from the list will be tagged to + * the day identified in the selected timestamp from which the list was built. + * If the user has selected a local time display, then the day will be the actual + * UTC day that the item is in, rather than the UTC day of the item. In this way + * the display view provides a list of locally correct entries for each day. + **/ + public function getList( $pListHash ) { + $ret = []; + + $pListHash['include_data'] = TRUE; + if( !empty( $pListHash['focus_date'] ) ) { + $calDates = $this->doRangeCalculations( $pListHash ); + $pListHash['time_limit_start'] = $calDates['view_start'] - $this->display_offset; + $pListHash['time_limit_stop'] = $calDates['view_end'] - $this->display_offset; + } +// if ( empty( $pListHash['sort_mode'] ) ) { +// $pListHash['sort_mode'] = !empty( $_REQUEST['sort_mode'] ) ? $_REQUEST['sort_mode'] : 'event_time_asc'; +// } + $pListHash['sort_mode'] = 'event_time_asc'; + $pListHash['time_limit_column'] = preg_replace( "/(_asc$|_desc$)/i", "", $pListHash['sort_mode'] ); + + if ( empty( $pListHash['user_id'] ) ) { + $pListHash['user_id'] = !empty( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : NULL; + } + if ( !empty( $_REQUEST['order_table'] ) ) { + $pListHash['order_table'] = $_REQUEST['order_table']; + } + + // Don't think this is required. + $pListHash['offset'] = 0; + // There should at least be a preference for this. + $pListHash['max_records'] = 500; + + $res = $this->getContentList( $pListHash ); + + foreach( $res as $item ) { + // shift all time data by user timezone offset + // and then display as a simple UTC time + $item['timestamp'] = $item[$pListHash['time_limit_column']] + $this->display_offset; + $item['created'] += $this->display_offset; + $item['last_modified'] += $this->display_offset; + $item['event_time'] += $this->display_offset; + $item['parsed'] = self::parseDataHash( $item ); + $dstart = $this->mDate->gmmktime( 0, 0, 0, $this->mDate->date( "m", $item['timestamp'], true ), $this->mDate->date( "d", $item['timestamp'], true ), $this->mDate->date( "Y", $item['timestamp'], true ) ); + $ret[$dstart][] = $item; + } + return $ret; + } + + /** + * calculate the start and stop time for the current display page + **/ + public function doRangeCalculations( $pDateHash ) { + $focus = $this->mDate->_getdate( $pDateHash['focus_date'], false, true ); + + if( $pDateHash['view_mode'] == 'month' ) { + $view_start = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], 1, $focus['year'] ); + $view_end = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'] + 1, 1, $focus['year'] ) - 1; + } elseif( $pDateHash['view_mode'] == 'week' or $pDateHash['view_mode'] == 'weeklist') { + $wd = $focus['wday'] == 0 ? 7 + WEEK_OFFSET : $focus['wday'] + WEEK_OFFSET; + // if we are moving out from the selected week, move us back in + if( $wd > 7 ) { + $wd -= 7; + } + + $view_start = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'] - $wd, $focus['year'] ); + $view_end = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'] - $wd + 7, $focus['year'] ) - 1; + } else { + $view_start = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'] , $focus['year'] ); + $view_end = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'] + 1, $focus['year'] ) - 1; + } + + $start_year = $this->mDate->date( 'Y', $view_start, true ); + if ( $start_year < 1902 ) { + $view_start_iso = $view_start = $this->mDate->date( 'Y-m-d', $view_start, true ); + $view_end_iso = $view_end = $this->mDate->date( 'Y-m-d', $view_start, true ); + $view_start = 0; + $view_end = 0; + } + + $ret = [ + 'view_start' => $view_start, + 'view_end' => $view_end, + ]; + // Insert ISO dates if they are set - Used for dates pre 1902 + if ( !empty($view_start_iso) ) { + $ret['view_start_iso'] = $view_start_iso; + $ret['view_end_iso'] = $view_end_iso; + } + return $ret; + } + + /** + * prepare ListHash to ensure errorfree usage + * @param array pListHash hash of parameters for any getList() function + * @return array the link to display the page. + **/ + public function prepGetList( &$pListHash ) { + $pListHash['include_data'] = TRUE; + if( !empty( $pListHash['focus_date'] ) ) { + $calDates = $this->doRangeCalculations( $pListHash ); + $pListHash['time_limit_start'] = $calDates['view_start'] - $this->display_offset; + $pListHash['time_limit_stop'] = $calDates['view_end'] - $this->display_offset; + } + if ( empty( $pListHash['sort_mode'] ) ) { + $pListHash['sort_mode'] = !empty( $_REQUEST['sort_mode'] ) ? $_REQUEST['sort_mode'] : 'event_time_asc'; + } + $pListHash['time_limit_column'] = preg_replace( "/(_asc$|_desc$)/i", "", $pListHash['sort_mode'] ); + + if ( empty( $pListHash['user_id'] ) ) { + $pListHash['user_id'] = !empty( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : NULL; + } + if ( !empty( $_REQUEST['order_table'] ) ) { + $pListHash['order_table'] = $_REQUEST['order_table']; + } + + // Don't think this is required. + $pListHash['offset'] = 0; + // There should at least be a preference for this. + $pListHash['max_records'] = 500; + + LibertyContent::prepGetList( $pListHash ); + return TRUE; + } + + public function buildDay( $pDateHash ) { + global $gBitSystem, $gBitUser; + $focus = $this->mDate->getdate( $pDateHash['focus_date'], false, true ); + + $ret = []; + if( $pDateHash['view_mode'] == 'day' ) { + // calculare what the visible day view range is + $day_start = $gBitUser->mUserPrefs['calendar_day_start'] ?? $gBitSystem->getConfig( 'calendar_day_start', 0 ); + $day_end = $gBitUser->mUserPrefs['calendar_day_end'] ?? $gBitSystem->getConfig( 'calendar_day_end', 24 ); + $start_time = $this->mDate->mktime( 0, 0, 0, $focus['mon'], $focus['mday'], $focus['year'] ) + ( 60 * 60 * $day_start ); + $stop_time = $this->mDate->mktime( 0, 0, 0, $focus['mon'], $focus['mday'] + 1, $focus['year'] ) - ( 60 * 60 * ( 24 - $day_end ) ); + $hours_count = ( $stop_time - $start_time ) / ( 60 * 60 ); + + // allow for custom time intervals + $hour_fraction = !empty( $gBitUser->mUserPrefs['calendar_hour_fraction'] ) ? $gBitUser->mUserPrefs['calendar_hour_fraction'] : $gBitSystem->getConfig( 'calendar_hour_fraction', 1 ); + $row_count = $hours_count * $hour_fraction; + $start_time_info = $this->mDate->getdate( $start_time, false, true ); + $hour = $start_time_info['hours'] - 1; + $mins = 0; + for( $i = 0; $i < $row_count; $i++ ) { + if( !( $i % $hour_fraction ) ) { + // set vars + $hour++; + $mins = 0; + } + $ret[$i]['time'] = $this->mDate->gmmktime( $hour, $mins, 0, $focus['mon'], $focus['mday'], $focus['year'] ); + $mins += 60 / $hour_fraction; + } + // calendar data is added below + } + return $ret; + } + + /** + * build an array of unix UTC timestamps relating to the current + * calendar focus. This provides a fixed basis from which to apply local + * offsets provided by tz_offset information. Daylight saving information + * is not available via this route! + **/ + public function buildCalendarNavigation( $pDateHash ) { + global $gBitUser, $gBitSystem; + $today = $this->mDate->getdate( time(), false, true ); + $focus = $this->mDate->getdate( $pDateHash['focus_date'], false, true ); + + $ret = [ + 'before' => [ + 'day' => $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'] - 1, $focus['year'] ), + 'week' => $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'] - 7, $focus['year'] ), + 'month' => $this->mDate->gmmktime( 0, 0, 0, $focus['mon'] - 1, $focus['mday'], $focus['year'] ), + 'year' => $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'], $focus['year'] - 1 ), + ], + 'after' => [ + 'day' => $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'] + 1, $focus['year'] ), + 'week' => $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'] + 7, $focus['year'] ), + 'month' => $this->mDate->gmmktime( 0, 0, 0, $focus['mon'] + 1, $focus['mday'], $focus['year'] ), + 'year' => $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $focus['mday'], $focus['year'] + 1 ), + ], + 'focus_month' => $focus['mon'], + 'focus_year' => $focus['year'], + 'focus_date' => $focus[0], + 'today' => $this->mDate->gmmktime( 0, 0, 0, $today['mon'], $today['mday'], $today['year'] ), + 'tz_flag' => $gBitUser->getPreference( 'site_display_utc', "Local" ), + 'display_focus_date' => $focus[0], + ]; + + return $ret; + } + + /** + * build a two dimensional array of unix timestamps + * The timestamps are either UTC or display local time depending on the + * setting of the current users display time offset + * mktime SHOULD NOT BE USED since it offsets the times based on server + * timezone and daylight saving, but the USERS daylight saving information + * is not available, which will cause some problems! + **/ + public function buildMonth( $pDateHash ) { + global $gBitSmarty; + + $focus = $this->mDate->getdate( $pDateHash['focus_date'], false, true ); + + $prev_month_end = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], 0, $focus['year'] ); + $next_month_begin = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'] + 1, 1, $focus['year'] ); + + $prev_month_end_info = $this->mDate->getdate( $prev_month_end, false, true ); + $prev_month = $prev_month_end_info['mon']; + $prev_month_year = $prev_month_end_info['year']; + + // Build a two-dimensional array of UNIX timestamps. + $cal = []; + + // Start the first row with the final day( s ) of the previous month. + $week = []; + $month_begin = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], WEEK_OFFSET, $focus['year'] ); + $month_begin_day_of_week = $this->mDate->dayOfWeek( $focus['year'], $focus['mon'], WEEK_OFFSET ); + $days_in_prev_month = $this->mDate->daysInMonth( $prev_month, $prev_month_year ); + + // Fill out the first row with the last day( s ) of the previous month. + for( $day_of_week = 0; $day_of_week < $month_begin_day_of_week; $day_of_week++ ) { + $_day = $days_in_prev_month - $month_begin_day_of_week + $day_of_week + 1; + $week[]['day'] = $this->mDate->mktime( 0, 0, 0, $prev_month, $_day, $prev_month_year ); + } + + // Fill in the days of the selected month. + $days_in_month = $this->mDate->daysInMonth( $focus['mon'], $focus['year'] ); + for( $i = 1; $i <= $days_in_month; $i++ ) { + if( $day_of_week == 7 ) { + $calendar[$this->mDate->weekOfYear( $focus['year'], $focus['mon'], $i )] = $week; + + // re-initialize $day_of_week and $week + $day_of_week = 0; + $week = []; + } + $week[]['day'] = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'], $i, $focus['year'] ); + $day_of_week++; + } + + // Fill out the last row with the first day( s ) of the next month. + for( $j = 1; $day_of_week < 7; $j++, $day_of_week++ ) { + $week[]['day'] = $this->mDate->gmmktime( 0, 0, 0, $focus['mon'] + 1, $j, $focus['year'] ); + } + $week_num = $this->mDate->weekOfYear( $focus['year'], $focus['mon'], $days_in_month + $j ); + $calendar[$week_num] = $week; + + // Modify offset to fix roll over on week numbers + // This is required because the week numbers are calculated for Sunday + // Offseting the result in BitDate is the real solution + $offset = WEEK_OFFSET == 7 ? $focus['mday'] + 1 : $focus['mday'] + 1 + WEEK_OFFSET; + // this week number has to be calculated, since the cal start can be configured + $week_num = $this->mDate->weekOfYear( $focus['year'], $focus['mon'], $offset ); + + // if we only want to see a weeks / days worth of data, nuke all xs data + if( $pDateHash['view_mode'] == 'week' or $pDateHash['view_mode'] == 'weeklist' ) { + $cal = $calendar[$week_num]; + $calendar = []; + $calendar[$week_num] = $cal; + } elseif( $pDateHash['view_mode'] == 'day' ) { + $calendar = []; + $calendar[$week_num][]['day'] = $pDateHash['focus_date']; + } + + return $calendar; + } + + // Setup the content types for use in the calendar. + public function setupContentTypes() { + global $gLibertySystem, $gBitSmarty, $gBitSystem; + foreach( $gLibertySystem->mContentTypes as $cName => $cType ) { + if ( $gBitSystem->isPackageActive( $cType['handler_package'] ) ) { + $contentTypes[$cType['content_type_guid']] = $gLibertySystem->getContentTypeName( $cType['content_type_guid'] ); + } + } + asort($contentTypes); + $gBitSmarty->assign( 'calContentTypes', $contentTypes ); + } + + // Setup the day names for use in the calendar + public function setupDayNames() { + global $gBitSmarty; + + // set up daynames for the calendar + $dayNames = [ + KernelTools::tra( "Monday" ), + KernelTools::tra( "Tuesday" ), + KernelTools::tra( "Wednesday" ), + KernelTools::tra( "Thursday" ), + KernelTools::tra( "Friday" ), + KernelTools::tra( "Saturday" ), + KernelTools::tra( "Sunday" ), + ]; + + // depending on what day we want to view first, we need to adjust the dayNames array + for( $i = 0; $i < WEEK_OFFSET; $i++ ) { + $pop = array_pop( $dayNames ); + array_unshift( $dayNames, $pop ); + } + $gBitSmarty->assign( 'dayNames', $dayNames ); + } + + function processRequestHash(&$pRequest, &$pStore) { + global $gBitUser; + if( !empty( $pRequest["content_type_guid"] ) ) { + if( $gBitUser->isRegistered() ) { + $gBitUser->storePreference( 'calendar_default_guids', serialize( $pRequest['content_type_guid'] ) ); + } + $pStore['content_type_guid'] = $pRequest["content_type_guid"]; + } elseif( !isset( $pStore['content_type_guid'] ) && $gBitUser->getPreference( 'calendar_default_guids' ) && $gBitUser->isRegistered() ) { + $pStore['content_type_guid'] = unserialize( $gBitUser->getPreference( 'calendar_default_guids' ) ); + } elseif( !isset( $pStore['content_type_guid'] ) ) { + $pStore['content_type_guid'] = []; + } elseif( isset( $pRequest["refresh"] ) && !isset( $pRequest["content_type_guid"] ) ) { + $pStore['content_type_guid'] = []; + } + + // set up the todate + if( !empty( $pRequest["todate"] ) ) { + // clean up todate. who knows where this has come from + $pStore['focus_date'] = is_numeric( $pRequest['todate'] ) ? $pRequest['todate'] = $this->mDate->gmmktime( 0, 0, 0, $this->mDate->date( 'm', $pRequest['todate'], true ), $this->mDate->date( 'd', $pRequest['todate'], true ), $this->mDate->date( 'Y', $pRequest['todate'], true ) ) : $pRequest['todate'] = $this->mDate->gmmktime( 0, 0, 0, $this->mDate->date2( 'm', $pRequest['todate'], true ), $this->mDate->date2( 'd', $pRequest['todate'], true ), $this->mDate->date2( 'Y', $pRequest['todate'], true ) ); + } elseif( !empty( $pStore['focus_date'] ) ) { + $pRequest["todate"] = $pStore['focus_date']; + } else { + $pStore['focus_date'] = $this->mDate->gmmktime( 0, 0, 0, $this->mDate->date( 'm' ), $this->mDate->date( 'd' ), $this->mDate->date( 'Y' ) ); + $pRequest["todate"] = $pStore['focus_date']; + } + + $focus = $pRequest['todate']; + if( !empty( $pRequest["view_mode"] ) ) { + $pStore['view_mode'] = $pRequest["view_mode"]; + } elseif( empty( $pStore['view_mode'] ) ) { + $pStore['view_mode'] = 'month'; + } + } + + public function getEvents(&$pListHash) { + global $gBitSystem, $gLibertySystem; + + $bitEvents = []; + if ( !empty( $pListHash['content_type_guid'] ) ) { + // Verify that the type is still active + foreach ( $pListHash['content_type_guid'] as $index => $type ) { + if ( !$gBitSystem->isPackageActive( $gLibertySystem->mContentTypes[$type]['handler_package'] ) ) { + unset( $pListHash['content_type_guid'][$index] ); + } + if ( !empty( $pListHash['content_type_guid'] ) ) { + $bitEvents = $this->getList( $pListHash ); + } + } + } + + return $bitEvents; + } + + public function buildCalendar(&$pListHash, &$pDateHash) { + global $gBitSmarty, $gBitSystem; + + if( isset($pDateHash['focus_date']) && !isset($pListHash['focus_date']) ) { + $pListHash['focus_date'] = $pDateHash['focus_date']; + } + if( isset($pDateHash['view_mode']) && !isset($pListHash['view_mode']) ) { + $pListHash['view_mode'] = $pDateHash['view_mode']; + } + $bitEvents = $this->getEvents($pListHash); + + $gBitSmarty->assign( 'navigation', $this->buildCalendarNavigation( $pDateHash ) ); + $calMonth = $this->buildMonth( $pDateHash ); + $calDay = $this->buildDay( $pDateHash ); + + foreach( $calMonth as $w => $week ) { + foreach( $week as $d => $day ) { + $dayEvents = []; + if( !empty( $bitEvents[$day['day']] ) ) { + $i = 0; + foreach( $bitEvents[$day['day']] as $bitEvent ) { + $bitEvent['parsed_data'] = self::parseDataHash($bitEvent); + $dayEvents[$i] = $bitEvent; + if (!$gBitSystem->isFeatureActive('calendar_ajax_popups')) { + $gBitSmarty->assign( 'cellHash', $bitEvent ); + $dayEvents[$i]["over"] = $gBitSmarty->fetch( "bitpackage:calendar/calendar_box.tpl" ); + } + + // populate $calDay array with events + if( !empty ( $bitEvent ) && $pDateHash['view_mode'] == 'day' ) { + foreach( $calDay as $key => $t ) { + // special case - last item entry in array - check this first + + if( $bitEvent['timestamp'] >= $calDay[$key]['time'] && empty( $calDay[$key + 1]['time'] ) ) { + $calDay[$key]['items'][] = $dayEvents[$i]; + } elseif( $bitEvent['timestamp'] >= $calDay[$key]['time'] && $bitEvent['timestamp'] < $calDay[$key + 1]['time'] ) { + $calDay[$key]['items'][] = $dayEvents[$i]; + } + } + } + + $i++; + } + } + if( !empty( $dayEvents ) ) { + $calMonth[$w][$d]['items'] = array_values( $dayEvents ); + } + } + } + $gBitSmarty->assign( 'calDay', $calDay ); + $gBitSmarty->assign( 'calMonth', $calMonth ); + } + + function setupCalendar($pShowContentOptions = TRUE) { + global $gBitThemes, $gBitSmarty, $gBitSystem; + if ( $pShowContentOptions ) { + $this->setupContentTypes(); + } + $this->setupDayNames(); + + if ($gBitSystem->isFeatureActive('calendar_ajax_popups')) { + $gBitThemes->loadAjax( 'mochikit' ); + } + + // TODO: make this a pref + $gBitSmarty->assign( 'trunc', $gBitSystem->getConfig( 'title_truncate', 32 ) ); + } + + // Display the actual calendar doing any other work required for the template + function display($pTitle, $pShowContentOptions = TRUE, $pBaseUrl=NULL) { + global $gBitSystem, $gBitSmarty; + + $this->setupCalendar($pShowContentOptions); + + // A default base for the calendar + if( empty($pBaseUrl) ){ + $pBaseUrl = CALENDAR_PKG_URL.'index.php'; + } + // Asssign it so templates see it. + $gBitSmarty->assign('baseCalendarUrl', $pBaseUrl); + + $gBitSystem->display( 'bitpackage:calendar/calendar.tpl', $pTitle , [ 'display_mode' => 'display' ]); + + } +} |
