<?php
/**
* @brief Calendar Views
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @subpackage Calendar
* @since 23 Dec 2013
*/
namespace IPS\calendar\modules\front\calendar;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Calendar Views
*/
class _view extends \IPS\Dispatcher\Controller
{
/**
* @brief Calendar we are viewing
*/
protected $_calendar = NULL;
/**
* @brief Date object for the current day
*/
protected $_today = NULL;
/**
* Route
*
* @return void
*/
protected function manage()
{
/* We aren't showing a sidebar in Calendar */
\IPS\Output::i()->sidebar['enabled'] = FALSE;
\IPS\calendar\Calendar::addCss();
/* Show the RSS link */
if ( \IPS\Settings::i()->calendar_rss_feed )
{
$urls = $this->_downloadLinks();
\IPS\Output::i()->rssFeeds['calendar_rss_title'] = $urls['rss'];
}
/* Is there only one calendar? */
$roots = \IPS\Settings::i()->club_nodes_in_apps ? \IPS\calendar\Calendar::rootsWithClubs() : \IPS\calendar\Calendar::roots();
if ( count( $roots ) == 1 AND !isset( \IPS\Request::i()->id ) )
{
$roots = array_shift( $roots );
$this->_calendar = \IPS\calendar\Calendar::loadAndCheckPerms( $roots->_id );
}
/* Are we viewing a specific calendar only? */
if( \IPS\Request::i()->id )
{
try
{
$this->_calendar = \IPS\calendar\Calendar::loadAndCheckPerms( \IPS\Request::i()->id );
}
catch( \OutOfRangeException $e )
{
\IPS\Output::i()->error( 'node_error', '2L182/2', 404, '' );
}
if ( $club = $this->_calendar->club() )
{
\IPS\core\FrontNavigation::$clubTabActive = TRUE;
\IPS\Output::i()->breadcrumb = array();
\IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs') );
\IPS\Output::i()->breadcrumb[] = array( $club->url(), $club->name );
\IPS\Output::i()->breadcrumb[] = array( $this->_calendar->url(), $this->_calendar->_title );
if ( \IPS\Settings::i()->clubs_header == 'sidebar' )
{
\IPS\Output::i()->sidebar['contextual'] = \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->header( $club, $this->_calendar, 'sidebar' );
}
}
}
if( $this->_calendar !== NULL AND $this->_calendar->_id )
{
\IPS\Output::i()->contextualSearchOptions[ \IPS\Member::loggedIn()->language()->addToStack( 'search_contextual_item_calendars' ) ] = array( 'type' => 'calendar_event', 'nodes' => $this->_calendar->_id );
}
$this->_today = \IPS\calendar\Date::getDate();
/* Get the date jumper - do this first in case we need to redirect */
$jump = $this->_jump();
if( !\IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_browse.js', 'calendar', 'front' ) );
}
/* If there is a view requested in the URL, use it */
if( isset( \IPS\Request::i()->view ) )
{
if( method_exists( $this, '_view' . ucwords( \IPS\Request::i()->view ) ) )
{
$method = "_view" . ucwords( \IPS\Request::i()->view );
$this->$method( $jump );
}
else
{
$method = "_view" . ucwords( \IPS\Settings::i()->calendar_default_view );
$this->$method( $jump );
}
}
/* Otherwise use ACP default preference */
else
{
$method = "_view" . ucwords( \IPS\Settings::i()->calendar_default_view );
$this->$method( $jump, iterator_to_array( \IPS\calendar\Event::featured( 4, '_rand' ) ) );
}
/* Online User Location */
if ( $this->_calendar )
{
\IPS\Session::i()->setLocation( $this->_calendar->url(), array(), 'loc_calendar_viewing_calendar', array( "calendar_calendar_{$this->_calendar->id}" => TRUE ) );
}
else
{
\IPS\Session::i()->setLocation( \IPS\Http\Url::internal( 'app=calendar', 'front', 'calendar' ), array(), 'loc_calendar_viewing_calendar_all' );
}
}
/**
* Show month view
*
* @param \IPS\Helpers\Form $jump Calendar jump
* @param array $featured Featured events (only populated for the ACP-specified default view)
* @return void
*/
protected function _viewMonth( $jump, $featured=array() )
{
/* Get the calendars we can view */
$calendars = \IPS\Settings::i()->club_nodes_in_apps ? \IPS\calendar\Calendar::rootsWithClubs() : \IPS\calendar\Calendar::roots();
/* Get the month data */
$day = NULL;
if( ( !\IPS\Request::i()->y OR \IPS\Request::i()->y == $this->_today->year ) AND ( !\IPS\Request::i()->m OR \IPS\Request::i()->m == $this->_today->mon ) )
{
$day = $this->_today->mday;
}
try
{
$date = \IPS\calendar\Date::getDate( \IPS\Request::i()->y ?: NULL, \IPS\Request::i()->m ?: NULL, $day );
/* Get birthdays */
$birthdays = ( $this->_calendar === NULL OR count( $calendars ) == 1 ) ? $date->getBirthdays( FALSE, TRUE ) : array();
/* Get the events within this range */
$events = \IPS\calendar\Event::retrieveEvents(
\IPS\calendar\Date::getDate( $date->firstDayOfMonth('year'), $date->firstDayOfMonth('mon'), $date->firstDayOfMonth('mday') ),
\IPS\calendar\Date::getDate( $date->lastDayOfMonth('year'), $date->lastDayOfMonth('mon'), $date->lastDayOfMonth('mday'), 23, 59, 59 ),
$this->_calendar
);
}
catch( \Exception $e )
{
\IPS\Output::i()->error( 'error_bad_date', '2L182/7', 403, '' );
}
/* If there are no events, tell search engines not to index the page but do NOT tell them not to follow links */
if( count($events) === 0 )
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
}
/* Display */
$output = \IPS\Theme::i()->getTemplate( 'browse' )->calendarMonth( $calendars, $date, $featured, $birthdays, $events, $this->_today, $this->_calendar, $jump );
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( $output, 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
else
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('cal_month_title', FALSE, array( 'sprintf' => array( $date->monthName, $date->year ) ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'browse' )->calendarWrapper(
$output,
$calendars,
$this->_calendar,
$jump,
$date,
$featured,
$this->_downloadLinks()
);
}
}
/**
* Show week view
*
* @param \IPS\Helpers\Form $jump Calendar jump
* @param array $featured Featured events (only populated for the ACP-specified default view)
* @return void
*/
protected function _viewWeek( $jump, $featured=array() )
{
/* Get the calendars we can view */
$calendars = \IPS\Settings::i()->club_nodes_in_apps ? \IPS\calendar\Calendar::rootsWithClubs() : \IPS\calendar\Calendar::roots();
/* Get the month data */
$week = \IPS\Request::i()->w ? explode( '-', \IPS\Request::i()->w ) : NULL;
try
{
$date = \IPS\calendar\Date::getDate( isset( $week[0] ) ? $week[0] : NULL, isset( $week[1] ) ? $week[1] : NULL, isset( $week[2] ) ? $week[2] : NULL );
}
catch( \Exception $e )
{
\IPS\Output::i()->error( 'error_bad_date', '2L182/8', 403, '' );
}
$nextWeek = $date->adjust( '+1 week' );
$lastWeek = $date->adjust( '-1 week' );
/* Get the days of the week - we do this in PHP to help keep template a little cleaner */
try
{
$days = array();
for( $i = 0; $i < 7; $i++ )
{
$days[] = \IPS\calendar\Date::getDate( $date->firstDayOfWeek('year'), $date->firstDayOfWeek('mon'), $date->firstDayOfWeek('mday') )->adjust( $i . ' days' );
}
/* Get birthdays */
$birthdays = ( $this->_calendar === NULL OR count( $calendars ) == 1 ) ? $date->getBirthdays( FALSE, TRUE ) : array();
/* Get the events within this range */
$events = \IPS\calendar\Event::retrieveEvents(
\IPS\calendar\Date::getDate( $date->firstDayOfWeek('year'), $date->firstDayOfWeek('mon'), $date->firstDayOfWeek('mday') ),
\IPS\calendar\Date::getDate( $date->lastDayOfWeek('year'), $date->lastDayOfWeek('mon'), $date->lastDayOfWeek('mday'), 23, 59, 59 ),
$this->_calendar
);
}
catch( \Exception $e )
{
\IPS\Output::i()->error( 'error_bad_date', '2L182/C', 403, '' );
}
/* If there are no events, tell search engines not to index the page but do NOT tell them not to follow links */
if( count( $events ) === 0 )
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
}
/* Display */
$output = \IPS\Theme::i()->getTemplate( 'browse' )->calendarWeek( $calendars, $date, $featured, $birthdays, $events, $nextWeek, $lastWeek, $days, $this->_today, $this->_calendar, $jump );
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( $output, 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
else
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('cal_week_title', FALSE, array( 'sprintf' => array(
$date->firstDayOfWeek('monthNameShort'),
$date->firstDayOfWeek('mday'),
$date->firstDayOfWeek('year'),
$date->lastDayOfWeek('monthNameShort'),
$date->lastDayOfWeek('mday'),
$date->lastDayOfWeek('year')
) ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'browse' )->calendarWrapper(
$output,
$calendars,
$this->_calendar,
$jump,
$date,
$featured,
$this->_downloadLinks()
);
}
}
/**
* Show day view
*
* @param \IPS\Helpers\Form $jump Calendar jump
* @param array $featured Featured events (only populated for the ACP-specified default view)
* @return void
*/
protected function _viewDay( $jump, $featured=array() )
{
/* Get the calendars we can view */
$calendars = \IPS\Settings::i()->club_nodes_in_apps ? \IPS\calendar\Calendar::rootsWithClubs() : \IPS\calendar\Calendar::roots();
/* Get the month data */
try
{
$date = \IPS\calendar\Date::getDate( \IPS\Request::i()->y ?: NULL, \IPS\Request::i()->m ?: NULL, \IPS\Request::i()->d ?: NULL );
}
catch( \Exception $e )
{
\IPS\Output::i()->error( 'error_bad_date', '2L182/9', 403, '' );
}
$tomorrow = $date->adjust( '+1 day' );
$yesterday = $date->adjust( '-1 day' );
/* Get birthdays */
if( isset( \IPS\Request::i()->show ) AND \IPS\Request::i()->show == 'birthdays' AND \IPS\Settings::i()->show_bday_calendar )
{
$table = new \IPS\Helpers\Table\Db( 'core_members', \IPS\Request::i()->url()->setQueryString('show', 'birthdays'), $date->getBirthdaysWhere( TRUE ) );
$table->sortBy = 'name';
$table->sortDirection = 'asc';
$table->tableTemplate = array( \IPS\Theme::i()->getTemplate( 'browse', 'calendar', 'front' ), 'birthdaysTable' );
$table->rowsTemplate = array( \IPS\Theme::i()->getTemplate( 'browse', 'calendar', 'front' ), 'birthday' );
\IPS\Output::i()->output = $table;
\IPS\Output::i()->metaTags['robots'] = 'noindex';
return;
}
$birthdayCount = ( $this->_calendar === NULL OR count( $calendars ) == 1 ) ? $date->getBirthdays( TRUE, TRUE ) : array();
$birthdays = ( $this->_calendar === NULL OR count( $calendars ) == 1 ) ? $date->getBirthdays( TRUE, FALSE ) : array();
/* Get the events within this range */
$events = \IPS\calendar\Event::retrieveEvents( $date, $date, $this->_calendar );
/* If there are no events, tell search engines not to index the page but do NOT tell them not to follow links */
if( count($events) === 0 )
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
}
$dayEvents = array_fill( 0, 23, array() );
$dayEvents['allDay'] = array();
$dayEvents['count'] = 0;
foreach( $events as $day => $_events )
{
foreach( $_events as $type => $event )
{
foreach( $event as $_event )
{
$dayEvents['count']++;
if( $_event->all_day )
{
$dayEvents['allDay'][ $_event->id ] = $_event;
}
else
{
$dayEvents[ $_event->_start_date->hours ][ $_event->id ] = $_event;
}
}
}
}
/* If there are no events, tell search engines not to index the page but do NOT tell them not to follow links */
if( $dayEvents['count'] === 0 )
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
}
/* Display */
$output = \IPS\Theme::i()->getTemplate( 'browse' )->calendarDay( $calendars, $date, $featured, $birthdays, $birthdayCount, $dayEvents, $tomorrow, $yesterday, $this->_today, $this->_calendar, $jump );
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( $output, 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
else
{
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('cal_month_day', FALSE, array( 'sprintf' => array( $date->monthName, $date->mday, $date->year ) ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'browse' )->calendarWrapper(
$output,
$calendars,
$this->_calendar,
$jump,
$date,
$featured,
$this->_downloadLinks()
);
}
}
/**
* @brief Stream per page
*/
public $streamPerPage = 50;
/**
* Show stream view
*
* @param \IPS\Helpers\Form $jump Calendar jump
* @param array $featured Featured events (only populated for the ACP-specified default view)
* @return void
*/
protected function _viewStream( $jump, $featured=array() )
{
/* Get the calendars we can view */
$calendars = \IPS\Settings::i()->club_nodes_in_apps ? \IPS\calendar\Calendar::rootsWithClubs() : \IPS\calendar\Calendar::roots();
/* Get the month data */
$day = NULL;
if( ( !\IPS\Request::i()->y OR \IPS\Request::i()->y == $this->_today->year ) AND ( !\IPS\Request::i()->m OR \IPS\Request::i()->m == $this->_today->mon ) )
{
$day = $this->_today->mday;
}
try
{
$date = \IPS\calendar\Date::getDate( \IPS\Request::i()->y ?: NULL, \IPS\Request::i()->m ?: NULL, $day );
/* Get the events within this range */
$events = \IPS\calendar\Event::retrieveEvents(
\IPS\calendar\Date::getDate( $date->firstDayOfMonth('year'), $date->firstDayOfMonth('mon'), $date->firstDayOfMonth('mday') ),
\IPS\calendar\Date::getDate( $date->lastDayOfMonth('year'), $date->lastDayOfMonth('mon'), $date->lastDayOfMonth('mday'), 23, 59, 59 ),
$this->_calendar,
NULL,
FALSE
);
}
catch( \InvalidArgumentException $e )
{
\IPS\Output::i()->error( 'error_bad_date', '2L182/A', 403, '' );
}
/* Sort */
$startDate = \IPS\calendar\Date::getDate();
@usort( $events, function( $a, $b ) use ( $startDate )
{
if( $a->nextOccurrence( $startDate, 'startDate' ) === NULL )
{
return -1;
}
if( $b->nextOccurrence( $startDate, 'startDate' ) === NULL )
{
return 1;
}
if ( $a->nextOccurrence( $startDate, 'startDate' )->mysqlDatetime() == $b->nextOccurrence( $startDate, 'startDate' )->mysqlDatetime() )
{
return 0;
}
return ( $a->nextOccurrence( $startDate, 'startDate' )->mysqlDatetime() < $b->nextOccurrence( $startDate, 'startDate' )->mysqlDatetime() ) ? -1 : 1;
} );
/* Pagination */
$pagination = array(
'page' => ( isset( \IPS\Request::i()->page ) ) ? \IPS\Request::i()->page : 1,
'pages' => ( count( $events ) > 0 ) ? ceil( count( $events ) / $this->streamPerPage ) : 1,
'limit' => $this->streamPerPage
);
if( $pagination['page'] < 1 )
{
$pagination['page'] = 1;
}
/* If there are no events, tell search engines not to index the page but do NOT tell them not to follow links */
if( count( $events ) === 0 )
{
\IPS\Output::i()->metaTags['robots'] = 'noindex';
}
else
{
$events = array_slice( $events, ( $pagination['page'] - 1 ) * $this->streamPerPage, $this->streamPerPage );
}
/* Non-existent page */
if( $pagination['page'] > 1 AND !count( $events ) )
{
\IPS\Output::i()->error( 'no_events_month', '2L182/B', 404, '' );
}
/* Display */
$output = \IPS\Theme::i()->getTemplate( 'browse' )->calendarStream( $calendars, $date, $featured, $events, $this->_calendar, $jump, $pagination );
if( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( $output, 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
else
{
/* We can show the sidebar in stream view */
\IPS\Output::i()->sidebar['enabled'] = TRUE;
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'cal_month_stream_title', FALSE, array( 'sprintf' => array( $date->monthName, $date->year ) ) );
\IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'browse' )->calendarWrapper(
$output,
$calendars,
$this->_calendar,
$jump,
$date,
$featured,
$this->_downloadLinks()
);
}
}
/**
* Generate keyed links for RSS/iCal download
*
* @return array
*/
protected function _downloadLinks()
{
$downloadLinks = array( 'iCalCalendar' => '', 'iCalAll' => \IPS\Http\Url::internal( 'app=calendar&module=calendar&controller=view&do=download', 'front', 'calendar_icaldownload' ), 'rss' => \IPS\Http\Url::internal( 'app=calendar&module=calendar&controller=view&do=rss', 'front', 'calendar_rss' ) );
if( $this->_calendar )
{
$downloadLinks['iCalCalendar'] = \IPS\Http\Url::internal( 'app=calendar&module=calendar&controller=view&id=' . $this->_calendar->id . '&do=download', 'front', 'calendar_calicaldownload', $this->_calendar->title_seo );
}
if ( \IPS\Member::loggedIn()->member_id )
{
$key = md5( ( \IPS\Member::loggedIn()->members_pass_hash ?: \IPS\Member::loggedIn()->email ) . \IPS\Member::loggedIn()->members_pass_salt );
if( $this->_calendar )
{
$downloadLinks['iCalCalendar'] = $downloadLinks['iCalCalendar']->setQueryString( array( 'member' => \IPS\Member::loggedIn()->member_id , 'key' => $key ) );
}
$downloadLinks['iCalAll'] = $downloadLinks['iCalAll']->setQueryString( array( 'member' => \IPS\Member::loggedIn()->member_id , 'key' => $key ) );
$downloadLinks['rss'] = $downloadLinks['rss']->setQueryString( array( 'member' => \IPS\Member::loggedIn()->member_id , 'key' => $key ) );
}
return $downloadLinks;
}
/**
* Return jump form and redirect if appropriate
*
* @return void
*/
protected function _jump()
{
/* Build the form */
$form = new \IPS\Helpers\Form;
$form->add( new \IPS\Helpers\Form\Date( 'jump_to', \IPS\DateTime::create(), TRUE, array(), NULL, NULL, NULL, 'jump_to' ) );
if( $values = $form->values() )
{
if( \IPS\Request::i()->goto )
{
$dateToGoTo = \IPS\DateTime::create();
}
else
{
$dateToGoTo = $values['jump_to'];
}
if ( $this->_calendar )
{
$url = \IPS\Http\Url::internal( "app=calendar&module=calendar&controller=view&view=day&id={$this->_calendar->_id}&y={$dateToGoTo->format('Y')}&m={$dateToGoTo->format('m')}&d={$dateToGoTo->format('j')}", 'front', 'calendar_calday', $this->_calendar->title_seo );
}
else
{
$url = \IPS\Http\Url::internal( "app=calendar&module=calendar&controller=view&view=day&y={$dateToGoTo->format('Y')}&m={$dateToGoTo->format('m')}&d={$dateToGoTo->format('j')}", 'front', 'calendar_day' );
}
\IPS\Output::i()->redirect( $url );
}
return $form;
}
}