* @link https://TheTempusProject.com * @license https://opensource.org/licenses/MIT [MIT LICENSE] */ namespace TheTempusProject\Models; use TheTempusProject\Bedrock\Classes\Config; use TheTempusProject\Bedrock\Functions\Check; use TheTempusProject\Canary\Bin\Canary as Debug; use TheTempusProject\Classes\DatabaseModel; use TheTempusProject\TheTempusProject as App; use TheTempusProject\Houdini\Classes\Filters; use TheTempusProject\Models\Calendars; use TheTempusProject\Bedrock\Classes\CustomException; use TheTempusProject\Bedrock\Functions\Date; use TheTempusProject\Houdini\Classes\Pagination; class Events extends DatabaseModel { public $tableName = 'events'; public $repeatOptions = [ 'Does Not Repeat' => 'none', 'Every Day' => 'daily', 'Every Week' => 'weekly', 'Every Month' => 'monthly', 'Every Year' => 'yearly', ]; public $databaseMatrix = [ [ 'calendar_id', 'int', '11' ], [ 'title', 'varchar', '256' ], [ 'event_time', 'int', '11' ], [ 'event_ends', 'int', '11' ], [ 'description', 'text', '' ], [ 'location', 'varchar', '256' ], [ 'repeats', 'varchar', '48' ], [ 'color', 'varchar', '48' ], [ 'parent_id', 'int', '11' ], [ 'createdBy', 'int', '11' ], [ 'createdAt', 'int', '11' ], ]; protected static $calendars; /** * The model constructor. */ public function __construct() { parent::__construct(); self::$calendars = new Calendars; } /** * Saves a chat form to the db. * * @param string $message -contents of the chat form. * @return bool */ public function create( $calendar_id, $title, $event_time, $description = '', $location = '', $repeats = 'none', $color = 'default', $parent_id = 0 ) { if ( ! Check::id( $calendar_id ) ) { Debug::info( 'calendar event: illegal calendar ID.' ); return false; } $fields = [ 'calendar_id' => $calendar_id, 'title' => $title, 'event_time' => $event_time, 'description' => $description, 'location' => $location, 'repeats' => $repeats, 'color' => $color, 'parent_id' => $parent_id, 'createdBy' => App::$activeUser->ID, 'createdAt' => time(), ]; if ( !self::$db->insert( $this->tableName, $fields ) ) { Debug::info( 'Events::create - failed to insert to db' ); return false; } return self::$db->lastId(); } public function update( $id, $title, $event_time, $description = '', $location ='', $repeats = 'none', $color = 'default', $parent_id = 0, $calendar_id = '' ) { if ( ! Check::id( $id ) ) { Debug::info( 'calendar event: illegal ID.' ); return false; } $fields = [ 'title' => $title, 'event_time' => $event_time, 'description' => $description, 'location' => $location, 'repeats' => $repeats, 'color' => $color, 'parent_id' => $parent_id, ]; if ( ! self::$db->update( $this->tableName, $id, $fields ) ) { new CustomException( 'calendarEventUpdate' ); Debug::error( "Event: $id not updated: $fields" ); return false; } return true; } public function dayArray( $calendar_id = 0, $date = 0 ) { $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; if ( ! empty( $calendar_id ) ) { $whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id, 'AND' ] ); } $whereClause = array_merge( $whereClause, [ 'event_time', '>=', Date::getDayStartTimestamp( $date, true ), 'AND', 'event_time', '<=', Date::getDayEndTimestamp( $date, true ), ] ); $events = self::$db->get( $this->tableName, $whereClause ); if ( ! $events->count() ) { $results = []; } else { $results = $events->results(); } $events = $this->sortByEventTimeHour( $this->filter( $results ) ); // Generate day array $currentDay = []; $currentHour = Date::getDayStartTimestamp( $date ); $lastHour = Date::getDayEndTimestamp( $date ); while ( $currentHour <= $lastHour ) { $hour = date( 'H', $currentHour ); if ( ! empty( $events[ $hour ] ) ) { $dailyEvents = $events[ $hour ]; } else { $dailyEvents = []; } $currentDay[ $hour ] = $dailyEvents; $currentHour = strtotime('+1 hour', $currentHour); } return $currentDay; } public function weekArray( $calendar_id = 0, $date = null ) { $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; if ( ! empty( $calendar_id ) ) { $whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id, 'AND' ] ); } $whereClause = array_merge( $whereClause, [ 'event_time', '>=', Date::getWeekStartTimestamp( $date, true ), 'AND', 'event_time', '<=', Date::getWeekEndTimestamp( $date, true ), ] ); $events = self::$db->get( $this->tableName, $whereClause ); if ( ! $events->count() ) { $results = []; } else { $results = $events->results(); } $events = $this->sortByEventTime( $this->filter( $results ), ); // Generate weeks array $output = []; $currentWeek = []; $currentDay = Date::getWeekStartTimestamp( $date ); $weekCounter = 1; $dayCounter = 1; // Re-index the array using the date derived from event_time as the key while ( $currentDay <= Date::getWeekEndTimestamp( $date ) ) { $month = date( 'F', $currentDay ); $dayOfMonth = date( 'd', $currentDay ); if ( ! empty( $events[ $month ][ $dayOfMonth ] ) ) { $dailyEvents = $events[ $month ][ $dayOfMonth ]; } else { $dailyEvents = []; } $currentWeek[ $dayOfMonth ] = $dailyEvents; $currentDay = strtotime('+1 day', $currentDay); } // Handle any remaining days in the last week if ( ! empty( $currentWeek ) ) { $output["week$weekCounter"] = $currentWeek; } return $output; } public function monthArray( $calendar_id = 0, $date = null ) { $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; if ( ! empty( $calendar_id ) ) { $whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id, 'AND' ] ); } $whereClause = array_merge( $whereClause, [ 'event_time', '>=', Date::getMonthStartTimestamp( $date, true ), 'AND', 'event_time', '<=', Date::getMonthEndTimestamp( $date, true ), ] ); $events = self::$db->get( $this->tableName, $whereClause ); if ( ! $events->count() ) { $results = []; } else { $results = $events->results(); } $events = $this->sortByEventTime( $this->filter( $results ), ); // Generate weeks array $output = []; $currentWeek = []; $currentDay = Date::getMonthStartTimestamp( $date ); $weekCounter = 1; $dayCounter = 1; // Re-index the array using the date derived from event_time as the key while ( $currentDay <= Date::getMonthEndTimestamp( $date ) ) { $month = date( 'F', $currentDay ); $dayOfMonth = date( 'd', $currentDay ); if ( ! empty( $events[ $month ][ $dayOfMonth ] ) ) { $dailyEvents = $events[ $month ][ $dayOfMonth ]; } else { $dailyEvents = []; } $currentWeek[$dayOfMonth] = $dailyEvents; if (count($currentWeek) == 7) { $output["week$weekCounter"] = $currentWeek; $currentWeek = []; $weekCounter++; } $currentDay = strtotime('+1 day', $currentDay); } // Handle any remaining days in the last week if ( ! empty( $currentWeek ) ) { $output["week$weekCounter"] = $currentWeek; } while ( $weekCounter < 7 ) { $output["week$weekCounter"] = []; $weekCounter++; } return $output; } public function yearArray( $calendar_id = 0, $date = null ) { if ( empty( $date ) ) { $date = time(); } $year = date( 'Y', $date ); $firstDayUnix = date( 'U', strtotime( "Jan 1, $year" ) ); $lastDayUnix = date( 'U', strtotime( "December 31, $year" ) ); $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; if ( ! empty( $calendar_id ) ) { $whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id, 'AND' ] ); } $whereClause = array_merge( $whereClause, [ 'event_time', '<=', $lastDayUnix, 'AND', 'event_time', '>=', $firstDayUnix, ] ); $events = self::$db->get( $this->tableName, $whereClause ); if ( ! $events->count() ) { Debug::info( 'No ' . $this->tableName . ' data found.' ); return []; } return $this->filter( $events->results() ); } public function compareEventTime($a, $b) { return $a->event_time - $b->event_time; } public function sortByEventTimeHour( $results ) { usort( $results, [ $this,'compareEventTime' ] ); $sortedResults = array(); foreach ( $results as $result ) { $date = new \DateTime(); $date->setTimestamp( $result->event_time ); $timezone = new \DateTimeZone( Date::getTimezone() ); $date->setTimezone( $timezone ); $dateKey = $date->format('H'); $sortedResults[ $dateKey ][] = $result; } return $sortedResults; } public function sortByEventTime( $results ) { usort( $results, [ $this,'compareEventTime' ] ); $sortedResults = array(); foreach ( $results as $result ) { $date = new \DateTime(); $date->setTimestamp( $result->event_time ); $timezone = new \DateTimeZone( Date::getTimezone() ); $date->setTimezone( $timezone ); $month = $date->format('F'); $dateKey = $date->format('d'); if ( ! isset( $sortedResults[ $month ] ) ) { $sortedResults[ $month ] = []; } if ( ! isset( $sortedResults[ $month ][ $dateKey ] ) ) { $sortedResults[ $month ][ $dateKey ] = []; } $sortedResults[ $month ][ $dateKey ][] = $result; } return $sortedResults; } public function findByCalendar( $id ) { $calendar = $this->verifyCalendar( $id ); if ( empty( $calendar ) ) { return []; } $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; $whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $id ] ); $events = self::$db->getPaginated( $this->tableName, $whereClause ); if ( ! $events->count() ) { Debug::info( 'No ' . $this->tableName . ' data found.' ); return []; } return $this->filter( $events->results() ); } public function userEvents( $limit = 0 ) { $whereClause = [ 'createdBy', '=', App::$activeUser->ID ]; $events = self::$db->getPaginated( $this->tableName, $whereClause ); if ( ! $events->count() ) { Debug::info( 'No ' . $this->tableName . ' data found.' ); return []; } return $this->filter( $events->results() ); } public function filter( $data, $params = [] ) { $out = []; foreach ( $data as $instance ) { if ( !is_object( $instance ) ) { $instance = $data; $end = true; } $instance->repeatsText = array_search('none', $this->repeatOptions); $instance->calendarName = self::$calendars->getName( $instance->calendar_id ); if ( empty( $instance->color ) || $instance->color == 'default' || $instance->color == 'none') { $instance->displayColor = self::$calendars->getColor( $instance->calendar_id ); } else { $instance->displayColor = $instance->color; } $out[] = $instance; if ( !empty( $end ) ) { $out = $out[0]; break; } } return $out; } public function today( $limit = 0 ) { $date = time(); $day = date('d', $date); $month = date('M', $date); $year = date( 'Y', $date ); $firstDayUnix = date( 'U', strtotime( "00:00:00 $day $month $year" ) ); $lastDayUnix = date( 'U', strtotime( "23:59:59 $day $month $year" ) ); $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; $whereClause = array_merge( $whereClause, [ 'event_time', '<=', $lastDayUnix, 'AND', 'event_time', '>=', $firstDayUnix, ] ); if ( empty( $limit ) ) { $events = self::$db->get( $this->tableName, $whereClause ); } else { $events = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] ); } if ( ! $events->count() ) { Debug::info( 'No ' . $this->tableName . ' data found.' ); return []; } return $this->filter( $events->results() ); } private function verifyCalendar( $calendar_id ) { if ( ! Check::id( $calendar_id ) ) { Debug::info( 'Invalid Calendar ID' ); return false; } $calendar = self::$calendars->findById( $calendar_id ); if ( $calendar == false ) { Debug::info( 'Calendar not found' ); return false; } if ( $calendar->createdBy != App::$activeUser->ID ) { Debug::info( 'You do not have permission to view this calendar' ); return false; } return $calendar; } public function deleteByCalendar( $calendar_id ) { $calendar = $this->verifyCalendar( $calendar_id ); if ( empty( $calendar ) ) { return []; } $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; $whereClause = array_merge( $whereClause, [ 'calendar_id', '=', $calendar_id ] ); $events = self::$db->get( $this->tableName, $whereClause ); if ( ! $events->count() ) { Debug::info( 'No ' . $this->tableName . ' data found.' ); return []; } foreach( $events->results() as $event ) { $this->deleteByParent( $event->ID ); } return true; } public function deleteByParent( $event_id ) { $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; $whereClause = array_merge( $whereClause, [ 'parent_id', '=', $event_id ] ); $events = self::$db->get( $this->tableName, $whereClause ); if ( ! $events->count() ) { Debug::info( 'No ' . $this->tableName . ' data found.' ); return []; } foreach( $events->results() as $event ) { $this->delete( $event->ID ); } // need to delete all child events accordingly return true; } public function delete( $idArray ) { if ( !is_array( $idArray ) ) { $idArray = [ $idArray ]; } return parent::delete( $idArray ); } public function generateChildEvents( $event_id, $remove_existing_children = true, $remove_history = false ) { $event = self::findById( $event_id ); if (empty($event)) { return false; } if ($event->parent_id != 0) { return false; } if (!in_array($event->repeats, $this->repeatOptions)) { return false; } $startDate = time(); $whereClause = [ 'parent_id', '=', $event_id, ]; if ( $remove_history && ! $remove_existing_children) { $whereClause = array_merge( $whereClause, [ 'AND', 'event_time', '<=', $startDate ] ); } elseif ( $remove_existing_children && ! $remove_history ) { $whereClause = array_merge( $whereClause, [ 'AND', 'event_time', '>=', $startDate ] ); } elseif ( !$remove_existing_children && !$remove_history ) { return false; } self::$db->delete($this->tableName, $whereClause); switch ($event->repeats) { case 'daily': return $this->generateEvents( $event, 'P90D' ); case 'weekly': return $this->generateEvents( $event, 'P1W' ); case 'monthly': return $this->generateEvents( $event, 'P1M' ); case 'yearly': return $this->generateEvents( $event, 'P1Y' ); default: return false; } } private function generateEvents( $event, $interval, $years = 2 ) { $endDate = strtotime('+'.$years.' years'); $interval = new \DateInterval( $interval ); $period = new \DatePeriod(new \DateTime('@' . $event->event_time), $interval, new \DateTime('@' . $endDate)); foreach ($period as $date) { if ( $date->getTimestamp() <= time() ) { continue; } $result = $this->create( $event->calendar_id, $event->title, $date->getTimestamp(), $event->description, $event->location, $event->repeats, $event->color, $event->ID, ); } } }