summaryrefslogtreecommitdiff
path: root/includes/classes/Calendar.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/classes/Calendar.php')
-rw-r--r--includes/classes/Calendar.php483
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' ]);
+
+ }
+}