diff --git a/CREDITS b/CREDITS index 518f7366c..826799c99 100644 --- a/CREDITS +++ b/CREDITS @@ -2,9 +2,35 @@ CREDITS ======= +Version 2.5.1 + +Albert Santoni (albert.santoni@sourcefabric.org) + Role: Developer Team Lead + +Denise Rigato (denise.rigato@sourcefabric.org) + Role: Software Developer + +Naomi Aro (naomi.aro@sourcefabric.org) + Role: Software Developer + +Cliff Wang (cliff.wang@sourcefabric.org) + Role: QA + +Daniel James (daniel.james@sourcefabric.org) + Role: Documentor & QA + + +Community Contributors: + +John Chewter + + Version 2.5.0 ------------- +Albert Santoni (albert.santoni@sourcefabric.org) + Role: Developer Team Lead + Denise Rigato (denise.rigato@sourcefabric.org) Role: Software Developer diff --git a/README b/README index 4b5b7c46a..3b4f0147f 100644 --- a/README +++ b/README @@ -1,40 +1,50 @@ ========================================================================= -========================== AIRTIME ================================= +================= Airtime - Live Broadcast Together ================= ========================================================================= -Airtime is an open source application that provides remote automation -of a radio station. -Home page: http://airtime.sourcefabric.org/ +Airtime is an open source application that provides remote and +collaborative automation of a broadcast station. + +Home page: http://www.sourcefabric.org/en/airtime/ Major features: - * Web-based remote station management. Authorized personnel can add - program material, create playlists and schedule programming all via - a web interface. - * Automation. Airtime has a scheduler function that enables users to - set shows with playlists for playback at a date and time of their choosing. - Playlists can be played back multiple times. - * Solid, fast playback. Airtime uses the open source Liquidsoap - multimedia framework for clean, reliable, fast playback. + + * Web-based remote station management. Authorized personnel can add + programme material, create playlists or smart blocks, and stream in live, + all via a web interface. + * Automation. Airtime has a scheduler function that enables users to + create shows with content for playback at the exact date and time specified. + Playlists, smart blocks and remote stream URLs can be used multiple times. + * Solid playout. Airtime uses the open source Liquidsoap streaming language + for reliable and precise playback to multiple outputs. * Open, extensible architecture. Stations are free to extend and alter - all parts of the program code. + all parts of the program code, under the GNU GPLv3 license. INSTALLATION ------------ -Please see this page for a typical user installation: -http://www.sourcefabric.org/en/products/airtime_manuals/ + +For the fastest, most painless installation on Ubuntu 12.04 and up, run: + + cd install_full/ubuntu + sudo ./airtime-full-install + +For Debian, run: + + cd install_full/debian + sudo ./airtime-full-install + +For custom installation and other distributions of Linux, please see this +chapter to begin a typical installation: +http://sourcefabric.booktype.pro/airtime-24-for-broadcasters/preparing-the-server/ If you are a developer, please see this page: http://wiki.sourcefabric.org/display/CC/Airtime+Dev+Site Quick links to our resources ---------------------------- - User Manual: http://www.sourcefabric.org/en/airtime/manuals/ - Forums and mailing lists: http://forum.sourcefabric.org - Bug Tracking: http://dev.sourcefabric.org - Code view/review: http://code.sourcefabric.org - Public source code hosting: http://github.com/sourcefabric - Download link: https://sourceforge.net/projects/airtime/files/ - Developer's wiki: http://wiki.sourcefabric.org - - +User manuals: http://www.sourcefabric.org/en/airtime/manuals/ +Forums and mailing lists: http://forum.sourcefabric.org +Bug tracker: http://dev.sourcefabric.org +Source code: http://github.com/sourcefabric/Airtime +IRC chat: #airtime on Freenode diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index 02adbdc5e..c3e2a884f 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -17,10 +17,8 @@ require_once "Timezone.php"; require_once __DIR__.'/forms/helpers/ValidationTypes.php'; require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php'; -date_default_timezone_set('UTC'); require_once (APPLICATION_PATH."/logging/Logging.php"); Logging::setLogPath('/var/log/airtime/zendphp.log'); -date_default_timezone_set(Application_Model_Preference::GetTimezone()); Config::setAirtimeVersion(); require_once __DIR__."/configs/navigation.php"; @@ -102,7 +100,21 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap $view->headScript()->appendFile($baseUrl.'locale/datatables-translation-table?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendScript("$.i18n.setDictionary(general_dict)"); $view->headScript()->appendScript("var baseUrl='$baseUrl'"); - + + //These timezones are needed to adjust javascript Date objects on the client to make sense to the user's set timezone + //or the server's set timezone. + $serverTimeZone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); + $now = new DateTime("now", $serverTimeZone); + $offset = $now->format("Z") * -1; + $view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds"); + + if (class_exists("Zend_Auth", false) && Zend_Auth::getInstance()->hasIdentity()) { + $userTimeZone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + $now = new DateTime("now", $userTimeZone); + $offset = $now->format("Z") * -1; + $view->headScript()->appendScript("var userTimezoneOffset = {$offset}; //in seconds"); + } + //scripts for now playing bar $view->headScript()->appendFile($baseUrl.'js/airtime/airtime_bootstrap.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl.'js/airtime/dashboard/helperfunctions.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); diff --git a/airtime_mvc/application/common/DateHelper.php b/airtime_mvc/application/common/DateHelper.php index bbac8abc1..07ef3bd18 100644 --- a/airtime_mvc/application/common/DateHelper.php +++ b/airtime_mvc/application/common/DateHelper.php @@ -45,140 +45,94 @@ class Application_Common_DateHelper return gmdate("H:i:s", $this->_dateTime); } - /** - * Get the week start date of this week in the format - * YYYY-MM-DD - * - * @return String - week start date + /** Get the abbreviated timezone for the currently logged in user. + * @return A string containing the short form of the timezone set in the preferences for the current user (eg. EST, CEST, etc.) */ - function getWeekStartDate() + public static function getUserTimezoneAbbreviation() { + return self::getTimezoneAbbreviation(Application_Model_Preference::GetUserTimezone()); + } + + /** Get the abbreviated timezone string of the timezone the station is set to. + * @return A string containing the short form of the station's timezone (eg. EST, CEST, etc.) + */ + public static function getStationTimezoneAbbreviation() + { + return self::getTimezoneAbbreviation(Application_Model_Preference::GetDefaultTimezone()); + } + + private static function getTimezoneAbbreviation($fullTimeZoneName) + { + $timeZone = new DateTimeZone($fullTimeZoneName); + $now = new DateTime("now", $timeZone); + return $now->format("T"); + } + + public static function getUserTimezoneOffset() + { + $userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + $now = new DateTime("now", $userTimezone); + + return $now->format("Z"); + } + + public static function getStationTimezoneOffset() + { + $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); + $now = new DateTime("now", $stationTimezone); + + return $now->format("Z"); + } + + /** + * + * @return DateTime - YYYY-MM-DD 00:00 in station timezone of today + */ + public static function getTodayStationStartDateTime() + { + $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); + $now = new DateTime("now", $stationTimezone); + + $now->setTime(0, 0, 0); + + return $now; + } + + /** + * + * @return DateTime - YYYY-MM-DD 00:00 in station timezone of tomorrow + */ + public static function getTodayStationEndDateTime() + { + $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); + $now = new DateTime("now", $stationTimezone); + + $now->add(new DateInterval("P1D")); + $now->setTime(0, 0, 0); + + return $now; + } + + /** + * + * @return DateTime - YYYY-MM-DD 00:00 in station timezone + */ + public static function getWeekStartDateTime() + { + $now = self::getTodayStationStartDateTime(); + // our week starts on monday, but php week starts on sunday. - $startDate = date('w') == 0 ? date('Y-m-d', strtotime('monday last week')) : date('Y-m-d', strtotime('monday this week')); - $startDateTime = new DateTime($startDate); - return $startDateTime->format('Y-m-d H:i:s'); - } - - /** - * Set the internal timestamp of the object. - */ - function setDate($dateString) - { - $dateTime = new DateTime($dateString, new DateTimeZone("UTC")); - $this->_dateTime = $dateTime->getTimestamp(); - } - - /** - * Calculate and return the timestamp for end of day today - * in local time. - * - * For example, if local time is 2PM on 2011-11-01, - * then the function would return 2011-11-02 00:00:00 - * - * @return End of day timestamp in local timezone - */ - public static function GetDayEndTimestamp($time = "") { - $dateTime = $time == "" ? new DateTime(date("Y-m-d")) : new DateTime($time); - $dateTime->add(new DateInterval('P1D')); - return $dateTime->format('Y-m-d H:i:s'); - } - - public static function GetDayEndTimestampInUtc($time = "") { - $dayEndTimestamp = Application_Common_DateHelper::GetDayEndTimestamp($time); - return Application_Common_DateHelper::ConvertToUtcDateTimeString($dayEndTimestamp); - } - - /** - * Find the epoch timestamp difference from "now" to the beginning of today. - */ - function getNowDayStartDiff() - { - $dayStartTs = ((int)($this->_dateTime/86400))*86400; - return $this->_dateTime - $dayStartTs; - } - - /** - * Find the epoch timestamp difference from "now" to the end of today. - */ - function getNowDayEndDiff() - { - $dayEndTs = ((int)(($this->_dateTime+86400)/86400))*86400; - return $dayEndTs - $this->_dateTime; - } - - function getEpochTime() - { - return $this->_dateTime; - } - - /** - * Returns the offset in seconds, between local and UTC timezones. - * E.g., if local timezone is -4, this function - * returns -14400. - * - * @return type offset in int, between local and UTC timezones - */ - function getLocalTimeZoneOffset() { - $dateTime = new DateTime("@".$this->_dateTime, new DateTimeZone("UTC")); - $timezone = new DateTimeZone(date_default_timezone_get()); - return $timezone->getOffset($dateTime); - } - - /** - * Returns the offset hour in int, between local and UTC timezones. - * E.g., if local timezone is -4:30, this function - * returns -4. - * - * @return type offset hour in int, between local and UTC timezones - */ - function getLocalOffsetHour() { - $offset = $this->getLocalTimeZoneOffset(); - return (int)($offset / 3600); - } - - /** - * Returns the offset minute in int, between local and UTC timezones. - * E.g., if local timezone is -4:30, this function - * returns -30. - * - * @return type offset minute in int, between local and UTC timezones - */ - function getLocalOffsetMinute() { - $offset = $this->getLocalTimeZoneOffset(); - return (int)(($offset % 3600) / 60); - } - - public static function TimeDiff($time1, $time2) - { - return strtotime($time2) - strtotime($time1); - } - - public static function TimeAdd($time1, $time2) - { - return strtotime($time2) + strtotime($time1); - } - - public static function ConvertMSToHHMMSSmm($time) - { - $hours = floor($time / 3600000); - $time -= 3600000*$hours; - - $minutes = floor($time / 60000); - $time -= 60000*$minutes; - - $seconds = floor($time / 1000); - $time -= 1000*$seconds; - - $ms = $time; - - if (strlen($hours) == 1) - $hours = "0".$hours; - if (strlen($minutes) == 1) - $minutes = "0".$minutes; - if (strlen($seconds) == 1) - $seconds = "0".$seconds; - - return $hours.":".$minutes.":".$seconds.".".$ms; + $day = $now->format('w'); + if ($day == 0) { + $day = 7; + } + + $dayDiff = $day - 1; + if ($dayDiff > 0) { + $now->sub(new DateInterval("P{$dayDiff}D")); + } + + return $now; } /** @@ -206,16 +160,6 @@ class Application_Common_DateHelper return $p_dateTime; } - public static function getDateFromTimestamp($p_dateTime){ - $explode = explode(" ", $p_dateTime); - return $explode[0]; - } - - public static function getTimeFromTimestamp($p_dateTime){ - $explode = explode(" ", $p_dateTime); - return $explode[1]; - } - /* Given a track length in the format HH:MM:SS.mm, we want to * convert this to seconds. This is useful for Liquidsoap which * likes input parameters give in seconds. @@ -244,68 +188,6 @@ class Application_Common_DateHelper $totalSeconds = ($hours*3600 + $minutes*60 + $seconds).".$ms"; return round($totalSeconds, 3); } - - public static function ConvertToUtcDateTime($p_dateString, $timezone=null){ - if (isset($timezone)) { - $dateTime = new DateTime($p_dateString, new DateTimeZone($timezone)); - } - else { - $dateTime = new DateTime($p_dateString, new DateTimeZone(date_default_timezone_get())); - } - $dateTime->setTimezone(new DateTimeZone("UTC")); - - return $dateTime; - } - - public static function ConvertToSpecificTimezoneDateTime($p_dateString, $timezone){ - $dateTime = new DateTime($p_dateString, new DateTimeZone("UTC")); - $dateTime->setTimezone(new DateTimeZone($timezone)); - - return $dateTime; - } - - public static function ConvertToLocalDateTime($p_dateString){ - $dateTime = new DateTime($p_dateString, new DateTimeZone("UTC")); - $dateTime->setTimezone(new DateTimeZone(Application_Model_Preference::GetTimezone())); - - return $dateTime; - } - - /* Convenience method to return a date formatted into a String rather than a - * DateTime object. Note that if an empty string is provided for $p_dateString - * then the current time is provided. - * - * @param $p_dateString - * Date string in UTC timezone. - * @param $p_format - * Format which the string should be returned in. - * - * @return string - * Date String in localtime - * */ - public static function ConvertToLocalDateTimeString($p_dateString, $p_format="Y-m-d H:i:s"){ - if (is_null($p_dateString) || strlen($p_dateString) == 0) - return $p_dateString; - return self::ConvertToLocalDateTime($p_dateString)->format($p_format); - } - - public static function ConvertToUtcDateTimeString($p_dateString, $p_format="Y-m-d H:i:s"){ - if (is_null($p_dateString) || strlen($p_dateString) == 0) - return $p_dateString; - return self::ConvertToUtcDateTime($p_dateString)->format($p_format); - } - - /* - * Example input: "00:02:32.746562". Output is a DateInterval object - * representing that 2 minute, 32.746562 second interval. - * - */ - public static function getDateIntervalFromString($p_interval){ - list($hour_min_sec, $subsec) = explode(".", $p_interval); - list($hour, $min, $sec) = explode(":", $hour_min_sec); - - return new DateInterval("PT{$hour}H{$min}M{$sec}S"); - } /** * returns true or false depending on input is wether in @@ -362,6 +244,74 @@ class Application_Common_DateHelper return $retVal; } + /* + * @param $datetime string Y-m-d H:i:s in UTC timezone + * + * @return string in $format default Y-m-d H:i:s in station timezone + */ + public static function UTCStringToStationTimezoneString($datetime, $format="Y-m-d H:i:s") { + $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); + $utcTimezone = new DateTimeZone("UTC"); + + $d = new DateTime($datetime, $utcTimezone); + $d->setTimezone($stationTimezone); + + return $d->format($format); + } + + /* + * @param $datetime string Y-m-d H:i:s in UTC timezone + * + * @return string Y-m-d H:i:s in user's timezone + */ + public static function UTCStringToUserTimezoneString($datetime, $format="Y-m-d H:i:s") { + $userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + $utcTimezone = new DateTimeZone("UTC"); + + $d = new DateTime($datetime, $utcTimezone); + $d->setTimezone($userTimezone); + + return $d->format($format); + } + + /* + * @param $datetime string Y-m-d H:i:s in user timezone + * + * @return string Y-m-d H:i:s in UTC timezone + */ + public static function UserTimezoneStringToUTCString($datetime, $format="Y-m-d H:i:s") { + $userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + $utcTimezone = new DateTimeZone("UTC"); + + $d = new DateTime($datetime, $userTimezone); + $d->setTimezone($utcTimezone); + + return $d->format($format); + } + + /** + * Convert the columns given in the array $columnsToConvert in the + * database result $rows to local timezone. + * + * @param array $rows arrays of arrays containing database query result + * @param array $columnsToConvert array of column names to convert + * @param string (station|user) convert to either station or user timezone. + */ + public static function convertTimestamps(&$rows, $columnsToConvert, $domain="station") + { + if (!is_array($rows)) { + return; + } + + $converter = "UTCStringTo".ucfirst($domain)."TimezoneString"; + + foreach ($rows as &$row) { + foreach ($columnsToConvert as $column) { + $row[$column] = self::$converter($row[$column]); + } + } + } + /** * This function is used for calculations! Don't modify for display purposes! * diff --git a/airtime_mvc/application/common/Timezone.php b/airtime_mvc/application/common/Timezone.php index 271ea5f02..7da82f6f9 100644 --- a/airtime_mvc/application/common/Timezone.php +++ b/airtime_mvc/application/common/Timezone.php @@ -14,7 +14,8 @@ class Application_Common_Timezone 'Australia' => DateTimeZone::AUSTRALIA, 'Europe' => DateTimeZone::EUROPE, 'Indian' => DateTimeZone::INDIAN, - 'Pacific' => DateTimeZone::PACIFIC + 'Pacific' => DateTimeZone::PACIFIC, + 'UTC' => DateTimeZone::UTC ); $tzlist = array(); diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 3a956306d..bf520dd86 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -68,30 +68,6 @@ class ApiController extends Zend_Controller_Action "version" => Application_Model_Preference::GetAirtimeVersion())); } - /** - * Sets up and send init values used in the Calendar. - * This is only being used by schedule.js at the moment. - */ - public function calendarInitAction() - { - if (is_null(Zend_Auth::getInstance()->getStorage()->read())) { - header('HTTP/1.0 401 Unauthorized'); - print _('You are not allowed to access this resource.'); - - return; - } - - $this->view->calendarInit = array( - "timestamp" => time(), - "timezoneOffset" => date("Z"), - "timeScale" => Application_Model_Preference::GetCalendarTimeScale(), - "timeInterval" => Application_Model_Preference::GetCalendarTimeInterval(), - "weekStartDay" => Application_Model_Preference::GetWeekStartDay() - ); - - $this->_helper->json->sendJson(array()); - } - /** * Allows remote client to download requested media file. * @@ -209,6 +185,7 @@ class ApiController extends Zend_Controller_Action } } + //Used by the SaaS monitoring public function onAirLightAction() { $this->view->layout()->disableLayout(); @@ -267,9 +244,8 @@ class ApiController extends Zend_Controller_Action // disable the view and the layout $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); - - $date = new Application_Common_DateHelper; - $utcTimeNow = $date->getUtcTimestamp(); + + $utcTimeNow = gmdate("Y-m-d H:i:s"); $utcTimeEnd = ""; // if empty, getNextShows will use interval instead of end of day $request = $this->getRequest(); @@ -284,44 +260,48 @@ class ApiController extends Zend_Controller_Action } // make getNextShows use end of day - $utcTimeEnd = Application_Common_DateHelper::GetDayEndTimestampInUtc(); - $result = array("env"=>APPLICATION_ENV, - "schedulerTime"=>gmdate("Y-m-d H:i:s"), - "currentShow"=>Application_Model_Show::getCurrentShow($utcTimeNow), - "nextShow"=>Application_Model_Show::getNextShows($utcTimeNow, $limit, $utcTimeEnd) - ); - // XSS exploit prevention - foreach ($result["currentShow"] as &$current) { - $current["name"] = htmlspecialchars($current["name"]); - } - foreach ($result["nextShow"] as &$next) { - $next["name"] = htmlspecialchars($next["name"]); - } - - Application_Model_Show::convertToLocalTimeZone($result["currentShow"], - array("starts", "ends", "start_timestamp", "end_timestamp")); - Application_Model_Show::convertToLocalTimeZone($result["nextShow"], - array("starts", "ends", "start_timestamp", "end_timestamp")); - } else { + $end = Application_Common_DateHelper::getTodayStationEndDateTime(); + $end->setTimezone(new DateTimeZone("UTC")); + $utcTimeEnd = $end->format("Y-m-d H:i:s"); + $result = array( + "env" => APPLICATION_ENV, + "schedulerTime" => $utcTimeNow, + "currentShow" => Application_Model_Show::getCurrentShow($utcTimeNow), + "nextShow" => Application_Model_Show::getNextShows($utcTimeNow, $limit, $utcTimeEnd) + ); + } + else { $result = Application_Model_Schedule::GetPlayOrderRange(); // XSS exploit prevention $result["previous"]["name"] = htmlspecialchars($result["previous"]["name"]); $result["current"]["name"] = htmlspecialchars($result["current"]["name"]); - $result["next"]["name"] = htmlspecialchars($result["next"]["name"]); - foreach ($result["currentShow"] as &$current) { - $current["name"] = htmlspecialchars($current["name"]); - } - foreach ($result["nextShow"] as &$next) { - $next["name"] = htmlspecialchars($next["name"]); - } - - //Convert from UTC to localtime for Web Browser. - Application_Model_Show::ConvertToLocalTimeZone($result["currentShow"], - array("starts", "ends", "start_timestamp", "end_timestamp")); - Application_Model_Show::ConvertToLocalTimeZone($result["nextShow"], - array("starts", "ends", "start_timestamp", "end_timestamp")); + $result["next"]["name"] = htmlspecialchars($result["next"]["name"]); } + + // XSS exploit prevention + foreach ($result["currentShow"] as &$current) { + $current["name"] = htmlspecialchars($current["name"]); + } + foreach ($result["nextShow"] as &$next) { + $next["name"] = htmlspecialchars($next["name"]); + } + + //For consistency, all times here are being sent in the station timezone, which + //seems to be what we've normalized everything to. + + //Convert the UTC scheduler time ("now") to the station timezone. + $result["schedulerTime"] = Application_Common_DateHelper::UTCStringToStationTimezoneString($result["schedulerTime"]); + $result["timezone"] = Application_Common_DateHelper::getStationTimezoneAbbreviation(); + $result["timezoneOffset"] = Application_Common_DateHelper::getStationTimezoneOffset(); + + //Convert from UTC to station time for Web Browser. + Application_Common_DateHelper::convertTimestamps($result["currentShow"], + array("starts", "ends", "start_timestamp", "end_timestamp"), + "station"); + Application_Common_DateHelper::convertTimestamps($result["nextShow"], + array("starts", "ends", "start_timestamp", "end_timestamp"), + "station"); //used by caller to determine if the airtime they are running or widgets in use is out of date. $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION; @@ -343,22 +323,37 @@ class ApiController extends Zend_Controller_Action $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); - $date = new Application_Common_DateHelper; - $dayStart = $date->getWeekStartDate(); - $utcDayStart = Application_Common_DateHelper::ConvertToUtcDateTimeString($dayStart); - + //weekStart is in station time. + $weekStartDateTime = Application_Common_DateHelper::getWeekStartDateTime(); + $dow = array("monday", "tuesday", "wednesday", "thursday", "friday", - "saturday", "sunday"); + "saturday", "sunday", "nextmonday", "nexttuesday", "nextwednesday", + "nextthursday", "nextfriday", "nextsaturday", "nextsunday"); $result = array(); - for ($i=0; $i<7; $i++) { - $utcDayEnd = Application_Common_DateHelper::GetDayEndTimestamp($utcDayStart); + $utcTimezone = new DateTimeZone("UTC"); + $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); + + $weekStartDateTime->setTimezone($utcTimezone); + $utcDayStart = $weekStartDateTime->format("Y-m-d H:i:s"); + for ($i = 0; $i < 14; $i++) { + + //have to be in station timezone when adding 1 day for daylight savings. + $weekStartDateTime->setTimezone($stationTimezone); + $weekStartDateTime->add(new DateInterval('P1D')); + + //convert back to UTC to get the actual timestamp used for search. + $weekStartDateTime->setTimezone($utcTimezone); + + $utcDayEnd = $weekStartDateTime->format("Y-m-d H:i:s"); $shows = Application_Model_Show::getNextShows($utcDayStart, "ALL", $utcDayEnd); $utcDayStart = $utcDayEnd; - - Application_Model_Show::convertToLocalTimeZone($shows, - array("starts", "ends", "start_timestamp", - "end_timestamp")); + + Application_Common_DateHelper::convertTimestamps( + $shows, + array("starts", "ends", "start_timestamp","end_timestamp"), + "station" + ); $result[$dow[$i]] = $shows; } @@ -434,23 +429,22 @@ class ApiController extends Zend_Controller_Action public function recordedShowsAction() { - $today_timestamp = date("Y-m-d H:i:s"); - $now = new DateTime($today_timestamp); - $end_timestamp = $now->add(new DateInterval("PT2H")); - $end_timestamp = $end_timestamp->format("Y-m-d H:i:s"); + $utcTimezone = new DateTimeZone("UTC"); + $nowDateTime = new DateTime("now", $utcTimezone); + $endDateTime = clone $nowDateTime; + $endDateTime = $endDateTime->add(new DateInterval("PT2H")); $this->view->shows = Application_Model_Show::getShows( - Application_Common_DateHelper::ConvertToUtcDateTime($today_timestamp, date_default_timezone_get()), - Application_Common_DateHelper::ConvertToUtcDateTime($end_timestamp, date_default_timezone_get()), + $nowDateTime, + $endDateTime, $onlyRecord = true); $this->view->is_recording = false; - $this->view->server_timezone = Application_Model_Preference::GetTimezone(); - - $rows = Application_Model_Show::getCurrentShow($today_timestamp); - Application_Model_Show::convertToLocalTimeZone($rows, array("starts", "ends", "start_timestamp", "end_timestamp")); + $this->view->server_timezone = Application_Model_Preference::GetDefaultTimezone(); + $rows = Application_Model_Show::getCurrentShow(); + if (count($rows) > 0) { $this->view->is_recording = ($rows[0]['record'] == 1); } @@ -493,9 +487,8 @@ class ApiController extends Zend_Controller_Action try { $show_inst = new Application_Model_ShowInstance($show_instance_id); $show_inst->setRecordedFile($file_id); - //$show_start_time = Application_Common_DateHelper::ConvertToLocalDateTimeString($show_inst->getShowInstanceStart()); - - } catch (Exception $e) { + } + catch (Exception $e) { //we've reached here probably because the show was //cancelled, and therefore the show instance does not exist //anymore (ShowInstance constructor threw this error). We've @@ -1064,12 +1057,15 @@ class ApiController extends Zend_Controller_Action { $request = $this->getRequest(); $data = $request->getParam("data"); - $media_id = $request->getParam("media_id"); + $media_id = intval($request->getParam("media_id")); $data_arr = json_decode($data); + + //$media_id is -1 sometimes when a stream has stopped playing + if (!is_null($media_id) && $media_id > 0) { - if (!is_null($media_id)) { - if (isset($data_arr->title) && - strlen($data_arr->title) < 1024) { + if (isset($data_arr->title)) { + + $data_title = substr($data_arr->title, 0, 1024); $previous_metadata = CcWebstreamMetadataQuery::create() ->orderByDbStartTime('desc') @@ -1078,23 +1074,27 @@ class ApiController extends Zend_Controller_Action $do_insert = true; if ($previous_metadata) { - if ($previous_metadata->getDbLiquidsoapData() == $data_arr->title) { - Logging::debug("Duplicate found: ".$data_arr->title); + if ($previous_metadata->getDbLiquidsoapData() == $data_title) { + Logging::debug("Duplicate found: ". $data_title); $do_insert = false; } } if ($do_insert) { + + $startDT = new DateTime("now", new DateTimeZone("UTC")); + $webstream_metadata = new CcWebstreamMetadata(); $webstream_metadata->setDbInstanceId($media_id); - $webstream_metadata->setDbStartTime(new DateTime("now", new DateTimeZone("UTC"))); - $webstream_metadata->setDbLiquidsoapData($data_arr->title); + $webstream_metadata->setDbStartTime($startDT); + $webstream_metadata->setDbLiquidsoapData($data_title); $webstream_metadata->save(); + + $historyService = new Application_Service_HistoryService(); + $historyService->insertWebstreamMetadata($media_id, $startDT, $data_arr); } } - } else { - throw new Exception("Null value of media_id"); - } + } $this->view->response = $data; $this->view->media_id = $media_id; diff --git a/airtime_mvc/application/controllers/DashboardController.php b/airtime_mvc/application/controllers/DashboardController.php index fc2461d3f..bdcaf2f1b 100644 --- a/airtime_mvc/application/controllers/DashboardController.php +++ b/airtime_mvc/application/controllers/DashboardController.php @@ -97,7 +97,7 @@ class DashboardController extends Zend_Controller_Action $baseUrl = Application_Common_OsPath::getBaseDir(); $this->view->headLink()->appendStylesheet($baseUrl.'js/jplayer/skin/jplayer.blue.monday.css?'.$CC_CONFIG['airtime_version']); - $this->_helper->layout->setLayout('bare'); + $this->_helper->layout->setLayout('livestream'); $logo = Application_Model_Preference::GetStationLogo(); if ($logo) { diff --git a/airtime_mvc/application/controllers/ListenerstatController.php b/airtime_mvc/application/controllers/ListenerstatController.php index 3ec495839..178aec631 100644 --- a/airtime_mvc/application/controllers/ListenerstatController.php +++ b/airtime_mvc/application/controllers/ListenerstatController.php @@ -20,9 +20,6 @@ class ListenerstatController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/flot/jquery.flot.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/flot/jquery.flot.crosshair.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/listenerstat/listenerstat.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); - - $offset = date("Z") * -1; - $this->view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds"); $this->view->headScript()->appendFile($baseUrl.'js/timepicker/jquery.ui.timepicker.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/utilities/utilities.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); @@ -33,11 +30,14 @@ class ListenerstatController extends Zend_Controller_Action $now = time(); $from = $request->getParam("from", $now - (24*60*60)); $to = $request->getParam("to", $now); + + $utcTimezone = new DateTimeZone("UTC"); + $displayTimeZone = new DateTimeZone(Application_Model_Preference::GetTimezone()); - $start = DateTime::createFromFormat("U", $from, new DateTimeZone("UTC")); - $start->setTimezone(new DateTimeZone(date_default_timezone_get())); - $end = DateTime::createFromFormat("U", $to, new DateTimeZone("UTC")); - $end->setTimezone(new DateTimeZone(date_default_timezone_get())); + $start = DateTime::createFromFormat("U", $from, $utcTimezone); + $start->setTimezone($displayTimeZone); + $end = DateTime::createFromFormat("U", $to, $utcTimezone); + $end->setTimezone($displayTimeZone); $form = new Application_Form_DateRange(); $form->populate(array( diff --git a/airtime_mvc/application/controllers/LocaleController.php b/airtime_mvc/application/controllers/LocaleController.php index 91b9b6055..1e12dbca3 100644 --- a/airtime_mvc/application/controllers/LocaleController.php +++ b/airtime_mvc/application/controllers/LocaleController.php @@ -148,17 +148,7 @@ class LocaleController extends Zend_Controller_Action "is greater than" => _("is greater than"), "is less than" => _("is less than"), "is in the range" => _("is in the range"), - //playouthistory/historytable.js - "Title" => _("Title"), - "Creator" => _("Creator"), - "Played" => _("Played"), - "Length" => _("Length"), - "Composer" => _("Composer"), - "Copyright" => _("Copyright"), - "All" => _("All"), - "Copied %s row%s to the clipboard" => _("Copied %s row%s to the clipboard"), - "%sPrint view%sPlease use your browser's print function to print this table. Press escape when finished." => _("%sPrint view%sPlease use your browser's print function to print this table. Press escape when finished."), - //preferences/musicdirs.js + //preferences/musicdirs.js "Choose Storage Folder" => _("Choose Storage Folder"), "Choose Folder to Watch" => _("Choose Folder to Watch"), "Are you sure you want to change the storage folder?\nThis will remove the files from your Airtime library!" @@ -187,6 +177,7 @@ class LocaleController extends Zend_Controller_Action //preferences/support-setting.js "Image must be one of jpg, jpeg, png, or gif" => _("Image must be one of jpg, jpeg, png, or gif"), //schedule/add-show.js + "Warning: You cannot change this field while the show is currently playing" => _("Warning: You cannot change this field while the show is currently playing"), "No result found" => _("No result found"), "This follows the same security pattern for the shows: only users assigned to the show can connect." => _("This follows the same security pattern for the shows: only users assigned to the show can connect."), "Specify custom authentication which will work only for this show." => _("Specify custom authentication which will work only for this show."), @@ -194,6 +185,7 @@ class LocaleController extends Zend_Controller_Action "The show instance doesn't exist anymore!" => _("The show instance doesn't exist anymore!"), "Warning: Shows cannot be re-linked" => _("Warning: Shows cannot be re-linked"), "By linking your repeating shows any media items scheduled in any repeat show will also get scheduled in the other repeat shows" => _("By linking your repeating shows any media items scheduled in any repeat show will also get scheduled in the other repeat shows"), + "Timezone is set to the station timezone by default. Shows in the calendar will be displayed in your local time defined by the Interface Timezone in your user settings." => _("Timezone is set to the station timezone by default. Shows in the calendar will be displayed in your local time defined by the Interface Timezone in your user settings."), //schedule/full-calendar-functions //already in schedule/add-show.js //"The show instance doesn't exist anymore!" => _("The show instance doesn't exist anymore!"), @@ -391,13 +383,20 @@ class LocaleController extends Zend_Controller_Action "File: %f, size: %s, max file size: %m" => _("File: %f, size: %s, max file size: %m"), "Upload URL might be wrong or doesn't exist" => _("Upload URL might be wrong or doesn't exist"), "Error: File too large: " => _("Error: File too large: "), - "Error: Invalid file extension: " => _("Error: Invalid file extension: ") - + "Error: Invalid file extension: " => _("Error: Invalid file extension: "), + //history translations + "Set Default" => _("Set Default"), + "Create Entry" => _("Create Entry"), + "Edit History Record" => _("Edit History Record"), + "No Show" => _("No Show"), + "All" => _("All"), + "Copied %s row%s to the clipboard" => _("Copied %s row%s to the clipboard"), + "%sPrint view%sPlease use your browser's print function to print this table. Press escape when finished." => _("%sPrint view%sPlease use your browser's print function to print this table. Press escape when finished.") ); $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); header("Content-type: text/javascript"); echo "var general_dict=".json_encode($translations); - + } } diff --git a/airtime_mvc/application/controllers/LoginController.php b/airtime_mvc/application/controllers/LoginController.php index edb8222d7..4c58a6b57 100644 --- a/airtime_mvc/application/controllers/LoginController.php +++ b/airtime_mvc/application/controllers/LoginController.php @@ -70,7 +70,7 @@ class LoginController extends Zend_Controller_Action $tempSess->referrer = 'login'; //set the user locale in case user changed it in when logging in - Application_Model_Preference::SetUserLocale($auth->getIdentity()->id, $locale); + Application_Model_Preference::SetUserLocale($locale); $this->_redirect('Showbuilder'); } else { diff --git a/airtime_mvc/application/controllers/PlayouthistoryController.php b/airtime_mvc/application/controllers/PlayouthistoryController.php index d28c96412..cbd15517f 100644 --- a/airtime_mvc/application/controllers/PlayouthistoryController.php +++ b/airtime_mvc/application/controllers/PlayouthistoryController.php @@ -32,10 +32,13 @@ class PlayouthistoryController extends Zend_Controller_Action $from = $request->getParam("from", $now - (24*60*60)); $to = $request->getParam("to", $now); - $start = DateTime::createFromFormat("U", $from, new DateTimeZone("UTC")); - $start->setTimezone(new DateTimeZone(date_default_timezone_get())); - $end = DateTime::createFromFormat("U", $to, new DateTimeZone("UTC")); - $end->setTimezone(new DateTimeZone(date_default_timezone_get())); + $utcTimezone = new DateTimeZone("UTC"); + $displayTimeZone = new DateTimeZone(Application_Model_Preference::GetTimezone()); + + $start = DateTime::createFromFormat("U", $from, $utcTimezone); + $start->setTimezone($displayTimeZone); + $end = DateTime::createFromFormat("U", $to, $utcTimezone); + $end->setTimezone($displayTimeZone); $form = new Application_Form_DateRange(); $form->populate(array( @@ -54,8 +57,6 @@ class PlayouthistoryController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/TableTools-2.1.5/js/ZeroClipboard.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/TableTools-2.1.5/js/TableTools.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); - $offset = date("Z") * -1; - $this->view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds"); $this->view->headScript()->appendFile($baseUrl.'js/timepicker/jquery.ui.timepicker.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/bootstrap-datetime/bootstrap-datetimepicker.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index ced193bec..95415f8be 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -35,6 +35,7 @@ class ScheduleController extends Zend_Controller_Action ->addActionContext('calculate-duration', 'json') ->addActionContext('get-current-show', 'json') ->addActionContext('update-future-is-scheduled', 'json') + ->addActionContext('localize-start-end-time', 'json') ->initContext(); $this->sched_sess = new Zend_Session_Namespace("schedule"); @@ -50,7 +51,7 @@ class ScheduleController extends Zend_Controller_Action "var calendarPref = {};\n". "calendarPref.weekStart = ".Application_Model_Preference::GetWeekStartDay().";\n". "calendarPref.timestamp = ".time().";\n". - "calendarPref.timezoneOffset = ".date("Z").";\n". + "calendarPref.timezoneOffset = ".Application_Common_DateHelper::getUserTimezoneOffset().";\n". "calendarPref.timeScale = '".Application_Model_Preference::GetCalendarTimeScale()."';\n". "calendarPref.timeInterval = ".Application_Model_Preference::GetCalendarTimeInterval().";\n". "calendarPref.weekStartDay = ".Application_Model_Preference::GetWeekStartDay().";\n". @@ -60,15 +61,17 @@ class ScheduleController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/contextmenu/jquery.contextMenu.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); //full-calendar-functions.js requires this variable, so that datePicker widget can be offset to server time instead of client time - $this->view->headScript()->appendScript("var timezoneOffset = ".date("Z")."; //in seconds"); - $this->view->headScript()->appendFile($baseUrl.'js/airtime/schedule/full-calendar-functions.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + //this should be as a default, however with our new drop down timezone changing for shows, we should reset this offset then?? + $this->view->headScript()->appendScript("var timezoneOffset = ".Application_Common_DateHelper::getStationTimezoneOffset()."; //in seconds"); + //set offset to ensure it loads last + $this->view->headScript()->offsetSetFile(90, $baseUrl.'js/airtime/schedule/full-calendar-functions.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/fullcalendar/fullcalendar.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/timepicker/jquery.ui.timepicker.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/colorpicker/js/colorpicker.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/schedule/add-show.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'js/airtime/schedule/schedule.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + $this->view->headScript()->offsetSetFile(100, $baseUrl.'js/airtime/schedule/schedule.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/blockui/jquery.blockUI.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headLink()->appendStylesheet($baseUrl.'css/jquery.ui.timepicker.css?'.$CC_CONFIG['airtime_version']); @@ -78,7 +81,6 @@ class ScheduleController extends Zend_Controller_Action $this->view->headLink()->appendStylesheet($baseUrl.'css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']); //Start Show builder JS/CSS requirements - $this->view->headScript()->appendFile($baseUrl.'js/contextmenu/jquery.contextMenu.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/js/jquery.dataTables.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.pluginAPI.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.fnSetFilteringDelay.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); @@ -114,9 +116,11 @@ class ScheduleController extends Zend_Controller_Action $service_user = new Application_Service_UserService(); $currentUser = $service_user->getCurrentUser(); - $start = new DateTime($this->_getParam('start', null)); + $userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + + $start = new DateTime($this->_getParam('start', null), $userTimezone); $start->setTimezone(new DateTimeZone("UTC")); - $end = new DateTime($this->_getParam('end', null)); + $end = new DateTime($this->_getParam('end', null), $userTimezone); $end->setTimezone(new DateTimeZone("UTC")); $events = &Application_Model_Show::getFullCalendarEvents($start, $end, @@ -182,6 +186,7 @@ class ScheduleController extends Zend_Controller_Action $deltaDay = $this->_getParam('day'); $deltaMin = $this->_getParam('min'); $showId = $this->_getParam('showId'); + $instanceId = $this->_getParam('instanceId'); $userInfo = Zend_Auth::getInstance()->getStorage()->read(); $user = new Application_Model_User($userInfo->id); @@ -194,7 +199,7 @@ class ScheduleController extends Zend_Controller_Action return false; } - $error = $show->resizeShow($deltaDay, $deltaMin); + $error = $show->resizeShow($deltaDay, $deltaMin, $instanceId); } if (isset($error)) { @@ -260,22 +265,36 @@ class ScheduleController extends Zend_Controller_Action $show = Application_Model_Show::getCurrentShow(); /* Convert all UTC times to localtime before sending back to user. */ + $range["schedulerTime"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["schedulerTime"]); + if (isset($range["previous"])) { - $range["previous"]["starts"] = Application_Common_DateHelper::ConvertToLocalDateTimeString($range["previous"]["starts"]); - $range["previous"]["ends"] = Application_Common_DateHelper::ConvertToLocalDateTimeString($range["previous"]["ends"]); + $range["previous"]["starts"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["previous"]["starts"]); + $range["previous"]["ends"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["previous"]["ends"]); } if (isset($range["current"])) { - $range["current"]["starts"] = Application_Common_DateHelper::ConvertToLocalDateTimeString($range["current"]["starts"]); - $range["current"]["ends"] = Application_Common_DateHelper::ConvertToLocalDateTimeString($range["current"]["ends"]); + $range["current"]["starts"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["current"]["starts"]); + $range["current"]["ends"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["current"]["ends"]); } if (isset($range["next"])) { - $range["next"]["starts"] = Application_Common_DateHelper::ConvertToLocalDateTimeString($range["next"]["starts"]); - $range["next"]["ends"] = Application_Common_DateHelper::ConvertToLocalDateTimeString($range["next"]["ends"]); + $range["next"]["starts"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["next"]["starts"]); + $range["next"]["ends"] = Application_Common_DateHelper::UTCStringToUserTimezoneString($range["next"]["ends"]); } + + Application_Common_DateHelper::convertTimestamps( + $range["currentShow"], + array("starts", "ends", "start_timestamp", "end_timestamp"), + "user" + ); + Application_Common_DateHelper::convertTimestamps( + $range["nextShow"], + array("starts", "ends", "start_timestamp", "end_timestamp"), + "user" + ); - Application_Model_Show::convertToLocalTimeZone($range["currentShow"], array("starts", "ends", "start_timestamp", "end_timestamp")); - Application_Model_Show::convertToLocalTimeZone($range["nextShow"], array("starts", "ends", "start_timestamp", "end_timestamp")); - + //TODO: Add timezone and timezoneOffset back into the ApiController's results. + $range["timezone"] = Application_Common_DateHelper::getUserTimezoneAbbreviation(); + $range["timezoneOffset"] = Application_Common_DateHelper::getUserTimezoneOffset(); + $source_status = array(); $switch_status = array(); $live_dj = Application_Model_Preference::GetSourceStatus("live_dj"); @@ -323,9 +342,10 @@ class ScheduleController extends Zend_Controller_Action $originalShowStart = $originalShow->getShowInstanceStart(); //convert from UTC to user's timezone for display. + $displayTimeZone = new DateTimeZone(Application_Model_Preference::GetTimezone()); $originalDateTime = new DateTime($originalShowStart, new DateTimeZone("UTC")); - $originalDateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); - //$timestamp = Application_Common_DateHelper::ConvertToLocalDateTimeString($originalDateTime->format("Y-m-d H:i:s")); + $originalDateTime->setTimezone($displayTimeZone); + $this->view->additionalShowInfo = sprintf(_("Rebroadcast of show %s from %s at %s"), $originalShowName, @@ -417,7 +437,7 @@ class ScheduleController extends Zend_Controller_Action if ($service_showForm->validateShowForms($forms, $data, $validateStartDate, $originalShowStartDateTime, true, $data["add_show_instance_id"])) { - $service_show->createShowFromRepeatingInstance($data); + $service_show->editRepeatingShowInstance($data); $this->view->addNewShow = true; $this->view->newForm = $this->view->render('schedule/add-show-form.phtml'); @@ -430,7 +450,7 @@ class ScheduleController extends Zend_Controller_Action } $this->view->rr->getElement('add_show_record')->setOptions(array('disabled' => true)); $this->view->addNewShow = false; - $this->view->action = "edit-show"; + $this->view->action = "edit-repeating-show-instance"; $this->view->form = $this->view->render('schedule/add-show-form.phtml'); } } @@ -627,9 +647,12 @@ class ScheduleController extends Zend_Controller_Action public function calculateDurationAction() { + $start = $this->_getParam('startTime'); + $end = $this->_getParam('endTime'); + $timezone = $this->_getParam('timezone'); + $service_showForm = new Application_Service_ShowFormService(); - $result = $service_showForm->calculateDuration($this->_getParam('startTime'), - $this->_getParam('endTime')); + $result = $service_showForm->calculateDuration($start, $end, $timezone); echo Zend_Json::encode($result); exit(); @@ -638,7 +661,25 @@ class ScheduleController extends Zend_Controller_Action public function updateFutureIsScheduledAction() { $schedId = $this->_getParam('schedId'); - $redrawLibTable = Application_Model_StoredFile::setIsScheduled($schedId, false); + + $scheduleService = new Application_Service_SchedulerService(); + $redrawLibTable = $scheduleService->updateFutureIsScheduled($schedId, false); + $this->_helper->json->sendJson(array("redrawLibTable" => $redrawLibTable)); } + + public function localizeStartEndTimeAction() + { + $newTimezone = $this->_getParam('newTimezone'); + $oldTimezone = $this->_getParam('oldTimezone'); + $localTime = array(); + + $localTime["start"] = Application_Service_ShowFormService::localizeDateTime( + $this->_getParam('startDate'), $this->_getParam('startTime'), $newTimezone, $oldTimezone); + + $localTime["end"] = Application_Service_ShowFormService::localizeDateTime( + $this->_getParam('endDate'), $this->_getParam('endTime'), $newTimezone, $oldTimezone); + + $this->_helper->json->sendJson($localTime); + } } diff --git a/airtime_mvc/application/controllers/ShowbuilderController.php b/airtime_mvc/application/controllers/ShowbuilderController.php index ec34f68f2..dd1a19e13 100644 --- a/airtime_mvc/application/controllers/ShowbuilderController.php +++ b/airtime_mvc/application/controllers/ShowbuilderController.php @@ -20,31 +20,15 @@ class ShowbuilderController extends Zend_Controller_Action { $CC_CONFIG = Config::getConfig(); - + $request = $this->getRequest(); - + $baseUrl = Application_Common_OsPath::getBaseDir(); - + $user = Application_Model_User::GetCurrentUser(); $userType = $user->getType(); $this->view->headScript()->appendScript("localStorage.setItem( 'user-type', '$userType' );"); - $data = Application_Model_Preference::getCurrentLibraryTableSetting(); - if (!is_null($data)) { - $libraryTable = json_encode($data); - $this->view->headScript()->appendScript("localStorage.setItem( 'datatables-library', JSON.stringify($libraryTable) );"); - } else { - $this->view->headScript()->appendScript("localStorage.setItem( 'datatables-library', '' );"); - } - - $data = Application_Model_Preference::getTimelineDatatableSetting(); - if (!is_null($data)) { - $timelineTable = json_encode($data); - $this->view->headScript()->appendScript("localStorage.setItem( 'datatables-timeline', JSON.stringify($timelineTable) );"); - } else { - $this->view->headScript()->appendScript("localStorage.setItem( 'datatables-timeline', '' );"); - } - $this->view->headScript()->appendFile($baseUrl.'js/contextmenu/jquery.contextMenu.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/js/jquery.dataTables.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.pluginAPI.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); @@ -57,14 +41,12 @@ class ShowbuilderController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/blockui/jquery.blockUI.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/utilities/utilities.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headLink()->appendStylesheet($baseUrl.'css/media_library.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'css/datatables/css/ColVis.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'css/datatables/css/ColReorder.css?'.$CC_CONFIG['airtime_version']); - $this->view->headScript()->appendFile($baseUrl.'js/airtime/library/events/library_showbuilder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $refer_sses = new Zend_Session_Namespace('referrer'); if ($request->isPost()) { @@ -141,16 +123,40 @@ class ShowbuilderController extends Zend_Controller_Action $this->view->disableLib = $disableLib; $this->view->showLib = $showLib; + //only include library things on the page if the user can see it. + if (!$disableLib) { + $this->view->headScript()->appendFile($baseUrl.'js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'js/airtime/library/events/library_showbuilder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + + $data = Application_Model_Preference::getCurrentLibraryTableSetting(); + if (!is_null($data)) { + $libraryTable = json_encode($data); + $this->view->headScript()->appendScript("localStorage.setItem( 'datatables-library', JSON.stringify($libraryTable) );"); + } else { + $this->view->headScript()->appendScript("localStorage.setItem( 'datatables-library', '' );"); + } + } + + $data = Application_Model_Preference::getTimelineDatatableSetting(); + if (!is_null($data)) { + $timelineTable = json_encode($data); + $this->view->headScript()->appendScript("localStorage.setItem( 'datatables-timeline', JSON.stringify($timelineTable) );"); + } else { + $this->view->headScript()->appendScript("localStorage.setItem( 'datatables-timeline', '' );"); + } + //populate date range form for show builder. $now = time(); $from = $request->getParam("from", $now); $to = $request->getParam("to", $now + (24*60*60)); - $start = DateTime::createFromFormat("U", $from, new DateTimeZone("UTC")); - $start->setTimezone(new DateTimeZone(date_default_timezone_get())); + $utcTimezone = new DateTimeZone("UTC"); + $displayTimeZone = new DateTimeZone(Application_Model_Preference::GetTimezone()); - $end = DateTime::createFromFormat("U", $to, new DateTimeZone("UTC")); - $end->setTimezone(new DateTimeZone(date_default_timezone_get())); + $start = DateTime::createFromFormat("U", $from, $utcTimezone); + $start->setTimezone($displayTimeZone); + $end = DateTime::createFromFormat("U", $to, $utcTimezone); + $end->setTimezone($displayTimeZone); $form = new Application_Form_ShowBuilder(); $form->populate(array( @@ -162,8 +168,6 @@ class ShowbuilderController extends Zend_Controller_Action $this->view->sb_form = $form; - $offset = date("Z") * -1; - $this->view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds"); $this->view->headScript()->appendFile($baseUrl.'js/timepicker/jquery.ui.timepicker.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/showbuilder/builder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/airtime/showbuilder/main_builder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); @@ -214,10 +218,12 @@ class ShowbuilderController extends Zend_Controller_Action return; } + $displayTimeZone = new DateTimeZone(Application_Model_Preference::GetTimezone()); + $start = $instance->getDbStarts(null); - $start->setTimezone(new DateTimeZone(date_default_timezone_get())); + $start->setTimezone($displayTimeZone); $end = $instance->getDbEnds(null); - $end->setTimezone(new DateTimeZone(date_default_timezone_get())); + $end->setTimezone($displayTimeZone); $show_name = $instance->getCcShow()->getDbName(); $start_time = $start->format("Y-m-d H:i:s"); @@ -270,8 +276,8 @@ class ShowbuilderController extends Zend_Controller_Action $startsDT = DateTime::createFromFormat("U", $starts_epoch, new DateTimeZone("UTC")); $endsDT = DateTime::createFromFormat("U", $ends_epoch, new DateTimeZone("UTC")); - $opts = array("myShows" => $my_shows, - "showFilter" => $show_filter, + $opts = array("myShows" => $my_shows, + "showFilter" => $show_filter, "showInstanceFilter" => $show_instance_filter); $showBuilder = new Application_Model_ShowBuilder($startsDT, $endsDT, $opts); diff --git a/airtime_mvc/application/controllers/UserController.php b/airtime_mvc/application/controllers/UserController.php index ddfd15c9c..fad0277db 100644 --- a/airtime_mvc/application/controllers/UserController.php +++ b/airtime_mvc/application/controllers/UserController.php @@ -72,8 +72,8 @@ class UserController extends Zend_Controller_Action // Language and timezone settings are saved on a per-user basis // By default, the default language, and timezone setting on // preferences page is what gets assigned. - Application_Model_Preference::SetUserLocale($user->getId()); - Application_Model_Preference::SetUserTimezone($user->getId()); + Application_Model_Preference::SetUserLocale(); + Application_Model_Preference::SetUserTimezone(); $form->reset(); $this->view->form = $form; @@ -143,8 +143,8 @@ class UserController extends Zend_Controller_Action $user->setJabber($formData['cu_jabber']); $user->save(); - Application_Model_Preference::SetUserLocale($user->getId(), $formData['cu_locale']); - Application_Model_Preference::SetUserTimezone($user->getId(), $formData['cu_timezone']); + Application_Model_Preference::SetUserLocale($formData['cu_locale']); + Application_Model_Preference::SetUserTimezone($formData['cu_timezone']); //configure localization with new locale setting Application_Model_Locale::configureLocalization($formData['cu_locale']); diff --git a/airtime_mvc/application/forms/AddShowLiveStream.php b/airtime_mvc/application/forms/AddShowLiveStream.php index b34aab2e5..65e2a19d3 100644 --- a/airtime_mvc/application/forms/AddShowLiveStream.php +++ b/airtime_mvc/application/forms/AddShowLiveStream.php @@ -72,4 +72,14 @@ class Application_Form_AddShowLiveStream extends Zend_Form_SubForm return $isValid; } + + public function disable() + { + $elements = $this->getElements(); + foreach ($elements as $element) { + if ($element->getType() != 'Zend_Form_Element_Hidden') { + $element->setAttrib('disabled','disabled'); + } + } + } } diff --git a/airtime_mvc/application/forms/AddShowRepeats.php b/airtime_mvc/application/forms/AddShowRepeats.php index d09b5284a..a8179ccd8 100644 --- a/airtime_mvc/application/forms/AddShowRepeats.php +++ b/airtime_mvc/application/forms/AddShowRepeats.php @@ -17,7 +17,9 @@ class Application_Form_AddShowRepeats extends Zend_Form_SubForm 'class' => ' input_select', 'multiOptions' => array( "0" => _("weekly"), - "1" => _("bi-weekly"), + "1" => _("every 2 weeks"), + "4" => _("every 3 weeks"), + "5" => _("every 4 weeks"), "2" => _("monthly") ), )); @@ -81,8 +83,8 @@ class Application_Form_AddShowRepeats extends Zend_Form_SubForm } public function isValid($formData) { - if (parent::isValid($formData)) { - return $this->checkReliantFields($formData); + if (parent::isValid($formData)) { + return $this->checkReliantFields($formData); } else { return false; } @@ -93,15 +95,22 @@ class Application_Form_AddShowRepeats extends Zend_Form_SubForm if (!$formData['add_show_no_end']) { $start_timestamp = $formData['add_show_start_date']; $end_timestamp = $formData['add_show_end_date']; - - $start_epoch = strtotime($start_timestamp); - $end_epoch = strtotime($end_timestamp); - - if ($end_epoch < $start_epoch) { + $showTimeZone = new DateTimeZone($formData['add_show_timezone']); + + //We're assuming all data is valid at this point (timezone, etc.). + + $startDate = new DateTime($start_timestamp, $showTimeZone); + $endDate = new DateTime($end_timestamp, $showTimeZone); + + if ($endDate < $startDate) { $this->getElement('add_show_end_date')->setErrors(array(_('End date must be after start date'))); - return false; } + return true; + } + + if (!isset($formData['add_show_day_check'])) { + $this->getElement('add_show_day_check')->setErrors(array(_('Please select a repeat day'))); } return true; diff --git a/airtime_mvc/application/forms/AddShowWhat.php b/airtime_mvc/application/forms/AddShowWhat.php index 20aeb6ef5..a1e87701f 100644 --- a/airtime_mvc/application/forms/AddShowWhat.php +++ b/airtime_mvc/application/forms/AddShowWhat.php @@ -75,4 +75,14 @@ class Application_Form_AddShowWhat extends Zend_Form_SubForm } } } + + public function makeReadonly() + { + $elements = $this->getElements(); + foreach ($elements as $element) { + if ($element->getType() != 'Zend_Form_Element_Hidden') { + $element->setAttrib('readonly','readonly'); + } + } + } } diff --git a/airtime_mvc/application/forms/AddShowWhen.php b/airtime_mvc/application/forms/AddShowWhen.php index 40645b4b9..5b1ef3e3a 100644 --- a/airtime_mvc/application/forms/AddShowWhen.php +++ b/airtime_mvc/application/forms/AddShowWhen.php @@ -78,6 +78,15 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm 'decorators' => array('ViewHelper') )); + $timezone = new Zend_Form_Element_Select('add_show_timezone'); + $timezone->setRequired(true) + ->setLabel(_("Timezone:")) + ->setMultiOptions(Application_Common_Timezone::getTimezones()) + ->setValue(Application_Model_Preference::GetUserTimezone()) + ->setAttrib('class', 'input_select add_show_input_select') + ->setDecorators(array('ViewHelper')); + $this->addElement($timezone); + // Add repeats element $this->addElement('checkbox', 'add_show_repeats', array( 'label' => _('Repeats?'), @@ -104,18 +113,20 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm $start_time = $formData['add_show_start_date']." ".$formData['add_show_start_time']; $end_time = $formData['add_show_end_date_no_repeat']." ".$formData['add_show_end_time']; - //DateTime stores $start_time in the current timezone - $nowDateTime = new DateTime(); - $showStartDateTime = new DateTime($start_time); - $showEndDateTime = new DateTime($end_time); + //have to use the timezone the user has entered in the form to check past/present + $showTimezone = new DateTimeZone($formData["add_show_timezone"]); + $nowDateTime = new DateTime("now", $showTimezone); + $showStartDateTime = new DateTime($start_time, $showTimezone); + $showEndDateTime = new DateTime($end_time, $showTimezone); + if ($validateStartDate) { - if ($showStartDateTime->getTimestamp() < $nowDateTime->getTimestamp()) { + if ($showStartDateTime < $nowDateTime) { $this->getElement('add_show_start_time')->setErrors(array(_('Cannot create show in the past'))); $valid = false; } // if edit action, check if original show start time is in the past. CC-3864 if ($originalStartDate) { - if ($originalStartDate->getTimestamp() < $nowDateTime->getTimestamp()) { + if ($originalStartDate < $nowDateTime) { $this->getElement('add_show_start_time')->setValue($originalStartDate->format("H:i")); $this->getElement('add_show_start_date')->setValue($originalStartDate->format("Y-m-d")); $this->getElement('add_show_start_time')->setErrors(array(_('Cannot modify start date/time of the show that is already started'))); @@ -126,43 +137,42 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm } // if end time is in the past, return error - if ($showEndDateTime->getTimestamp() < $nowDateTime->getTimestamp()) { + if ($showEndDateTime < $nowDateTime) { $this->getElement('add_show_end_time')->setErrors(array(_('End date/time cannot be in the past'))); $valid = false; } - $pattern = '/([0-9][0-9])h ([0-9][0-9])m/'; + //validate duration. + $duration = $showStartDateTime->diff($showEndDateTime); - if (preg_match($pattern, $formData['add_show_duration'], $matches) && count($matches) == 3) { - $hours = $matches[1]; - $minutes = $matches[2]; - if ($formData["add_show_duration"] == "00h 00m") { - $this->getElement('add_show_duration')->setErrors(array(_('Cannot have duration 00h 00m'))); - $valid = false; - } elseif (strpos($formData["add_show_duration"], 'h') !== false && $hours >= 24) { - if ($hours > 24 || ($hours == 24 && $minutes > 0)) { - $this->getElement('add_show_duration')->setErrors(array(_('Cannot have duration greater than 24h'))); - $valid = false; - } - } elseif ( strstr($formData["add_show_duration"], '-') ) { - $this->getElement('add_show_duration')->setErrors(array(_('Cannot have duration < 0m'))); - $valid = false; - } - } else { - $valid = false; + if ($showStartDateTime > $showEndDateTime) { + $this->getElement('add_show_duration')->setErrors(array(_('Cannot have duration < 0m'))); + $valid = false; } + else if ($showStartDateTime == $showEndDateTime) { + $this->getElement('add_show_duration')->setErrors(array(_('Cannot have duration 00h 00m'))); + $valid = false; + } + else if (intval($duration->format('%d')) > 0 && + (intval($duration->format('%h')) > 0 + || intval($duration->format('%i')) > 0 + || intval($duration->format('%s')) > 0)) { + $this->getElement('add_show_duration')->setErrors(array(_('Cannot have duration greater than 24h'))); + $valid = false; + } + /* Check if show is overlapping * We will only do this check if the show is valid * upto this point */ if ($valid) { + //we need to know the start day of the week in show's local timezome + $startDow = $showStartDateTime->format("w"); + $utc = new DateTimeZone('UTC'); - $localTimezone = new DateTimeZone(Application_Model_Preference::GetTimezone()); - $show_start = new DateTime($start_time); - $show_start->setTimezone($utc); - $show_end = new DateTime($end_time); - $show_end->setTimezone($utc); + $showStartDateTime->setTimezone($utc); + $showEndDateTime->setTimezone($utc); if ($formData["add_show_repeats"]) { @@ -179,7 +189,7 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm } elseif (!$formData["add_show_no_end"]) { $popUntil = $formData["add_show_end_date"]." ".$formData["add_show_end_time"]; - $populateUntilDateTime = new DateTime($popUntil); + $populateUntilDateTime = new DateTime($popUntil, $showTimezone); $populateUntilDateTime->setTimezone($utc); } @@ -188,6 +198,10 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm $interval = 'P7D'; } elseif ($formData["add_show_repeat_type"] == 1) { $interval = 'P14D'; + } elseif ($formData["add_show_repeat_type"] == 4) { + $interval = 'P21D'; + } elseif ($formData["add_show_repeat_type"] == 5) { + $interval = 'P28D'; } elseif ($formData["add_show_repeat_type"] == 2) { $interval = 'P1M'; } @@ -197,21 +211,24 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm */ if ($update) { $overlapping = Application_Model_Schedule::checkOverlappingShows( - $show_start, $show_end, $update, null, $formData["add_show_id"]); + $showStartDateTime, $showEndDateTime, $update, null, $formData["add_show_id"]); } else { $overlapping = Application_Model_Schedule::checkOverlappingShows( - $show_start, $show_end); + $showStartDateTime, $showEndDateTime); } - //$overlapping = Application_Model_Schedule::checkOverlappingShows($show_start, $show_end, $update, $instanceId); /* Check if repeats overlap with previously scheduled shows * Do this for each show day */ if (!$overlapping) { - $startDow = date("w", $show_start->getTimestamp()); + + if (!isset($formData['add_show_day_check'])) { + return false; + } + foreach ($formData["add_show_day_check"] as $day) { - $repeatShowStart = clone $show_start; - $repeatShowEnd = clone $show_end; + $repeatShowStart = clone $showStartDateTime; + $repeatShowEnd = clone $showEndDateTime; $daysAdd=0; if ($startDow !== $day) { if ($startDow > $day) @@ -223,9 +240,8 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm * to convert show start and show end to local time before * adding the interval for the next repeating show */ - - $repeatShowStart->setTimezone($localTimezone); - $repeatShowEnd->setTimezone($localTimezone); + $repeatShowStart->setTimezone($showTimezone); + $repeatShowEnd->setTimezone($showTimezone); $repeatShowStart->add(new DateInterval("P".$daysAdd."D")); $repeatShowEnd->add(new DateInterval("P".$daysAdd."D")); //set back to UTC @@ -245,14 +261,14 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm $overlapping = Application_Model_Schedule::checkOverlappingShows( $repeatShowStart, $repeatShowEnd, $update, null, $formData["add_show_id"]); } - + if ($overlapping) { $valid = false; $this->getElement('add_show_duration')->setErrors(array(_('Cannot schedule overlapping shows'))); break 1; } else { - $repeatShowStart->setTimezone($localTimezone); - $repeatShowEnd->setTimezone($localTimezone); + $repeatShowStart->setTimezone($showTimezone); + $repeatShowEnd->setTimezone($showTimezone); $repeatShowStart->add(new DateInterval($interval)); $repeatShowEnd->add(new DateInterval($interval)); $repeatShowStart->setTimezone($utc); @@ -264,8 +280,9 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm $valid = false; $this->getElement('add_show_duration')->setErrors(array(_('Cannot schedule overlapping shows'))); } + } else { - $overlapping = Application_Model_Schedule::checkOverlappingShows($show_start, $show_end, $update, $instanceId); + $overlapping = Application_Model_Schedule::checkOverlappingShows($showStartDateTime, $showEndDateTime, $update, $instanceId); if ($overlapping) { $this->getElement('add_show_duration')->setErrors(array(_('Cannot schedule overlapping shows'))); $valid = false; @@ -285,16 +302,16 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm * show start back to local time */ $rebroadcastShowStart->setTimezone(new DateTimeZone( - Application_Model_Preference::GetTimezone())); + $formData["add_show_timezone"])); $rebroadcastWhenDays = explode(" ", $formData["add_show_rebroadcast_date_".$i]); $rebroadcastWhenTime = explode(":", $formData["add_show_rebroadcast_time_".$i]); $rebroadcastShowStart->add(new DateInterval("P".$rebroadcastWhenDays[0]."D")); $rebroadcastShowStart->setTime($rebroadcastWhenTime[0], $rebroadcastWhenTime[1]); $rebroadcastShowStart->setTimezone(new DateTimeZone('UTC')); - + $rebroadcastShowEnd = clone $rebroadcastShowStart; $rebroadcastShowEnd->add(new DateInterval("PT".$hours."H".$minutes."M")); - + if ($showEdit) { $overlapping = Application_Model_Schedule::checkOverlappingShows( $rebroadcastShowStart, $rebroadcastShowEnd, true, null, $formData['add_show_id']); @@ -302,13 +319,13 @@ class Application_Form_AddShowWhen extends Zend_Form_SubForm $overlapping = Application_Model_Schedule::checkOverlappingShows( $rebroadcastShowStart, $rebroadcastShowEnd); } - + if ($overlapping) break; } - + return $overlapping; } - + public function disable() { $elements = $this->getElements(); diff --git a/airtime_mvc/application/forms/AddUser.php b/airtime_mvc/application/forms/AddUser.php index 75a8f659c..1d3835ae7 100644 --- a/airtime_mvc/application/forms/AddUser.php +++ b/airtime_mvc/application/forms/AddUser.php @@ -1,4 +1,5 @@ setAttrib('id', 'user_form'); $hidden = new Zend_Form_Element_Hidden('user_id'); @@ -42,6 +44,7 @@ class Application_Form_AddUser extends Zend_Form $passwordVerify->setRequired(true); $passwordVerify->addFilter('StringTrim'); $passwordVerify->addValidator($notEmptyValidator); + $passwordVerify->addValidator($notDemoValidator); $this->addElement($passwordVerify); $firstName = new Zend_Form_Element_Text('first_name'); diff --git a/airtime_mvc/application/forms/EditHistoryItem.php b/airtime_mvc/application/forms/EditHistoryItem.php index 16ba5ded5..2f6f38043 100644 --- a/airtime_mvc/application/forms/EditHistoryItem.php +++ b/airtime_mvc/application/forms/EditHistoryItem.php @@ -43,7 +43,7 @@ class Application_Form_EditHistoryItem extends Application_Form_EditHistory $ends->addFilter('StringTrim'); $ends->setLabel(_('End Time')); $ends->setDecorators(array('ViewHelper')); - $ends->setRequired(true); + //$ends->setRequired(true); $this->addElement($ends); } diff --git a/airtime_mvc/application/forms/EditUser.php b/airtime_mvc/application/forms/EditUser.php index cff9dfdda..8b8565fce 100644 --- a/airtime_mvc/application/forms/EditUser.php +++ b/airtime_mvc/application/forms/EditUser.php @@ -1,4 +1,5 @@ setDecorators(array( array('ViewScript', array('viewScript' => 'form/edit-user.phtml', "currentUser" => $currentUser->getLogin())))); $this->setAttrib('id', 'current-user-form'); @@ -52,6 +54,7 @@ class Application_Form_EditUser extends Zend_Form $passwordVerify->setRequired(true); $passwordVerify->addFilter('StringTrim'); $passwordVerify->addValidator($notEmptyValidator); + $passwordVerify->addValidator($notDemoValidator); $passwordVerify->setDecorators(array('viewHelper')); $this->addElement($passwordVerify); @@ -115,7 +118,7 @@ class Application_Form_EditUser extends Zend_Form $this->addElement($locale); $timezone = new Zend_Form_Element_Select("cu_timezone"); - $timezone->setLabel(_("Timezone:")); + $timezone->setLabel(_("Interface Timezone:")); $timezone->setMultiOptions(Application_Common_Timezone::getTimezones()); $timezone->setValue(Application_Model_Preference::GetUserTimezone($currentUserId)); $timezone->setDecorators(array('ViewHelper')); diff --git a/airtime_mvc/application/forms/GeneralPreferences.php b/airtime_mvc/application/forms/GeneralPreferences.php index 58fa8e53f..c45437b08 100644 --- a/airtime_mvc/application/forms/GeneralPreferences.php +++ b/airtime_mvc/application/forms/GeneralPreferences.php @@ -115,7 +115,7 @@ class Application_Form_GeneralPreferences extends Zend_Form_SubForm /* Form Element for setting the Timezone */ $timezone = new Zend_Form_Element_Select("timezone"); - $timezone->setLabel(_("Default Interface Timezone")); + $timezone->setLabel(_("Station Timezone")); $timezone->setMultiOptions(Application_Common_Timezone::getTimezones()); $timezone->setValue(Application_Model_Preference::GetDefaultTimezone()); $timezone->setDecorators(array('ViewHelper')); diff --git a/airtime_mvc/application/forms/LiveStreamingPreferences.php b/airtime_mvc/application/forms/LiveStreamingPreferences.php index 94e654079..6dd14c40e 100644 --- a/airtime_mvc/application/forms/LiveStreamingPreferences.php +++ b/airtime_mvc/application/forms/LiveStreamingPreferences.php @@ -107,6 +107,7 @@ class Application_Form_LiveStreamingPreferences extends Zend_Form_SubForm public function isValid($data) { $isValid = parent::isValid($data); + return $isValid; } diff --git a/airtime_mvc/application/forms/SmartBlockCriteria.php b/airtime_mvc/application/forms/SmartBlockCriteria.php index 52ea8d86d..990d5bde9 100644 --- a/airtime_mvc/application/forms/SmartBlockCriteria.php +++ b/airtime_mvc/application/forms/SmartBlockCriteria.php @@ -127,6 +127,31 @@ class Application_Form_SmartBlockCriteria extends Zend_Form_SubForm public function init() { } + + /* + * converts UTC timestamp citeria into user timezone strings. + */ + private function convertTimestamps(&$criteria) + { + $columns = array("utime", "mtime", "lptime"); + + foreach ($columns as $column) { + + if (isset($criteria[$column])) { + + foreach ($criteria[$column] as &$constraint) { + + $constraint['value'] = + Application_Common_DateHelper::UTCStringToUserTimezoneString($constraint['value']); + + if (isset($constraint['extra'])) { + $constraint['extra'] = + Application_Common_DateHelper::UTCStringToUserTimezoneString($constraint['extra']); + } + } + } + } + } public function startForm($p_blockId, $p_isValid = false) { @@ -150,6 +175,9 @@ class Application_Form_SmartBlockCriteria extends Zend_Form_SubForm $bl = new Application_Model_Block($p_blockId); $storedCrit = $bl->getCriteria(); + + //need to convert criteria to be displayed in the user's timezone if there's some timestamp type. + self::convertTimestamps($storedCrit["crit"]); /* $modRoadMap stores the number of same criteria * Ex: 3 Album titles, and 2 Track titles diff --git a/airtime_mvc/application/layouts/scripts/bare.phtml b/airtime_mvc/application/layouts/scripts/bare.phtml index f6b3f99bf..4194f7f78 100644 --- a/airtime_mvc/application/layouts/scripts/bare.phtml +++ b/airtime_mvc/application/layouts/scripts/bare.phtml @@ -2,7 +2,7 @@ - <?php echo _("Live stream") ?> + headLink() ?> headScript() ?> google_analytics)?$this->google_analytics:"" ?> diff --git a/airtime_mvc/application/layouts/scripts/livestream.phtml b/airtime_mvc/application/layouts/scripts/livestream.phtml new file mode 100644 index 000000000..d21c4d92d --- /dev/null +++ b/airtime_mvc/application/layouts/scripts/livestream.phtml @@ -0,0 +1,17 @@ +doctype() ?> + + + + + + + + <?php echo _("Live stream") ?> + headLink() ?> + headScript() ?> + google_analytics)?$this->google_analytics:"" ?> + + +
layout()->content ?>
+ + diff --git a/airtime_mvc/application/models/Block.php b/airtime_mvc/application/models/Block.php index f63d1d861..babef06a5 100644 --- a/airtime_mvc/application/models/Block.php +++ b/airtime_mvc/application/models/Block.php @@ -1197,13 +1197,26 @@ SQL; $critKeys = array_keys($p_criteriaData['criteria']); for ($i = 0; $i < count($critKeys); $i++) { foreach ($p_criteriaData['criteria'][$critKeys[$i]] as $d) { + + $field = $d['sp_criteria_field']; + $value = $d['sp_criteria_value']; + + if ($field == 'utime' || $field == 'mtime' || $field == 'lptime') { + $value = Application_Common_DateHelper::UserTimezoneStringToUTCString($value); + } + $qry = new CcBlockcriteria(); - $qry->setDbCriteria($d['sp_criteria_field']) + $qry->setDbCriteria($field) ->setDbModifier($d['sp_criteria_modifier']) - ->setDbValue($d['sp_criteria_value']) + ->setDbValue($value) ->setDbBlockId($this->id); if (isset($d['sp_criteria_extra'])) { + + if ($field == 'utime' || $field == 'mtime' || $field == 'lptime') { + $d['sp_criteria_extra'] = Application_Common_DateHelper::UserTimezoneStringToUTCString($d['sp_criteria_extra']); + } + $qry->setDbExtra($d['sp_criteria_extra']); } $qry->save(); @@ -1413,28 +1426,18 @@ SQL; foreach ($storedCrit["crit"] as $crit) { $i = 0; foreach ($crit as $criteria) { - //$spCriteriaPhpName = self::$criteria2PeerMap[$criteria['criteria']]; $spCriteria = $criteria['criteria']; $spCriteriaModifier = $criteria['modifier']; $column = CcFilesPeer::getTableMap()->getColumnByPhpName(self::$criteria2PeerMap[$spCriteria]); - // if the column is timestamp, convert it into UTC - if ($column->getType() == PropelColumnTypes::TIMESTAMP) { - $spCriteriaValue = Application_Common_DateHelper::ConvertToUtcDateTimeString($criteria['value']); - /* Check if only a date was supplied and trim - * the time after it is converted to UTC time - */ - if (strlen($criteria['value']) <= 10) { - //extract date only from timestamp in db - $spCriteria = 'date('.$spCriteria.')'; - $spCriteriaValue = substr($spCriteriaValue, 0, 10); - } + //data should already be in UTC, do we have to do anything special here anymore? + if ($column->getType() == PropelColumnTypes::TIMESTAMP) { + + $spCriteriaValue = $criteria['value']; + if (isset($criteria['extra'])) { - $spCriteriaExtra = Application_Common_DateHelper::ConvertToUtcDateTimeString($criteria['extra']); - if (strlen($criteria['extra']) <= 10) { - $spCriteriaExtra = substr($spCriteriaExtra, 0, 10); - } + $spCriteriaExtra = $criteria['extra']; } } elseif ($spCriteria == "bit_rate" || $spCriteria == 'sample_rate') { // multiply 1000 because we store only number value diff --git a/airtime_mvc/application/models/Cache.php b/airtime_mvc/application/models/Cache.php new file mode 100644 index 000000000..fc473395a --- /dev/null +++ b/airtime_mvc/application/models/Cache.php @@ -0,0 +1,32 @@ +$p_start, 'p2'=>$p_end, 'p3'=>$d['mount_name'], 'p4'=>$jump, 'p5'=>$remainder)); + + $utcTimezone = new DateTimeZone("UTC"); + $displayTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + foreach ($result as $r) { - $t = new DateTime($r['timestamp'], new DateTimeZone("UTC")); - $t->setTimezone(new DateTimeZone(date_default_timezone_get())); + $t = new DateTime($r['timestamp'], $utcTimezone); + $t->setTimezone($displayTimezone); // tricking javascript so it thinks the server timezone is in UTC - $dt = new DateTime($t->format("Y-m-d H:i:s"), new DateTimeZone("UTC")); + $dt = new DateTime($t->format("Y-m-d H:i:s"), $utcTimezone); $r['timestamp'] = $dt->format("U"); $out[$r['mount_name']][] = $r; diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 7f4ea8602..772152c8e 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -1,26 +1,43 @@ hasIdentity()) { + $userId = null; + } + else { + $auth = Zend_Auth::getInstance(); + $userId = $auth->getIdentity()->id; + } + + return $userId; + } + /** * - * @param integer $userId is not null when we are setting a locale for a specific user * @param boolean $isUserValue is true when we are setting a value for the current user */ - private static function setValue($key, $value, $isUserValue = false, $userId = null) + private static function setValue($key, $value, $isUserValue = false) { + $cache = new Cache(); + try { $con = Propel::getConnection(CcPrefPeer::DATABASE_NAME); $con->beginTransaction(); - //called from a daemon process - if (!class_exists("Zend_Auth", false) || !Zend_Auth::getInstance()->hasIdentity()) { - $id = NULL; - } else { - $auth = Zend_Auth::getInstance(); - $id = $auth->getIdentity()->id; + $userId = self::getUserId(); + + if ($isUserValue && is_null($userId)) { + throw new Exception("User id can't be null for a user preference {$key}."); } + + Application_Common_Database::prepareAndExecute("LOCK TABLE cc_pref"); //Check if key already exists $sql = "SELECT COUNT(*) FROM cc_pref" @@ -30,15 +47,10 @@ class Application_Model_Preference $paramMap[':key'] = $key; //For user specific preference, check if id matches as well - if ($isUserValue && is_null($userId)) { + if ($isUserValue) { $sql .= " AND subjid = :id"; - $paramMap[':id'] = $id; - } else if (!is_null($userId)) { - $sql .= " AND subjid= :id"; $paramMap[':id'] = $userId; - } - - Application_Common_Database::prepareAndExecute("LOCK TABLE cc_pref"); + } $result = Application_Common_Database::prepareAndExecute($sql, $paramMap, @@ -51,39 +63,39 @@ class Application_Model_Preference //this case should not happen. throw new Exception("Invalid number of results returned. Should be ". "0 or 1, but is '$result' instead"); - } elseif ($result == 1) { + } + elseif ($result == 1) { + // result found - if (is_null($id) || !$isUserValue) { + if (!$isUserValue) { // system pref $sql = "UPDATE cc_pref" ." SET subjid = NULL, valstr = :value" ." WHERE keystr = :key"; - } else { + } + else { // user pref $sql = "UPDATE cc_pref" . " SET valstr = :value" . " WHERE keystr = :key AND subjid = :id"; - if (is_null($userId)) { - $paramMap[':id'] = $id; - } else { - $paramMap[':id'] = $userId; - } + + $paramMap[':id'] = $userId; } - } else { + } + else { + // result not found - if (is_null($id) || !$isUserValue) { + if (!$isUserValue) { // system pref $sql = "INSERT INTO cc_pref (keystr, valstr)" ." VALUES (:key, :value)"; - } else { + } + else { // user pref $sql = "INSERT INTO cc_pref (subjid, keystr, valstr)" ." VALUES (:id, :key, :value)"; - if (is_null($userId)) { - $paramMap[':id'] = $id; - } else { - $paramMap[':id'] = $userId; - } + + $paramMap[':id'] = $userId; } } $paramMap[':key'] = $key; @@ -96,18 +108,35 @@ class Application_Model_Preference $con); $con->commit(); - } catch (Exception $e) { + } + catch (Exception $e) { $con->rollback(); header('HTTP/1.0 503 Service Unavailable'); Logging::info("Database error: ".$e->getMessage()); exit; } + $cache->store($key, $value, $isUserValue, $userId); + //Logging::info("SAVING {$key} {$userId} into cache. = {$value}"); } private static function getValue($key, $isUserValue = false) { + $cache = new Cache(); + try { + + $userId = self::getUserId(); + + if ($isUserValue && is_null($userId)) { + throw new Exception("User id can't be null for a user preference."); + } + + $res = $cache->fetch($key, $isUserValue, $userId); + if ($res !== false) { + //Logging::info("returning {$key} {$userId} from cache. = {$res}"); + return $res; + } //Check if key already exists $sql = "SELECT COUNT(*) FROM cc_pref" @@ -117,20 +146,16 @@ class Application_Model_Preference $paramMap[':key'] = $key; //For user specific preference, check if id matches as well - if ($isUserValue) { - $auth = Zend_Auth::getInstance(); - if ($auth->hasIdentity()) { - $id = $auth->getIdentity()->id; - - $sql .= " AND subjid = :id"; - $paramMap[':id'] = $id; - } + if ($isUserValue) { + $sql .= " AND subjid = :id"; + $paramMap[':id'] = $userId; } $result = Application_Common_Database::prepareAndExecute($sql, $paramMap, Application_Common_Database::COLUMN); + //return an empty string if the result doesn't exist. if ($result == 0) { - return ""; + $res = ""; } else { $sql = "SELECT valstr FROM cc_pref" @@ -140,16 +165,20 @@ class Application_Model_Preference $paramMap[':key'] = $key; //For user specific preference, check if id matches as well - if ($isUserValue && $auth->hasIdentity()) { + if ($isUserValue) { $sql .= " AND subjid = :id"; - $paramMap[':id'] = $id; + $paramMap[':id'] = $userId; } $result = Application_Common_Database::prepareAndExecute($sql, $paramMap, Application_Common_Database::COLUMN); - return ($result !== false) ? $result : ""; + $res = ($result !== false) ? $result : ""; } - } catch (Exception $e) { + + $cache->store($key, $res, $isUserValue, $userId); + return $res; + } + catch (Exception $e) { header('HTTP/1.0 503 Service Unavailable'); Logging::info("Could not connect to database: ".$e->getMessage()); exit; @@ -505,44 +534,48 @@ class Application_Model_Preference return self::getValue("description"); } + // Sets station default timezone (from preferences) public static function SetDefaultTimezone($timezone) { self::setValue("timezone", $timezone); - date_default_timezone_set($timezone); } + // Returns station default timezone (from preferences) public static function GetDefaultTimezone() { return self::getValue("timezone"); } - public static function SetUserTimezone($userId, $timezone = null) + public static function SetUserTimezone($timezone = null) { // When a new user is created they will get the default timezone // setting which the admin sets on preferences page if (is_null($timezone)) { $timezone = self::GetDefaultTimezone(); } - self::setValue("user_timezone", $timezone, true, $userId); + self::setValue("user_timezone", $timezone, true); } - public static function GetUserTimezone($id) + public static function GetUserTimezone() { $timezone = self::getValue("user_timezone", true); if (!$timezone) { return self::GetDefaultTimezone(); - } else { + } + else { return $timezone; } } + // Always attempts to returns the current user's personal timezone setting public static function GetTimezone() { - $auth = Zend_Auth::getInstance(); - if ($auth->hasIdentity()) { - $id = $auth->getIdentity()->id; - return self::GetUserTimezone($id); - } else { + $userId = self::getUserId(); + + if (!is_null($userId)) { + return self::GetUserTimezone(); + } + else { return self::GetDefaultTimezone(); } } @@ -558,33 +591,35 @@ class Application_Model_Preference return self::getValue("locale"); } - public static function GetUserLocale($id) + public static function GetUserLocale() { $locale = self::getValue("user_locale", true); if (!$locale) { return self::GetDefaultLocale(); - } else { + } + else { return $locale; } } - public static function SetUserLocale($userId, $locale = null) + public static function SetUserLocale($locale = null) { // When a new user is created they will get the default locale // setting which the admin sets on preferences page if (is_null($locale)) { $locale = self::GetDefaultLocale(); } - self::setValue("user_locale", $locale, true, $userId); + self::setValue("user_locale", $locale, true); } public static function GetLocale() { - $auth = Zend_Auth::getInstance(); - if ($auth->hasIdentity()) { - $id = $auth->getIdentity()->id; - return self::GetUserLocale($id); - } else { + $userId = self::getUserId(); + + if (!is_null($userId)) { + return self::GetUserLocale(); + } + else { return self::GetDefaultLocale(); } } @@ -1270,7 +1305,7 @@ class Application_Model_Preference return $ds['ColReorder'][$x]; } else { /*For now we just have this hack for debugging. We should not - rely on this crappy behaviour in case of failure*/ + rely on this behaviour in case of failure*/ Logging::warn("Index $x does not exist preferences"); Logging::warn("Defaulting to identity and printing preferences"); Logging::warn($ds); @@ -1286,15 +1321,8 @@ class Application_Model_Preference public static function setCurrentLibraryTableSetting($settings) { - $num_columns = count(Application_Model_StoredFile::getLibraryColumns()); - $new_columns_num = count($settings['abVisCols']); - - /*if ($num_columns != $new_columns_num) { - throw new Exception("Trying to write a user column preference with incorrect number of columns!"); - }*/ - $data = serialize($settings); - $v = self::setValue("library_datatable", $data, true); + self::setValue("library_datatable", $data, true); } public static function getCurrentLibraryTableSetting() diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index eb2e3db0f..72da5ffbd 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -65,6 +65,8 @@ SQL; */ public static function GetPlayOrderRange($p_prev = 1, $p_next = 1) { + //Everything in this function must be done in UTC. You will get a swift kick in the pants if you mess that up. + if (!is_int($p_prev) || !is_int($p_next)) { //must enter integers to specify ranges Logging::info("Invalid range parameters: $p_prev or $p_next"); @@ -72,25 +74,23 @@ SQL; return array(); } - $date = new Application_Common_DateHelper; - $timeNow = $date->getTimestamp(); - $utcTimeNow = $date->getUtcTimestamp(); + $utcNow = new DateTime("now", new DateTimeZone("UTC")); - $shows = Application_Model_Show::getPrevCurrentNext($utcTimeNow); + $shows = Application_Model_Show::getPrevCurrentNext($utcNow); $previousShowID = count($shows['previousShow'])>0?$shows['previousShow'][0]['instance_id']:null; $currentShowID = count($shows['currentShow'])>0?$shows['currentShow'][0]['instance_id']:null; $nextShowID = count($shows['nextShow'])>0?$shows['nextShow'][0]['instance_id']:null; - $results = self::GetPrevCurrentNext($previousShowID, $currentShowID, $nextShowID, $utcTimeNow); + $results = self::GetPrevCurrentNext($previousShowID, $currentShowID, $nextShowID, $utcNow); $range = array("env"=>APPLICATION_ENV, - "schedulerTime"=>$timeNow, + "schedulerTime"=> $utcNow->format("Y-m-d H:i:s"), + //Previous, current, next songs! "previous"=>$results['previous'] !=null?$results['previous']:(count($shows['previousShow'])>0?$shows['previousShow'][0]:null), "current"=>$results['current'] !=null?$results['current']:((count($shows['currentShow'])>0 && $shows['currentShow'][0]['record'] == 1)?$shows['currentShow'][0]:null), "next"=> $results['next'] !=null?$results['next']:(count($shows['nextShow'])>0?$shows['nextShow'][0]:null), + //Current and next shows "currentShow"=>$shows['currentShow'], "nextShow"=>$shows['nextShow'], - "timezone"=> date("T"), - "timezoneOffset"=> date("Z") ); return $range; @@ -106,8 +106,12 @@ SQL; * through this mechanism a call is made to the old way of querying * the database to find the track info. **/ - public static function GetPrevCurrentNext($p_previousShowID, $p_currentShowID, $p_nextShowID, $p_timeNow) + public static function GetPrevCurrentNext($p_previousShowID, $p_currentShowID, $p_nextShowID, $utcNow) { + $timeZone = new DateTimeZone("UTC"); //This function works entirely in UTC. + assert(get_class($utcNow) === "DateTime"); + assert($utcNow->getTimeZone() == $timeZone); + if ($p_previousShowID == null && $p_currentShowID == null && $p_nextShowID == null) { return; } @@ -165,14 +169,17 @@ SQL; $results['current'] = null; $results['next'] = null; - $timeNowAsMillis = strtotime($p_timeNow); for ($i = 0; $i < $numberOfRows; ++$i) { + // if the show is overbooked, then update the track end time to the end of the show time. if ($rows[$i]['ends'] > $rows[$i]["show_ends"]) { $rows[$i]['ends'] = $rows[$i]["show_ends"]; } - if ((strtotime($rows[$i]['starts']) <= $timeNowAsMillis) && (strtotime($rows[$i]['ends']) >= $timeNowAsMillis)) { + $curShowStartTime = new DateTime($rows[$i]['starts'], $timeZone); + $curShowEndTime = new DateTime($rows[$i]['ends'], $timeZone); + + if (($curShowStartTime <= $utcNow) && ($curShowEndTime >= $utcNow)) { if ($i - 1 >= 0) { $results['previous'] = array("name"=>$rows[$i-1]["artist_name"]." - ".$rows[$i-1]["track_title"], "starts"=>$rows[$i-1]["starts"], @@ -193,10 +200,10 @@ SQL; } break; } - if (strtotime($rows[$i]['ends']) < $timeNowAsMillis ) { + if ($curShowEndTime < $utcNow ) { $previousIndex = $i; } - if (strtotime($rows[$i]['starts']) > $timeNowAsMillis) { + if ($curShowStartTime > $utcNow) { $results['next'] = array("name"=>$rows[$i]["artist_name"]." - ".$rows[$i]["track_title"], "starts"=>$rows[$i]["starts"], "ends"=>$rows[$i]["ends"], @@ -585,33 +592,6 @@ SQL; return $ret; } - /** - * Compute the difference between two times in the format . - * "HH:MM:SS.mmmmmm" Note: currently only supports calculating . - * millisec differences . - * - * @param string $p_time1 - * @param string $p_time2 - * @return double - */ - private static function TimeDiff($p_time1, $p_time2) - { - $parts1 = explode(".", $p_time1); - $parts2 = explode(".", $p_time2); - $diff = 0; - if ( (count($parts1) > 1) && (count($parts2) > 1) ) { - $millisec1 = substr($parts1[1], 0, 3); - $millisec1 = str_pad($millisec1, 3, "0"); - $millisec1 = intval($millisec1); - $millisec2 = substr($parts2[1], 0, 3); - $millisec2 = str_pad($millisec2, 3, "0"); - $millisec2 = intval($millisec2); - $diff = abs($millisec1 - $millisec2)/1000; - } - - return $diff; - } - /** * Returns an array of schedule items from cc_schedule table. Tries * to return at least 3 items (if they are available). The parameters @@ -814,7 +794,6 @@ SQL; 'start' => $start, 'end' => $end, 'show_name' => $item["show_name"], - 'row_id' => $item["id"], 'independent_event' => true ); self::appendScheduleItem($data, $start, $schedule_item); @@ -850,16 +829,18 @@ SQL; { $CC_CONFIG = Config::getConfig(); + $utcTimeZone = new DateTimeZone('UTC'); + /* if $p_fromDateTime and $p_toDateTime function parameters are null, then set range * from "now" to "now + 24 hours". */ if (is_null($p_fromDateTime)) { - $t1 = new DateTime("@".time()); + $t1 = new DateTime("@".time(), $utcTimeZone); $range_start = $t1->format("Y-m-d H:i:s"); } else { $range_start = Application_Model_Schedule::PypoTimeToAirtimeTime($p_fromDateTime); } if (is_null($p_fromDateTime)) { - $t2 = new DateTime("@".time()); + $t2 = new DateTime("@".time(), $utcTimeZone); $cache_ahead_hours = $CC_CONFIG["cache_ahead_hours"]; @@ -909,12 +890,14 @@ SQL; $storedFile = Application_Model_StoredFile::RecallById($media_id); $uri = $storedFile->getFilePath(); self::createFileScheduleEvent($data, $item, $media_id, $uri); - } elseif (!is_null($item['stream_id'])) { + } + elseif (!is_null($item['stream_id'])) { //row is type "webstream" $media_id = $item['stream_id']; $uri = $item['url']; self::createStreamScheduleEvent($data, $item, $media_id, $uri); - } else { + } + else { throw new Exception("Unknown schedule type: ".print_r($item, true)); } @@ -991,7 +974,7 @@ SQL; self::createInputHarborKickTimes($data, $range_start, $range_end); self::createScheduledEvents($data, $range_start, $range_end); - self::foldData($data["media"]); + //self::foldData($data["media"]); return $data; } @@ -1008,198 +991,24 @@ SQL; Application_Common_Database::prepareAndExecute($sql, array(':file_id'=>$fileId), 'execute'); } - /*public static function createNewFormSections($p_view) - { - $formWhat = new Application_Form_AddShowWhat(); - $formWho = new Application_Form_AddShowWho(); - $formWhen = new Application_Form_AddShowWhen(); - $formRepeats = new Application_Form_AddShowRepeats(); - $formStyle = new Application_Form_AddShowStyle(); - $formLive = new Application_Form_AddShowLiveStream(); - - $formWhat->removeDecorator('DtDdWrapper'); - $formWho->removeDecorator('DtDdWrapper'); - $formWhen->removeDecorator('DtDdWrapper'); - $formRepeats->removeDecorator('DtDdWrapper'); - $formStyle->removeDecorator('DtDdWrapper'); - $formLive->removeDecorator('DtDdWrapper'); - - $p_view->what = $formWhat; - $p_view->when = $formWhen; - $p_view->repeats = $formRepeats; - $p_view->who = $formWho; - $p_view->style = $formStyle; - $p_view->live = $formLive; - - $formWhat->populate(array('add_show_id' => '-1', - 'add_show_instance_id' => '-1')); - $formWhen->populate(array('add_show_start_date' => date("Y-m-d"), - 'add_show_start_time' => '00:00', - 'add_show_end_date_no_repeate' => date("Y-m-d"), - 'add_show_end_time' => '01:00', - 'add_show_duration' => '01h 00m')); - - $formRepeats->populate(array('add_show_end_date' => date("Y-m-d"))); - - $p_view->addNewShow = true; - }*/ - - /* This function is responsible for handling the case where an individual - * show instance in a repeating show was edited (via the context menu in the Calendar). - * There is still lots of clean-up to do. For example we shouldn't be passing $controller into - * this method to manipulate the view (this should be done inside the controller function). With - * 2.1 deadline looming, this is OK for now. -Martin */ - public static function updateShowInstance($data, $controller) - { - $formWhat = new Application_Form_AddShowWhat(); - $formWhen = new Application_Form_AddShowWhen(); - $formRepeats = new Application_Form_AddShowRepeats(); - $formWho = new Application_Form_AddShowWho(); - $formStyle = new Application_Form_AddShowStyle(); - $formLive = new Application_Form_AddShowLiveStream(); - - $formWhat->removeDecorator('DtDdWrapper'); - $formWhen->removeDecorator('DtDdWrapper'); - $formRepeats->removeDecorator('DtDdWrapper'); - $formWho->removeDecorator('DtDdWrapper'); - $formStyle->removeDecorator('DtDdWrapper'); - $formLive->removeDecorator('DtDdWrapper'); - - $when = $formWhen->isValid($data); - - if ($when && $formWhen->checkReliantFields($data, true, null, true)) { - $start_dt = new DateTime($data['add_show_start_date']." ".$data['add_show_start_time'], - new DateTimeZone(date_default_timezone_get())); - $start_dt->setTimezone(new DateTimeZone('UTC')); - - $end_dt = new DateTime($data['add_show_end_date_no_repeat']." ".$data['add_show_end_time'], - new DateTimeZone(date_default_timezone_get())); - $end_dt->setTimezone(new DateTimeZone('UTC')); - - $ccShowInstance = CcShowInstancesQuery::create()->findPK($data["add_show_instance_id"]); - $ccShowInstance->setDbStarts($start_dt); - $ccShowInstance->setDbEnds($end_dt); - $ccShowInstance->save(); - - Application_Model_Schedule::createNewFormSections($controller->view); - - return true; - } else { - $formWhat->disable(); - $formWhen->disableRepeatCheckbox(); - $formRepeats->disable(); - $formWho->disable(); - $formStyle->disable(); - //$formLive->disable(); - - $controller->view->what = $formWhat; - $controller->view->when = $formWhen; - $controller->view->repeats = $formRepeats; - $controller->view->who = $formWho; - $controller->view->style = $formStyle; - $controller->view->live = $formLive; - - return false; - } - } - - /* This function is responsible for handling the case where the entire show (not a single show instance) - * was edited (via the context menu in the Calendar). - * There is still lots of clean-up to do. For example we shouldn't be passing $controller into - * this method to manipulate the view (this should be done inside the controller function). With - * 2.1 deadline looming, this is OK for now. - * Another clean-up is to move all the form manipulation to the proper form class..... - * -Martin - */ - /*public static function addUpdateShow($data, $controller, $validateStartDate, - $originalStartDate=null, $update=false, $instanceId=null) - { - $userInfo = Zend_Auth::getInstance()->getStorage()->read(); - $user = new Application_Model_User($userInfo->id); - $isAdminOrPM = $user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER)); - - $record = false; - - $formWhat = new Application_Form_AddShowWhat(); - $formWho = new Application_Form_AddShowWho(); - $formWhen = new Application_Form_AddShowWhen(); - $formRepeats = new Application_Form_AddShowRepeats(); - $formStyle = new Application_Form_AddShowStyle(); - $formLive = new Application_Form_AddShowLiveStream(); - - $formWhat->removeDecorator('DtDdWrapper'); - $formWho->removeDecorator('DtDdWrapper'); - $formWhen->removeDecorator('DtDdWrapper'); - $formRepeats->removeDecorator('DtDdWrapper'); - $formStyle->removeDecorator('DtDdWrapper'); - $formLive->removeDecorator('DtDdWrapper'); - - $what = $formWhat->isValid($data); - $when = $formWhen->isValid($data); - $live = $formLive->isValid($data); - if ($when) { - $when = $formWhen->checkReliantFields($data, $validateStartDate, $originalStartDate, $update, $instanceId); - } - - //The way the following code works is that is parses the hour and - //minute from a string with the format "1h 20m" or "2h" or "36m". - //So we are detecting whether an hour or minute value exists via strpos - //and then parse appropriately. A better way to do this in the future is - //actually pass the format from javascript in the format hh:mm so we don't - //have to do this extra String parsing. - $hPos = strpos($data["add_show_duration"], 'h'); - $mPos = strpos($data["add_show_duration"], 'm'); - - $hValue = 0; - $mValue = 0; - - if ($hPos !== false) { - $hValue = trim(substr($data["add_show_duration"], 0, $hPos)); - } - if ($mPos !== false) { - $hPos = $hPos === false ? 0 : $hPos+1; - $mValue = trim(substr($data["add_show_duration"], $hPos, -1 )); - } - - $data["add_show_duration"] = $hValue.":".$mValue; - - if ($data["add_show_repeats"]) { - $repeats = $formRepeats->isValid($data); - if ($repeats) { - $repeats = $formRepeats->checkReliantFields($data); - } - } else { - $repeats = 1; - } - - $who = $formWho->isValid($data); - $style = $formStyle->isValid($data); - if ($what && $when && $repeats && $who && $style && $live) { - if ($isAdminOrPM) { - Application_Model_Show::create($data); - } - - //send back a new form for the user. - Application_Model_Schedule::createNewFormSections($controller->view); - - //$controller->view->newForm = $controller->view->render('schedule/add-show-form.phtml'); - return true; - } else { - $controller->view->what = $formWhat; - $controller->view->when = $formWhen; - $controller->view->repeats = $formRepeats; - $controller->view->who = $formWho; - $controller->view->style = $formStyle; - $controller->view->live = $formLive; - //$controller->view->addNewShow = !$editShow; - //$controller->view->form = $controller->view->render('schedule/add-show-form.phtml'); - return false; - } - }*/ - public static function checkOverlappingShows($show_start, $show_end, $update=false, $instanceId=null, $showId=null) { + //if the show instance does not exist or was deleted, return false + if (!is_null($showId)) { + $ccShowInstance = CcShowInstancesQuery::create() + ->filterByDbShowId($showId) + ->filterByDbStarts($show_start->format("Y-m-d H:i:s")) + ->findOne(); + } elseif (!is_null($instanceId)) { + $ccShowInstance = CcShowInstancesQuery::create() + ->filterByDbId($instanceId) + ->findOne(); + } + if ($update && ($ccShowInstance && $ccShowInstance->getDbModifiedInstance() == true)) { + return false; + } + $overlapping = false; $params = array( diff --git a/airtime_mvc/application/models/Scheduler.php b/airtime_mvc/application/models/Scheduler.php index 661df065b..9cb79927c 100644 --- a/airtime_mvc/application/models/Scheduler.php +++ b/airtime_mvc/application/models/Scheduler.php @@ -19,6 +19,7 @@ class Application_Model_Scheduler private $user; private $crossfadeDuration; + private $applyCrossfades = true; private $checkUserPermissions = true; @@ -400,6 +401,7 @@ class Application_Model_Scheduler //check for if the show has started. if (bccomp( $nEpoch , $sEpoch , 6) === 1) { + $this->applyCrossfades = false; //need some kind of placeholder for cc_schedule. //playout_status will be -1. $nextDT = $this->nowDT; @@ -454,8 +456,8 @@ class Application_Model_Scheduler $itemEndDT = $this->findEndTime($itemStartDT, $item["clip_length"]); $update_sql = "UPDATE cc_schedule SET ". - "starts = '{$itemStartDT->format("Y-m-d H:i:s")}', ". - "ends = '{$itemEndDT->format("Y-m-d H:i:s")}' ". + "starts = '{$itemStartDT->format("Y-m-d H:i:s.u")}', ". + "ends = '{$itemEndDT->format("Y-m-d H:i:s.u")}' ". "WHERE id = {$item["id"]}"; Application_Common_Database::prepareAndExecute( $update_sql, array(), Application_Common_Database::EXECUTE); @@ -533,6 +535,9 @@ class Application_Model_Scheduler $linked = false; foreach ($scheduleItems as $schedule) { + //reset + $this->applyCrossfades = true; + $id = intval($schedule["id"]); /* Find out if the show where the cursor position (where an item will @@ -595,6 +600,9 @@ class Application_Model_Scheduler $excludePositions = array(); foreach($instances as &$instance) { + //reset + $this->applyCrossfades = true; + $instanceId = $instance["id"]; if ($id !== 0) { /* We use the selected cursor's position to find the same @@ -609,16 +617,41 @@ class Application_Model_Scheduler $linkedItemEnds = Application_Common_Database::prepareAndExecute( $linkedItem_sql, array(), Application_Common_Database::COLUMN); - $nextStartDT = $this->findNextStartTime( - new DateTime($linkedItemEnds, new DateTimeZone("UTC")), - $instanceId); + if (!$linkedItemEnds) { + //With dynamic smart blocks there may be different number of items in + //each show. In case the position does not exist we need to select + //the end time of the last position + $maxPos_sql = "SELECT max(position) from cc_schedule ". + "WHERE instance_id = {$instanceId}"; + $pos = Application_Common_Database::prepareAndExecute( + $maxPos_sql, array(), Application_Common_Database::COLUMN); - $pos++; + //show instance has no scheduled tracks + if (empty($pos)) { + $pos = 0; + $nextStartDT = new DateTime($instance["starts"], new DateTimeZone("UTC")); + } else { - /* Show is not empty so we need to apply crossfades - * for the first inserted item - */ - $applyCrossfades = true; + $linkedItem_sql = "SELECT ends FROM cc_schedule ". + "WHERE instance_id = {$instanceId} ". + "AND position = {$pos} ". + "AND playout_status != -1"; + $linkedItemEnds = Application_Common_Database::prepareAndExecute( + $linkedItem_sql, array(), Application_Common_Database::COLUMN); + + $nextStartDT = $this->findNextStartTime( + new DateTime($linkedItemEnds, new DateTimeZone("UTC")), + $instanceId); + } + } else { + $nextStartDT = $this->findNextStartTime( + new DateTime($linkedItemEnds, new DateTimeZone("UTC")), + $instanceId); + + $pos++; + } + + //$pos++; } //selected empty row to add after else { @@ -631,7 +664,7 @@ class Application_Model_Scheduler /* Show is empty so we don't need to calculate crossfades * for the first inserted item */ - $applyCrossfades = false; + $this->applyCrossfades = false; } if (!in_array($instanceId, $affectedShowInstances)) { @@ -646,7 +679,7 @@ class Application_Model_Scheduler $pstart = microtime(true); - if ($applyCrossfades) { + if ($this->applyCrossfades) { $initalStartDT = clone $this->findTimeDifference( $nextStartDT, $this->crossfadeDuration); } else { @@ -702,6 +735,13 @@ class Application_Model_Scheduler $sched = Application_Common_Database::prepareAndExecute( $movedItem_sql, array(), Application_Common_Database::SINGLE); } + /* If we don't find a schedule item it means the linked + * shows have a different amount of items (dyanmic block) + * and we should skip the item move for this show instance + */ + if (!$sched) { + continue; + } $excludeIds[] = intval($sched["id"]); $file["cliplength"] = $sched["clip_length"]; @@ -730,7 +770,7 @@ class Application_Model_Scheduler default: break; } - if ($applyCrossfades) { + if ($this->applyCrossfades) { $nextStartDT = $this->findTimeDifference($nextStartDT, $this->crossfadeDuration); $endTimeDT = $this->findEndTime($nextStartDT, $file['cliplength']); @@ -738,14 +778,14 @@ class Application_Model_Scheduler /* Set it to false because the rest of the crossfades * will be applied after we insert each item */ - $applyCrossfades = false; + $this->applyCrossfades = false; } $endTimeDT = $this->findEndTime($nextStartDT, $file['cliplength']); if ($doInsert) { $values[] = "(". - "'{$nextStartDT->format("Y-m-d H:i:s")}', ". - "'{$endTimeDT->format("Y-m-d H:i:s")}', ". + "'{$nextStartDT->format("Y-m-d H:i:s.u")}', ". + "'{$endTimeDT->format("Y-m-d H:i:s.u")}', ". "'{$file["cuein"]}', ". "'{$file["cueout"]}', ". "'{$file["fadein"]}', ". @@ -758,8 +798,8 @@ class Application_Model_Scheduler } elseif ($doUpdate) { $update_sql = "UPDATE cc_schedule SET ". - "starts = '{$nextStartDT->format("Y-m-d H:i:s")}', ". - "ends = '{$endTimeDT->format("Y-m-d H:i:s")}', ". + "starts = '{$nextStartDT->format("Y-m-d H:i:s.u")}', ". + "ends = '{$endTimeDT->format("Y-m-d H:i:s.u")}', ". "cue_in = '{$file["cuein"]}', ". "cue_out = '{$file["cueout"]}', ". "fade_in = '{$file["fadein"]}', ". @@ -827,8 +867,8 @@ class Application_Model_Scheduler $endTimeDT = $this->findEndTime($nextStartDT, $item["clip_length"]); $endTimeDT = $this->findTimeDifference($endTimeDT, $this->crossfadeDuration); $update_sql = "UPDATE cc_schedule SET ". - "starts = '{$nextStartDT->format("Y-m-d H:i:s")}', ". - "ends = '{$endTimeDT->format("Y-m-d H:i:s")}', ". + "starts = '{$nextStartDT->format("Y-m-d H:i:s.u")}', ". + "ends = '{$endTimeDT->format("Y-m-d H:i:s.u")}', ". "position = {$pos} ". "WHERE id = {$item["id"]}"; Application_Common_Database::prepareAndExecute( @@ -1140,6 +1180,7 @@ class Application_Model_Scheduler foreach ($instances as $instance) { $instance->updateScheduleStatus($this->con); + $instance->correctSchedulePositions(); } //update the last scheduled timestamp. diff --git a/airtime_mvc/application/models/Show.php b/airtime_mvc/application/models/Show.php index f5cac2aa0..4271d2d55 100644 --- a/airtime_mvc/application/models/Show.php +++ b/airtime_mvc/application/models/Show.php @@ -172,51 +172,118 @@ SQL; $show->delete(); } - public function resizeShow($deltaDay, $deltaMin) + public function resizeShow($deltaDay, $deltaMin, $instanceId) { $con = Propel::getConnection(); if ($deltaDay > 0) { return _("Shows can have a max length of 24 hours."); } - - $utc = new DateTimeZone("UTC"); - - $nowDateTime = new DateTime("now", $utc); - $showInstances = CcShowInstancesQuery::create() - ->filterByDbShowId($this->_showId) - ->find($con); + $utcTimezone = new DateTimeZone("UTC"); + $nowDateTime = new DateTime("now", $utcTimezone); + + //keep track of cc_show_day entries we need to update + $showDayIds = array(); + + /* + * If the resized show is an edited instance of a repeating show we + * need to treat it as a separate show and not resize the other instances + * + * Also, if the resized show has edited instances, we need to exclude + * those from the resize + */ + $ccShow = CcShowQuery::create()->findPk($this->_showId); + if ($ccShow->isRepeating()) { + + //convert instance to local timezone + $ccShowInstance = CcShowInstancesQuery::create()->findPk($instanceId); + $startsDT = $ccShowInstance->getDbStarts(null); + $timezone = $ccShow->getFirstCcShowDay()->getDbTimezone(); + $startsDT->setTimezone(new DateTimeZone($timezone)); + + /* Get cc_show_day for the current instance. If we don't find one + * we know it is a repeat interval of one of cc_show_days first + * show and we can assume we aren't resizing a modified instance + */ + $ccShowDay = CcShowDaysQuery::create() + ->filterByDbFirstShow($startsDT->format("Y-m-d")) + ->filterByDbStartTime($startsDT->format("H:i:s")) + ->filterByDbShowId($this->_showId) + ->findOne(); + + /* Check if this cc_show_day rule is non-repeating. If it is, then + * we know this instance was edited out of the repeating sequence + */ + if (!$ccShowDay || $ccShowDay->getDbRepeatType() != -1) { + $ccShowDays = $ccShow->getRepeatingCcShowDays(); + foreach ($ccShowDays as $day) { + array_push($showDayIds, $day->getDbId()); + } + + $excludeIds = $ccShow->getEditedRepeatingInstanceIds(); + + //exlcude edited instances from resize + $showInstances = CcShowInstancesQuery::create() + ->filterByDbShowId($this->_showId) + ->filterByDbModifiedInstance(false) + ->filterByDbId($excludeIds, criteria::NOT_IN) + ->find(); + } elseif ($ccShowDay->getDbRepeatType() == -1) { + array_push($showDayIds, $ccShowDay->getDbId()); + + //treat edited instance as separate show for resize + $showInstances = CcShowInstancesQuery::create() + ->filterByDbId($instanceId) + ->find(); + } + } else { + $ccShowDays = $ccShow->getCcShowDayss(); + foreach ($ccShowDays as $day) { + array_push($showDayIds, $day->getDbId()); + } + + $showInstances = CcShowInstancesQuery::create() + ->filterByDbShowId($this->_showId) + ->find($con); + } /* Check two things: 1. If the show being resized and any of its repeats end in the past 2. If the show being resized and any of its repeats overlap with other scheduled shows */ + //keep track of instance ids for update show instances start/end times + $instanceIds = array(); + $displayTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + + //check if new show time overlaps with any other shows foreach ($showInstances as $si) { - $startsDateTime = new DateTime($si->getDbStarts(), new DateTimeZone("UTC")); - $endsDateTime = new DateTime($si->getDbEnds(), new DateTimeZone("UTC")); + array_push($instanceIds, $si->getDbId()); + + $startsDateTime = $si->getDbStarts(null); + $endsDateTime = $si->getDbEnds(null); /* The user is moving the show on the calendar from the perspective of local time. * incase a show is moved across a time change border offsets should be added to the local * timestamp and then converted back to UTC to avoid show time changes */ - $startsDateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); - $endsDateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); + $startsDateTime->setTimezone($displayTimezone); + $endsDateTime->setTimezone($displayTimezone); - $newStartsDateTime = Application_Model_ShowInstance::addDeltas($startsDateTime, $deltaDay, $deltaMin); + //$newStartsDateTime = Application_Model_ShowInstance::addDeltas($startsDateTime, $deltaDay, $deltaMin); $newEndsDateTime = Application_Model_ShowInstance::addDeltas($endsDateTime, $deltaDay, $deltaMin); - + if ($newEndsDateTime->getTimestamp() < $nowDateTime->getTimestamp()) { return _("End date/time cannot be in the past"); } //convert our new starts/ends to UTC. - $newStartsDateTime->setTimezone($utc); - $newEndsDateTime->setTimezone($utc); + //$newStartsDateTime->setTimezone($utc); + $newEndsDateTime->setTimezone($utcTimezone); $overlapping = Application_Model_Schedule::checkOverlappingShows( - $newStartsDateTime, $newEndsDateTime, true, $si->getDbId()); + $startsDateTime, $newEndsDateTime, true, $si->getDbId()); if ($overlapping) { return _("Cannot schedule overlapping shows.\nNote: Resizing a repeating show ". @@ -228,39 +295,30 @@ SQL; $hours = ($hours > 0) ? floor($hours) : ceil($hours); $mins = abs($deltaMin % 60); - //current timesamp in UTC. - $current_timestamp = gmdate("Y-m-d H:i:s"); - - $sql_gen = << :current_timestamp1) - AND ((ends + :deltaDay2::INTERVAL + :interval2::INTERVAL - starts) <= interval '24:00') -SQL; + $sql_gen = "UPDATE cc_show_instances ". + "SET ends = (ends + :deltaDay1::INTERVAL + :interval1::INTERVAL) ". + "WHERE (id IN (".implode($instanceIds, ",").") ". + "AND ends > :current_timestamp1) ". + "AND ((ends + :deltaDay2::INTERVAL + :interval2::INTERVAL - starts) <= interval '24:00')"; Application_Common_Database::prepareAndExecute($sql_gen, array( ':deltaDay1' => "$deltaDay days", ':interval1' => "$hours:$mins", - ':show_id1' => $this->_showId, - ':current_timestamp1' => $current_timestamp, + ':current_timestamp1' => $nowDateTime->format("Y-m-d H:i:s"), ':deltaDay2' => "$deltaDay days", ':interval2' => "$hours:$mins" ), "execute"); - $sql_gen = << "$deltaDay days", ':interval3' => "$hours:$mins", - ':show_id2' => $this->_showId, ':deltaDay4' => "$deltaDay days", ':interval4' => "$hours:$mins" ), "execute"); @@ -278,8 +336,8 @@ SQL; CcShowInstancesPeer::clearInstancePool(); $instances = CcShowInstancesQuery::create() - ->filterByDbEnds($current_timestamp, Criteria::GREATER_THAN) - ->filterByDbShowId($this->_showId) + ->filterByDbEnds($nowDateTime->format("Y-m-d H:i:s"), Criteria::GREATER_THAN) + ->filterByDbId($instanceIds, Criteria::IN) ->find($con); foreach ($instances as $instance) { @@ -362,55 +420,6 @@ SQL; return !is_null($showInstancesRow); } - /** - * Get start time and absolute start date for a recorded - * shows rebroadcasts. For example start date format would be - * YYYY-MM-DD and time would HH:MM - * - * @return array - * array of associate arrays containing "start_date" and "start_time" - */ - public function getRebroadcastsAbsolute() - { - $sql = << $this->getId() ), 'all' ); - - $rebroadcastsLocal = array(); - //get each rebroadcast show in cc_show_instances, convert to current timezone to get start date/time. - /*TODO: refactor the following code to get rid of the $i temporary - variable. -- RG*/ - $i = 0; - - $utc = new DateTimeZone("UTC"); - $dtz = new DateTimeZone( date_default_timezone_get() ); - - foreach ($rebroadcasts as $show) { - $startDateTime = new DateTime($show["starts"], $utc); - $startDateTime->setTimezone($dtz); - - $rebroadcastsLocal[$i]["start_date"] = - $startDateTime->format("Y-m-d"); - $rebroadcastsLocal[$i]["start_time"] = - $startDateTime->format("H:i"); - - $i = $i + 1; - } - - return $rebroadcastsLocal; - } - /** * Get start time and relative start date for a recorded * shows rebroadcasts. For example start date format would be @@ -453,9 +462,10 @@ SQL; } /** - * Get the repeat type of the show. Show can have repeat - * type of "weekly", "bi-weekly" and "monthly". These values - * are represented by 0, 1, and 2 respectively. + * Get the repeat type of the show. Show can have repeat type of + * "weekly", "every 2 weeks", "monthly", "monthly on the same weekday", + * "every 3 weeks" and "every 4 weeks". These values are represented + * by 0, 1, 2, 3, 4 and 5, respectively. * * @return int * Return the integer corresponding to the repeat type. @@ -648,10 +658,7 @@ SQL; */ public function isStartDateTimeInPast() { - $date = new Application_Common_DateHelper; - $current_timestamp = $date->getUtcTimestamp(); - - return ($current_timestamp > ($this->getStartDate()." ".$this->getStartTime())); + return (gmdate("Y-m-d H:i:s") > ($this->getStartDate()." ".$this->getStartTime())); } /** @@ -692,9 +699,7 @@ SQL; { //need to update cc_show_instances, cc_show_days $con = Propel::getConnection(); - - $date = new Application_Common_DateHelper; - $timestamp = $date->getUtcTimestamp(); + $timestamp = gmdate("Y-m-d H:i:s"); $stmt = $con->prepare("UPDATE cc_show_days " ."SET duration = :add_show_duration " @@ -718,67 +723,6 @@ SQL; ':timestamp' => $timestamp), "execute"); } - private function updateStartDateTime($p_data, $p_endDate) - { - $date = new Application_Common_DateHelper; - $timestamp = $date->getTimestamp(); - - //TODO fix this from overwriting info. - $sql = "UPDATE cc_show_days " - ."SET start_time = :start_time::time, " - ."first_show = :start_date::date, "; - if (strlen ($p_endDate) == 0) { - $sql .= "last_show = NULL "; - } else { - $sql .= "last_show = :end_date::date"; - } - $sql .= "WHERE show_id = :show_id"; - - $map = array(":start_time" => $p_data['add_show_start_time'], - ':start_date' => $p_data['add_show_start_date'], - ':end_date' => $p_endDate, - ':show_id' => $p_data['add_show_id'], - ); - - $res = Application_Common_Database::prepareAndExecute($sql, $map, - Application_Common_Database::EXECUTE); - - $dtOld = new DateTime($this->getStartDate()." ".$this->getStartTime(), new DateTimeZone("UTC")); - $dtNew = new DateTime($p_data['add_show_start_date']." ".$p_data['add_show_start_time'], - new DateTimeZone(date_default_timezone_get())); - $diff = $dtOld->getTimestamp() - $dtNew->getTimestamp(); - - $sql = "UPDATE cc_show_instances " - ."SET starts = starts + :diff1::interval, " - ."ends = ends + :diff2::interval " - ."WHERE show_id = :show_id " - ."AND starts > :timestamp::timestamp"; - $map = array( - ":diff1"=>"$diff sec", - ":diff2"=>"$diff sec", - ":show_id"=>$p_data['add_show_id'], - ":timestamp"=>$timestamp, - ); - $res = Application_Common_Database::prepareAndExecute($sql, $map, - Application_Common_Database::EXECUTE); - - $showInstanceIds = $this->getAllFutureInstanceIds(); - if (count($showInstanceIds) > 0 && $diff != 0) { - $showIdsImploded = implode(",", $showInstanceIds); - $sql = "UPDATE cc_schedule " - ."SET starts = starts + :diff1::interval, " - ."ends = ends + :diff2::interval " - ."WHERE instance_id IN (:show_ids)"; - $map = array( - ":diff1"=>"$diff sec", - ":diff2"=>"$diff sec", - ":show_ids"=>$showIdsImploded, - ); - $res = Application_Common_Database::prepareAndExecute($sql, $map, - Application_Common_Database::EXECUTE); - } - } - public function getDuration($format=false) { $showDay = CcShowDaysQuery::create()->filterByDbShowId($this->getId())->findOne(); @@ -1028,10 +972,10 @@ SQL; $content_count = Application_Model_ShowInstance::getContentCount( $p_start, $p_end); $isFull = Application_Model_ShowInstance::getIsFull($p_start, $p_end); - $timezone = date_default_timezone_get(); - $current_timezone = new DateTimeZone($timezone); - $utc = new DateTimeZone("UTC"); - $now = new DateTime("now", $utc); + + $displayTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); + $utcTimezone = new DateTimeZone("UTC"); + $now = new DateTime("now", $utcTimezone); foreach ($shows as &$show) { $options = array(); @@ -1042,13 +986,13 @@ SQL; } if (isset($show["parent_starts"])) { - $parentStartsDT = new DateTime($show["parent_starts"], $utc); + $parentStartsDT = new DateTime($show["parent_starts"], $utcTimezone); } $startsDT = DateTime::createFromFormat("Y-m-d G:i:s", - $show["starts"],$utc); + $show["starts"], $utcTimezone); $endsDT = DateTime::createFromFormat("Y-m-d G:i:s", - $show["ends"], $utc); + $show["ends"], $utcTimezone); if( $p_editable ) { if ($show["record"] && $now > $startsDT) { @@ -1061,13 +1005,17 @@ SQL; } } - $startsDT->setTimezone($current_timezone); - $endsDT->setTimezone($current_timezone); + $startsDT->setTimezone($displayTimezone); + $endsDT->setTimezone($displayTimezone); $options["show_empty"] = (array_key_exists($show['instance_id'], $content_count)) ? 0 : 1; - $options["show_partial_filled"] = !$isFull[$show['instance_id']]; + if (array_key_exists($show['instance_id'], $isFull)) { + $options["show_partial_filled"] = !$isFull[$show['instance_id']]; + } else { + $options["show_partial_filled"] = true; + } $event = array(); @@ -1082,6 +1030,14 @@ SQL; $event["rebroadcast"] = intval($show["rebroadcast"]); $event["soundcloud_id"] = is_null($show["soundcloud_id"]) ? -1 : $show["soundcloud_id"]; + + //for putting the now playing icon on the show. + if ($now > $startsDT && $now < $endsDT) { + $event["nowPlaying"] = true; + } + else { + $event["nowPlaying"] = false; + } //event colouring if ($show["color"] != "") { @@ -1104,57 +1060,24 @@ SQL; /** * Calculates the percentage of a show scheduled given the start and end times in date/time format * and the time_filled as the total time the schow is scheduled for in time format. + * + * TODO when using propel properly this should be a method on the propel show instance model. **/ private static function getPercentScheduled($p_starts, $p_ends, $p_time_filled) { - $durationSeconds = (strtotime($p_ends) - strtotime($p_starts)); - $time_filled = Application_Model_Schedule::WallTimeToMillisecs($p_time_filled) / 1000; - $percent = ceil(( $time_filled / $durationSeconds) * 100); - + $utcTimezone = new DatetimeZone("UTC"); + $startDt = new DateTime($p_starts, $utcTimezone); + $endDt = new DateTime($p_ends, $utcTimezone); + $durationSeconds = intval($endDt->format("U")) - intval($startDt->format("U")); + $time_filled = Application_Common_DateHelper::playlistTimeToSeconds($p_time_filled); + if ($durationSeconds != 0) { //Prevent division by zero if the show duration somehow becomes zero. + $percent = ceil(( $time_filled / $durationSeconds) * 100); + } else { + $percent = 0; + } return $percent; } - /* Takes in a UTC DateTime object. - * Converts this to local time, since cc_show days - * requires local time. */ - public function setShowFirstShow($p_dt) - { - //clone object since we are modifying it and it was passed by reference. - $dt = clone $p_dt; - - $dt->setTimezone(new DateTimeZone(date_default_timezone_get())); - - $showDay = CcShowDaysQuery::create() - ->filterByDbShowId($this->_showId) - ->findOne(); - - $showDay->setDbFirstShow($dt)->setDbStartTime($dt) - ->save(); - - //Logging::info("setting show's first show."); - } - - /* Takes in a UTC DateTime object - * Converts this to local time, since cc_show days - * requires local time. */ - public function setShowLastShow($p_dt) - { - //clone object since we are modifying it and it was passed by reference. - $dt = clone $p_dt; - - $dt->setTimezone(new DateTimeZone(date_default_timezone_get())); - - //add one day since the Last Show date in CcShowDays is non-inclusive. - $dt->add(new DateInterval("P1D")); - - $showDay = CcShowDaysQuery::create() - ->filterByDbShowId($this->_showId) - ->findOne(); - - $showDay->setDbLastShow($dt) - ->save(); - } - /** * Given time $timeNow, returns the show being played right now. * Times are all in UTC time. @@ -1164,11 +1087,9 @@ SQL; */ public static function getCurrentShow($timeNow=null) { - $CC_CONFIG = Config::getConfig(); $con = Propel::getConnection(); if ($timeNow == null) { - $date = new Application_Common_DateHelper; - $timeNow = $date->getUtcTimestamp(); + $timeNow = gmdate("Y-m-d H:i:s"); } //TODO, returning starts + ends twice (once with an alias). Unify this after the 2.0 release. --Martin $sql = <<getTimeZone() == $timeZone); + $CC_CONFIG = Config::getConfig(); $con = Propel::getConnection(); // @@ -1235,9 +1164,10 @@ ORDER BY si.starts SQL; $stmt = $con->prepare($sql); - - $stmt->bindValue(':timeNow1', $p_timeNow); - $stmt->bindValue(':timeNow2', $p_timeNow); + + $utcNowStr = $utcNow->format("Y-m-d H:i:s"); + $stmt->bindValue(':timeNow1', $utcNowStr); + $stmt->bindValue(':timeNow2', $utcNowStr); if ($stmt->execute()) { $rows = $stmt->fetchAll(); @@ -1252,12 +1182,14 @@ SQL; $results['currentShow'] = array(); $results['nextShow'] = array(); - $timeNowAsMillis = strtotime($p_timeNow); - for ($i = 0; $i < $numberOfRows; ++$i) { + //All shows start/end times are stored in the database as UTC. + $showStartTime = new DateTime($rows[$i]['starts'], $timeZone); + $showEndTime = new DateTime($rows[$i]['ends'], $timeZone); + //Find the show that is within the current time. - if ((strtotime($rows[$i]['starts']) <= $timeNowAsMillis) - && (strtotime($rows[$i]['ends']) > $timeNowAsMillis)) { + if (($showStartTime <= $utcNow) && ($showEndTime > $utcNow)) + { if ($i-1 >= 0) { $results['previousShow'][0] = array( "id" => $rows[$i-1]['id'], @@ -1290,11 +1222,11 @@ SQL; break; } //Previous is any row that ends after time now capture it in case we need it later. - if (strtotime($rows[$i]['ends']) < $timeNowAsMillis ) { + if ($showEndTime < $utcNow ) { $previousShowIndex = $i; } //if we hit this we know we've gone to far and can stop looping. - if (strtotime($rows[$i]['starts']) > $timeNowAsMillis) { + if ($showStartTime > $utcNow) { $results['nextShow'][0] = array( "id" => $rows[$i]['id'], "instance_id" => $rows[$i]['instance_id'], @@ -1386,25 +1318,6 @@ SQL; return Application_Common_Database::prepareAndExecute( $sql, $params, 'all'); } - /** - * Convert the columns given in the array $columnsToConvert in the - * database result $rows to local timezone. - * - * @param type $rows arrays of arrays containing database query result - * @param type $columnsToConvert array of column names to convert - */ - public static function convertToLocalTimeZone(&$rows, $columnsToConvert) - { - if (!is_array($rows)) { - return; - } - foreach ($rows as &$row) { - foreach ($columnsToConvert as $column) { - $row[$column] = Application_Common_DateHelper::ConvertToLocalDateTimeString($row[$column]); - } - } - } - public static function getMaxLengths() { $sql = <<startDT = $p_startDT; - $this->endDT = $p_endDT; - $this->timezone = date_default_timezone_get(); - $this->user = Application_Model_User::getCurrentUser(); - $this->opts = $p_opts; - $this->epoch_now = floatval(microtime(true)); + $this->startDT = $p_startDT; + $this->endDT = $p_endDT; + $this->timezone = Application_Model_Preference::GetUserTimezone(); + $this->user = Application_Model_User::getCurrentUser(); + $this->opts = $p_opts; + $this->epoch_now = floatval(microtime(true)); $this->currentShow = false; } @@ -282,13 +282,13 @@ class Application_Model_ShowBuilder $row["starts"] = $schedStartDT->format("H:i:s"); $row["ends"] = $schedEndDT->format("H:i:s"); - $cue_out = Application_Common_DateHelper::calculateLengthInSeconds($p_item['cue_out']); - $cue_in = Application_Common_DateHelper::calculateLengthInSeconds($p_item['cue_in']); + $cue_out = Application_Common_DateHelper::playlistTimeToSeconds($p_item['cue_out']); + $cue_in = Application_Common_DateHelper::playlistTimeToSeconds($p_item['cue_in']); $run_time = $cue_out-$cue_in; - $formatter = new LengthFormatter(Application_Common_DateHelper::ConvertMSToHHMMSSmm($run_time*1000)); - $row['runtime'] = $formatter->format(); + $formatter = new LengthFormatter(Application_Common_DateHelper::secondsToPlaylistTime($run_time)); + $row['runtime'] = $formatter->format(); $row["title"] = htmlspecialchars($p_item["file_track_title"]); $row["creator"] = htmlspecialchars($p_item["file_artist_name"]); diff --git a/airtime_mvc/application/models/ShowInstance.php b/airtime_mvc/application/models/ShowInstance.php index bae020a7b..f5fbf1aa7 100644 --- a/airtime_mvc/application/models/ShowInstance.php +++ b/airtime_mvc/application/models/ShowInstance.php @@ -164,43 +164,6 @@ SQL; $this->_showInstance->getDbModifiedInstance(); } - public function correctScheduleStartTimes() - { - $con = Propel::getConnection(); - - $instance_id = $this->getShowInstanceId(); - $sql = << $instance_id ), 'column' ); - - if ($scheduleStarts) { - $scheduleStartsEpoch = strtotime($scheduleStarts); - $showStartsEpoch = strtotime($this->getShowInstanceStart()); - - $diff = $showStartsEpoch - $scheduleStartsEpoch; - - if ($diff != 0) { - $sql = << $diff, - ':diff2' => $diff, - ':instanceId' => $instance_id ), 'execute'); - } - } - Application_Model_RabbitMq::PushSchedule(); - } - /* * @param $dateTime * php Datetime object to add deltas to @@ -239,152 +202,6 @@ SQL; return $newDateTime; } - public function moveShow($deltaDay, $deltaMin) - { - if ($this->getShow()->isRepeating()) { - return _("Can't drag and drop repeating shows"); - } - - $today_timestamp = time(); - $startsDateTime = new DateTime($this->getShowInstanceStart(), new DateTimeZone("UTC")); - $endsDateTime = new DateTime($this->getShowInstanceEnd(), new DateTimeZone("UTC")); - - if ($today_timestamp > $startsDateTime->getTimestamp()) { - return _("Can't move a past show"); - } - - //the user is moving the show on the calendar from the perspective of local time. - //incase a show is moved across a time change border offsets should be added to the localtime - //stamp and then converted back to UTC to avoid show time changes! - $startsDateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); - $endsDateTime->setTimezone(new DateTimeZone(date_default_timezone_get())); - - $newStartsDateTime = self::addDeltas($startsDateTime, $deltaDay, $deltaMin); - $newEndsDateTime = self::addDeltas($endsDateTime, $deltaDay, $deltaMin); - - //convert our new starts/ends to UTC. - $newStartsDateTime->setTimezone(new DateTimeZone("UTC")); - $newEndsDateTime->setTimezone(new DateTimeZone("UTC")); - - if ($today_timestamp > $newStartsDateTime->getTimestamp()) { - return _("Can't move show into past"); - } - - //check if show is overlapping - $overlapping = Application_Model_Schedule::checkOverlappingShows($newStartsDateTime, $newEndsDateTime, true, $this->getShowInstanceId()); - if ($overlapping) { - return _("Cannot schedule overlapping shows"); - } - - if ($this->isRecorded()) { - - //rebroadcasts should start at max 1 hour after a recorded show has ended. - $minRebroadcastStart = self::addDeltas($newEndsDateTime, 0, 60); - //check if we are moving a recorded show less than 1 hour before any of its own rebroadcasts. - $rebroadcasts = CcShowInstancesQuery::create() - ->filterByDbOriginalShow($this->_instanceId) - ->filterByDbStarts($minRebroadcastStart->format('Y-m-d H:i:s'), Criteria::LESS_THAN) - ->find(); - - if (count($rebroadcasts) > 0) { - return _("Can't move a recorded show less than 1 hour before its rebroadcasts."); - } - } - - if ($this->isRebroadcast()) { - - try { - $recordedShow = new Application_Model_ShowInstance($this->_showInstance->getDbOriginalShow()); - } - //recorded show doesn't exist. - catch (Exception $e) { - $this->_showInstance->delete(); - - return _("Show was deleted because recorded show does not exist!"); - } - - $recordEndDateTime = new DateTime($recordedShow->getShowInstanceEnd(), new DateTimeZone("UTC")); - $newRecordEndDateTime = self::addDeltas($recordEndDateTime, 0, 60); - - if ($newStartsDateTime->getTimestamp() < $newRecordEndDateTime->getTimestamp()) { - return _("Must wait 1 hour to rebroadcast."); - } - } - - $this->setShowStart($newStartsDateTime); - $this->setShowEnd($newEndsDateTime); - $this->correctScheduleStartTimes(); - - $show = new Application_Model_Show($this->getShowId()); - if (!$show->isRepeating() && is_null($this->isRebroadcast())) { - $show->setShowFirstShow($newStartsDateTime); - $show->setShowLastShow($newEndsDateTime); - } - - Application_Model_RabbitMq::PushSchedule(); - } - - public function resizeShow($deltaDay, $deltaMin) - { - $con = Propel::getConnection(); - - $hours = $deltaMin / 60; - - $hours = ($hours > 0) ? floor($hours) : ceil($hours); - - $mins = abs($deltaMin % 60); - - $today_timestamp = gmdate("Y-m-d H:i:s"); - $starts = $this->getShowInstanceStart(); - $ends = $this->getShowInstanceEnd(); - - if (strtotime($today_timestamp) > strtotime($starts)) { - return _("can't resize a past show"); - } - - //$sql = "SELECT timestamp '{$ends}' + interval '{$deltaDay} days' + interval '{$hours}:{$mins}'"; - $sql = "SELECT timestamp :ends + interval :deltaDays + interval :deltaTime"; - - $now_ends = Application_Common_Database::prepareAndExecute($sql, - array(':ends' => $ends, - ':deltaDays' => "$deltaDay days", - ':deltaTime' => "{$hours}:{$mins}"), 'column' - ); - - //only need to check overlap if show increased in size. - if (strtotime($now_ends) > strtotime($ends)) { - - $utcStartDateTime = new DateTime($ends, new DateTimeZone("UTC")); - $utcEndDateTime = new DateTime($now_ends, new DateTimeZone("UTC")); - - $overlap = Application_Model_Show::getShows($utcStartDateTime, $utcEndDateTime); - - if (count($overlap) > 0) { - // TODO : fix ghetto error handling -- RG - return _("Should not overlap shows"); - } - } - //with overbooking no longer need to check already scheduled content still fits. - - //must update length of all rebroadcast instances. - if ($this->isRecorded()) { - $sql = << "$deltaDay days", - ':interval' => "$hours:$mins", - ':instanceId' => $this->_instanceId ), 'execute'); - - } - - $this->setShowEnd($now_ends); - Application_Model_RabbitMq::PushSchedule(); - } - /** * Add a playlist as the last item of the current show. * @@ -635,9 +452,12 @@ SQL; { $durationSeconds = $this->getDurationSecs(); $timeSeconds = $this->getTimeScheduledSecs(); - - $percent = ceil(($timeSeconds / $durationSeconds) * 100); - + + if ($durationSeconds != 0) { //Prevent division by zero if the show duration is somehow zero. + $percent = ceil(($timeSeconds / $durationSeconds) * 100); + } else { + $percent = 0; + } return $percent; } @@ -779,11 +599,15 @@ SQL; ':instance_id2' => $this->_instanceId )); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $userTimezone = Application_Model_Preference::GetUserTimezone(); + $displayTimezone = new DateTimeZone($userTimezone); + $utcTimezone = new DateTimeZone("UTC"); foreach ($results as &$row) { - $dt = new DateTime($row["starts"], new DateTimeZone("UTC")); - $dt->setTimezone(new DateTimeZone(date_default_timezone_get())); + $dt = new DateTime($row["starts"], $utcTimezone); + $dt->setTimezone($displayTimezone); $row["starts"] = $dt->format("Y-m-d H:i:s"); if (isset($row['length'])) { @@ -811,21 +635,6 @@ SQL; return ($query !== false) ? $query : null; } - public function getShowEndGapTime() - { - $showEnd = $this->getShowInstanceEnd(); - $lastItemEnd = $this->getLastAudioItemEnd(); - - if (is_null($lastItemEnd)) { - $lastItemEnd = $this->getShowInstanceStart(); - } - - - $diff = strtotime($showEnd) - strtotime($lastItemEnd); - - return ($diff < 0) ? 0 : $diff; - } - public static function GetLastShowInstance($p_timeNow) { $sql = <<format(); - + //convert mtime and utime to localtime - $row['mtime'] = new DateTime($row['mtime'], new DateTimeZone('UTC')); - $row['mtime']->setTimeZone(new DateTimeZone(date_default_timezone_get())); + $row['mtime'] = new DateTime($row['mtime'], $utcTimezone); + $row['mtime']->setTimeZone($displayTimezone); $row['mtime'] = $row['mtime']->format('Y-m-d H:i:s'); - $row['utime'] = new DateTime($row['utime'], new DateTimeZone('UTC')); - $row['utime']->setTimeZone(new DateTimeZone(date_default_timezone_get())); + $row['utime'] = new DateTime($row['utime'], $utcTimezone); + $row['utime']->setTimeZone($displayTimezone); $row['utime'] = $row['utime']->format('Y-m-d H:i:s'); + + //need to convert last played to localtime if it exists. + if (isset($row['lptime'])) { + $row['lptime'] = new DateTime($row['lptime'], $utcTimezone); + $row['lptime']->setTimeZone($displayTimezone); + $row['lptime'] = $row['lptime']->format('Y-m-d H:i:s'); + } // we need to initalize the checkbox and image row because we do not retrieve // any data from the db for these and datatables will complain @@ -1325,20 +1354,14 @@ SQL; } } - public static function setIsScheduled($p_scheduleItem, $p_status, - $p_fileId=null) { + public static function setIsScheduled($fileId, $status) { - if (is_null($p_fileId)) { - $fileId = Application_Model_Schedule::GetFileId($p_scheduleItem); - } else { - $fileId = $p_fileId; - } $file = self::RecallById($fileId); $updateIsScheduled = false; if (!is_null($fileId) && !in_array($fileId, Application_Model_Schedule::getAllFutureScheduledFiles())) { - $file->_file->setDbIsScheduled($p_status)->save(); + $file->_file->setDbIsScheduled($status)->save(); $updateIsScheduled = true; } diff --git a/airtime_mvc/application/models/Webstream.php b/airtime_mvc/application/models/Webstream.php index a39b26bde..693a08146 100644 --- a/airtime_mvc/application/models/Webstream.php +++ b/airtime_mvc/application/models/Webstream.php @@ -408,6 +408,23 @@ class Application_Model_Webstream implements Application_Model_LibraryEditable return $webstream->getDbId(); } + + /* + * method is not used, webstreams aren't currently kept track of for isScheduled. + */ + public static function setIsScheduled($p_webstreamId, $p_status) { + + $webstream = CcWebstreamQuery::create()->findPK($p_webstreamId); + $updateIsScheduled = false; + + if (isset($webstream) && !in_array($p_webstreamId, + Application_Model_Schedule::getAllFutureScheduledWebstreams())) { + //$webstream->setDbIsScheduled($p_status)->save(); + $updateIsScheduled = true; + } + + return $updateIsScheduled; + } } class WebstreamNoPermissionException extends Exception {} diff --git a/airtime_mvc/application/models/airtime/CcFiles.php b/airtime_mvc/application/models/airtime/CcFiles.php index fe19ab114..f49c9154a 100644 --- a/airtime_mvc/application/models/airtime/CcFiles.php +++ b/airtime_mvc/application/models/airtime/CcFiles.php @@ -27,36 +27,6 @@ class CcFiles extends BaseCcFiles { return $length; } - public function getDbLength($format = "H:i:s.u") - { - return parent::getDbLength($format); - } - - public function setDbLength($v) - { - //we are using DateTime instead of DateInterval because the latter doesn't - //support subseconds :( - if ($v instanceof DateTime) { - $dt = $v; - } - else { - - try { - - $dt = new DateTime($v); - - } catch (Exception $x) { - throw new PropelException('Error parsing date/time value: ' . - var_export($v, true), $x); - } - } - - $this->length = $dt->format('H:i:s.u'); - $this->modifiedColumns[] = CcFilesPeer::LENGTH; - - return $this; - } - public function setDbTrackNumber($v) { $max = pow(2, 31)-1; diff --git a/airtime_mvc/application/models/airtime/CcPlayoutHistoryMetaData.php b/airtime_mvc/application/models/airtime/CcPlayoutHistoryMetaData.php index 890a4ca05..9eb4958a2 100644 --- a/airtime_mvc/application/models/airtime/CcPlayoutHistoryMetaData.php +++ b/airtime_mvc/application/models/airtime/CcPlayoutHistoryMetaData.php @@ -14,5 +14,21 @@ * @package propel.generator.airtime */ class CcPlayoutHistoryMetaData extends BaseCcPlayoutHistoryMetaData { + + /** + * Set the value of [value] column. + * + * @param string $v new value + * @return CcPlayoutHistoryMetaData The current object (for fluent API support) + */ + public function setDbValue($v) + { + //make sure the metadata isn't longer than the DB field. + $v = substr($v, 0, 128); + + parent::setDbValue($v); + + return $this; + } // setDbValue() } // CcPlayoutHistoryMetaData diff --git a/airtime_mvc/application/models/airtime/CcSchedule.php b/airtime_mvc/application/models/airtime/CcSchedule.php index db7fb19a7..9b6196f92 100644 --- a/airtime_mvc/application/models/airtime/CcSchedule.php +++ b/airtime_mvc/application/models/airtime/CcSchedule.php @@ -182,19 +182,20 @@ class CcSchedule extends BaseCcSchedule { */ public function setDbStarts($v) { + $utcTimeZone = new DateTimeZone('UTC'); + if ($v instanceof DateTime) { $dt = $v; + $dt->setTimezone($utcTimeZone); } else { // some string/numeric value passed; we normalize that so that we can // validate it. try { if (is_numeric($v)) { // if it's a unix timestamp - $dt = new DateTime('@'.$v, new DateTimeZone('UTC')); - // We have to explicitly specify and then change the time zone because of a - // DateTime bug: http://bugs.php.net/bug.php?id=43003 - $dt->setTimeZone(new DateTimeZone(date_default_timezone_get())); + $dt = new DateTime('@'.$v, $utcTimeZone); + } else { - $dt = new DateTime($v); + $dt = new DateTime($v, $utcTimeZone); } } catch (Exception $x) { throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x); @@ -216,20 +217,20 @@ class CcSchedule extends BaseCcSchedule { */ public function setDbEnds($v) { - + $utcTimeZone = new DateTimeZone('UTC'); + if ($v instanceof DateTime) { $dt = $v; + $dt->setTimezone($utcTimeZone); } else { // some string/numeric value passed; we normalize that so that we can // validate it. try { if (is_numeric($v)) { // if it's a unix timestamp - $dt = new DateTime('@'.$v, new DateTimeZone('UTC')); - // We have to explicitly specify and then change the time zone because of a - // DateTime bug: http://bugs.php.net/bug.php?id=43003 - $dt->setTimeZone(new DateTimeZone(date_default_timezone_get())); + $dt = new DateTime('@'.$v, $utcTimeZone); + } else { - $dt = new DateTime($v); + $dt = new DateTime($v, $utcTimeZone); } } catch (Exception $x) { throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x); diff --git a/airtime_mvc/application/models/airtime/CcShow.php b/airtime_mvc/application/models/airtime/CcShow.php index b345a8d05..91874eed6 100644 --- a/airtime_mvc/application/models/airtime/CcShow.php +++ b/airtime_mvc/application/models/airtime/CcShow.php @@ -5,7 +5,7 @@ /** * Skeleton subclass for representing a row from the 'cc_show' table. * - * + * * * You should add additional methods to this class to meet the * application requirements. This class will only be generated as @@ -15,8 +15,20 @@ */ class CcShow extends BaseCcShow { - public function getCcShowDays(){ - return CcShowDaysQuery::create()->filterByDbShowId($this->getDbId())->find(); + /* + * Returns all cc_show_day rules that belong to a cc_show and that are + * repeating. + * We do this because editing a single instance from a repeating sequence + * creates a new rule in cc_show_days with the same cc_show id and a repeat + * type of -1 (non-repeating). + * So when the entire cc_show is updated after that, the single edited + * instance can remain separate from the rest of the instances + */ + public function getRepeatingCcShowDays(){ + return CcShowDaysQuery::create() + ->filterByDbShowId($this->id) + ->filterByDbRepeatType(-1, Criteria::NOT_EQUAL) + ->find(); } /** @@ -54,6 +66,86 @@ class CcShow extends BaseCcShow { return $this->collCcShowDayss[0]; } + /** + * + * A repeating show may have a rule in cc_show_days with a repeat type + * of -1 (not repeating). This happens when a single instances was edited + * from the repeating sequence. + * + * When the repeating show gets edited in this case, we want to exclude all + * the edited instances from the update. We do this by not returning any of + * the cc_show_day rules with a -1 repeat type. + */ + public function getFirstRepeatingCcShowDay() + { + return CcShowDaysQuery::create() + ->filterByDbShowId($this->id) + ->filterByDbRepeatType(-1, Criteria::NOT_EQUAL) + ->orderByDbFirstShow() + ->findOne(); + } + + /** + * + * In order to determine if a show is repeating we need to check each + * cc_show_day entry and check if there are any non -1 repeat types. + * Because editing a single instances creates a new cc_show_day rule + * with a -1 (non repeating) repeat type we need to check all cc_show_day + * entries + */ + public function isRepeating() + { + //get all cc_show_day entries that are repeating + $ccShowDays = CcShowDaysQuery::create() + ->filterByDbShowId($this->id) + ->filterByDbRepeatType(0, Criteria::GREATER_EQUAL) + ->find(); + + if (!$ccShowDays->isEmpty()) { + return true; + } + + return false; + } + + /** + * Returns all cc_show_instances that have been edited out of + * a repeating sequence + */ + public function getEditedRepeatingInstanceIds() + { + //get cc_show_days that have been edited (not repeating) + $ccShowDays = CcShowDaysQuery::create() + ->filterByDbShowId($this->id) + ->filterByDbRepeatType(-1) + ->find(); + + $startsUTC = array(); + + $utc = new DateTimeZone("UTC"); + foreach ($ccShowDays as $day) { + //convert to UTC + $starts = new DateTime( + $day->getDbFirstShow()." ".$day->getDbStartTime(), + new DateTimeZone($day->getDbTimezone()) + ); + $starts->setTimezone($utc); + array_push($startsUTC, $starts->format("Y-m-d H:i:s")); + } + + $excludeInstances = CcShowInstancesQuery::create() + ->filterByDbShowId($this->id) + ->filterByDbStarts($startsUTC, criteria::IN) + ->find(); + + $excludeIds = array(); + foreach ($excludeInstances as $instance) { + array_push($excludeIds, $instance->getDbId()); + } + + return $excludeIds; + } + /** * Gets an array of CcShowInstances objects which contain a foreign key that references this object. * @@ -91,24 +183,21 @@ class CcShow extends BaseCcShow { public function isRecorded() { - $ccShowInstances = CcShowInstancesQuery::create() + $ccShowDay = CcShowDaysQuery::create() ->filterByDbShowId($this->getDbId()) ->filterByDbRecord(1) - ->filterByDbModifiedInstance(false) ->findOne(); - return (!is_null($ccShowInstances)); + return (!is_null($ccShowDay)); } public function isRebroadcast() { - $ccShowInstances = CcShowInstancesQuery::create() + $ccShowRebroadcast = CcShowRebroadcastQuery::create() ->filterByDbShowId($this->getDbId()) - ->filterByDbRebroadcast(1) - ->filterByDbModifiedInstance(false) ->findOne(); - return (!is_null($ccShowInstances)); + return (!is_null($ccShowRebroadcast)); } public function getRebroadcastsRelative() diff --git a/airtime_mvc/application/models/airtime/CcShowDays.php b/airtime_mvc/application/models/airtime/CcShowDays.php index 7b08204a2..2f187bf44 100644 --- a/airtime_mvc/application/models/airtime/CcShowDays.php +++ b/airtime_mvc/application/models/airtime/CcShowDays.php @@ -31,6 +31,7 @@ class CcShowDays extends BaseCcShowDays { return $dt; } + // Returns the start of a show in the timezone it was created in public function getLocalStartDateAndTime() { $dt = new DateTime( @@ -38,20 +39,19 @@ class CcShowDays extends BaseCcShowDays { new DateTimeZone($this->getDbTimezone()) ); - //make timezone current user specific - $dt->setTimezone(new DateTimeZone(Application_Model_Preference::GetTimezone())); - + //set timezone to that of the show + //$dt->setTimezone(new DateTimeZone($this->getDbTimezone())); return $dt; } /** * - * Enter description here ... - * @param DateTime $startDateTime first show in user's local time + * Returns the end of a show in the timezone it was created in + * @param DateTime $startDateTime first show in show's local time */ - public function getLocalEndDateAndTime($showStart) + public function getLocalEndDateAndTime() { - $startDateTime = clone $showStart; + $startDateTime = $this->getLocalStartDateAndTime(); $duration = explode(":", $this->getDbDuration()); return $startDateTime->add(new DateInterval('PT'.$duration[0].'H'.$duration[1].'M')); diff --git a/airtime_mvc/application/models/airtime/CcShowInstances.php b/airtime_mvc/application/models/airtime/CcShowInstances.php index 6fdaaf8a2..e7dace7b1 100644 --- a/airtime_mvc/application/models/airtime/CcShowInstances.php +++ b/airtime_mvc/application/models/airtime/CcShowInstances.php @@ -138,6 +138,29 @@ class CcShowInstances extends BaseCcShowInstances { $this->setDbLastScheduled(gmdate("Y-m-d H:i:s")); $this->save($con); } + + /** + * + * This function resets the cc_schedule table's position numbers so that + * tracks for each cc_show_instances start at position 1 + * + * The position numbers can become out of sync when the user deletes items + * from linekd shows filled with dyanmic smart blocks, where each instance + * has a different amount of scheduled items + */ + public function correctSchedulePositions() + { + $schedule = CcScheduleQuery::create() + ->filterByDbInstanceId($this->id) + ->orderByDbStarts() + ->find(); + + $pos = 0; + foreach ($schedule as $item) { + $item->setDbPosition($pos)->save(); + $pos++; + } + } /** * Computes the value of the aggregate column time_filled diff --git a/airtime_mvc/application/models/airtime/map/CcPlayoutHistoryTableMap.php b/airtime_mvc/application/models/airtime/map/CcPlayoutHistoryTableMap.php index 6c365d7bb..23696f18d 100644 --- a/airtime_mvc/application/models/airtime/map/CcPlayoutHistoryTableMap.php +++ b/airtime_mvc/application/models/airtime/map/CcPlayoutHistoryTableMap.php @@ -41,7 +41,7 @@ class CcPlayoutHistoryTableMap extends TableMap { $this->addPrimaryKey('ID', 'DbId', 'INTEGER', true, null, null); $this->addForeignKey('FILE_ID', 'DbFileId', 'INTEGER', 'cc_files', 'ID', false, null, null); $this->addColumn('STARTS', 'DbStarts', 'TIMESTAMP', true, null, null); - $this->addColumn('ENDS', 'DbEnds', 'TIMESTAMP', true, null, null); + $this->addColumn('ENDS', 'DbEnds', 'TIMESTAMP', false, null, null); $this->addForeignKey('INSTANCE_ID', 'DbInstanceId', 'INTEGER', 'cc_show_instances', 'ID', false, null, null); // validators } // initialize() diff --git a/airtime_mvc/application/services/CalendarService.php b/airtime_mvc/application/services/CalendarService.php index b0e1ad865..a2d690490 100644 --- a/airtime_mvc/application/services/CalendarService.php +++ b/airtime_mvc/application/services/CalendarService.php @@ -79,22 +79,38 @@ class Application_Service_CalendarService "url" => $baseUrl."schedule/show-content-dialog"); } } else { - //Show content can be modified from the calendar if: - // the show has not started, + // Show content can be modified from the calendar if: // the user is admin or hosting the show, // the show is not recorded - if ($now < $start && ($isAdminOrPM || $isHostOfShow) && - !$this->ccShowInstance->isRecorded() ) { + $currentShow = Application_Model_Show::getCurrentShow(); + $currentShowId = count($currentShow) == 1 ? $currentShow[0]["id"] : null; + $showIsLinked = $this->ccShow->isLinked(); + if ($now < $end && ($isAdminOrPM || $isHostOfShow) && !$this->ccShowInstance->isRecorded()) { + //if the show is not linked the user can add/remove content + if (!$showIsLinked) { - $menu["schedule"] = array( + $menu["schedule"] = array( "name"=> _("Add / Remove Content"), "icon" => "add-remove-content", "url" => $baseUrl."showbuilder/builder-dialog/"); + //if the show is linked and it's not currently playing the user can add/remove content + } elseif ($showIsLinked && $currentShowId != $this->ccShow->getDbId()) { - $menu["clear"] = array( - "name"=> _("Remove All Content"), - "icon" => "remove-all-content", - "url" => $baseUrl."schedule/clear-show"); + $menu["schedule"] = array( + "name"=> _("Add / Remove Content"), + "icon" => "add-remove-content", + "url" => $baseUrl."showbuilder/builder-dialog/"); + } + + } + + if ($now < $start && ($isAdminOrPM || $isHostOfShow) && + !$this->ccShowInstance->isRecorded() ) { + + $menu["clear"] = array( + "name"=> _("Remove All Content"), + "icon" => "remove-all-content", + "url" => $baseUrl."schedule/clear-show"); } //"Show Content" should be a menu item at all times except when @@ -121,23 +137,37 @@ class Application_Service_CalendarService } } - $isRepeating = $this->ccShow->getFirstCcShowDay()->isRepeating(); + $excludeIds = $this->ccShow->getEditedRepeatingInstanceIds(); + + $isRepeating = $this->ccShow->isRepeating(); + $populateInstance = false; + if ($isRepeating && in_array($this->ccShowInstance->getDbId(), $excludeIds)) { + $populateInstance = true; + } + if (!$this->ccShowInstance->isRebroadcast() && $isAdminOrPM) { if ($isRepeating) { - $menu["edit"] = array( - "name" => _("Edit"), - "icon" => "edit", - "items" => array()); + if ($populateInstance) { + $menu["edit"] = array( + "name" => _("Edit This Instance"), + "icon" => "edit", + "url" => $baseUrl."Schedule/populate-repeating-show-instance-form"); + } else { + $menu["edit"] = array( + "name" => _("Edit"), + "icon" => "edit", + "items" => array()); - $menu["edit"]["items"]["all"] = array( - "name" => _("Edit Show"), - "icon" => "edit", - "url" => $baseUrl."Schedule/populate-show-form"); + $menu["edit"]["items"]["all"] = array( + "name" => _("Edit Show"), + "icon" => "edit", + "url" => $baseUrl."Schedule/populate-show-form"); - $menu["edit"]["items"]["instance"] = array( - "name" => _("Edit This Instance"), - "icon" => "edit", - "url" => $baseUrl."Schedule/populate-repeating-show-instance-form"); + $menu["edit"]["items"]["instance"] = array( + "name" => _("Edit This Instance"), + "icon" => "edit", + "url" => $baseUrl."Schedule/populate-repeating-show-instance-form"); + } } else { $menu["edit"] = array( "name"=> _("Edit Show"), @@ -166,6 +196,11 @@ class Application_Service_CalendarService "name"=> _("Delete This Instance and All Following"), "icon" => "delete", "url" => $baseUrl."schedule/delete-show"); + } elseif ($populateInstance) { + $menu["del"] = array( + "name"=> _("Delete"), + "icon" => "delete", + "url" => $baseUrl."schedule/delete-show-instance"); } else { $menu["del"] = array( "name"=> _("Delete"), @@ -215,14 +250,14 @@ class Application_Service_CalendarService throw new Exception(_("Permission denied")); } - if ($this->ccShow->getFirstCcShowDay()->isRepeating()) { + if ($this->ccShow->isRepeating()) { throw new Exception(_("Can't drag and drop repeating shows")); } $today_timestamp = time(); - $startsDateTime = new DateTime($this->ccShowInstance->getDbStarts(), new DateTimeZone("UTC")); - $endsDateTime = new DateTime($this->ccShowInstance->getDbEnds(), new DateTimeZone("UTC")); + $startsDateTime = $this->ccShowInstance->getDbStarts(null); + $endsDateTime = $this->ccShowInstance->getDbEnds(null); if ($today_timestamp > $startsDateTime->getTimestamp()) { throw new Exception(_("Can't move a past show")); @@ -231,13 +266,30 @@ class Application_Service_CalendarService //the user is moving the show on the calendar from the perspective of local time. //incase a show is moved across a time change border offsets should be added to the localtime //stamp and then converted back to UTC to avoid show time changes! - $localTimezone = Application_Model_Preference::GetTimezone(); - $startsDateTime->setTimezone(new DateTimeZone($localTimezone)); - $endsDateTime->setTimezone(new DateTimeZone($localTimezone)); + $showTimezone = $this->ccShow->getFirstCcShowDay()->getDbTimezone(); + $startsDateTime->setTimezone(new DateTimeZone($showTimezone)); + $endsDateTime->setTimezone(new DateTimeZone($showTimezone)); + $duration = $startsDateTime->diff($endsDateTime); + $newStartsDateTime = self::addDeltas($startsDateTime, $deltaDay, $deltaMin); - $newEndsDateTime = self::addDeltas($endsDateTime, $deltaDay, $deltaMin); - + /* WARNING: Do not separately add a time delta to the start and end times because + that does not preserve the duration across a DST time change. + For example, 5am - 3 hours = 3am when DST occurs at 2am. + BUT, 6am - 3 hours = 3am also! + So when a DST change occurs, adding the deltas like this + separately does not conserve the duration of a show. + Since that's what we want (otherwise we'll get a zero length show), + we calculate the show duration FIRST, then we just add that on + to the start time to calculate the end time. + This is a safer approach. + The key lesson here is that in general: duration != end - start + ... so be careful! + */ + //$newEndsDateTime = self::addDeltas($endsDateTime, $deltaDay, $deltaMin); <--- Wrong, don't do it. + $newEndsDateTime = clone $newStartsDateTime; + $newEndsDateTime = $newEndsDateTime->add($duration); + //convert our new starts/ends to UTC. $newStartsDateTime->setTimezone(new DateTimeZone("UTC")); $newEndsDateTime->setTimezone(new DateTimeZone("UTC")); @@ -258,7 +310,7 @@ class Application_Service_CalendarService $minRebroadcastStart = self::addDeltas($newEndsDateTime, 0, 60); //check if we are moving a recorded show less than 1 hour before any of its own rebroadcasts. $rebroadcasts = CcShowInstancesQuery::create() - ->filterByDbOriginalShow($this->_instanceId) + ->filterByDbOriginalShow($this->ccShow->getDbId()) ->filterByDbStarts($minRebroadcastStart->format('Y-m-d H:i:s'), Criteria::LESS_THAN) ->find(); @@ -268,8 +320,9 @@ class Application_Service_CalendarService } if ($this->ccShow->isRebroadcast()) { - $recordedShow = CcShowInstancesQuery::create()->findPk( - $this->ccShowInstance->getDbOriginalShow()); + $recordedShow = CcShowInstancesQuery::create() + ->filterByCcShow($this->ccShowInstance->getDbOriginalShow()) + ->findOne(); if (is_null($recordedShow)) { $this->ccShowInstance->delete(); throw new Exception(_("Show was deleted because recorded show does not exist!")); @@ -291,26 +344,32 @@ class Application_Service_CalendarService $con = Propel::getConnection(); $con->beginTransaction(); + //new starts,ends are in UTC list($newStartsDateTime, $newEndsDateTime) = $this->validateShowMove( $deltaDay, $deltaMin); + + $oldStartDateTime = $this->ccShowInstance->getDbStarts(null); $this->ccShowInstance ->setDbStarts($newStartsDateTime) ->setDbEnds($newEndsDateTime) - ->save(); + ->save($con); if (!$this->ccShowInstance->getCcShow()->isRebroadcast()) { //we can get the first show day because we know the show is //not repeating, and therefore will only have one show day entry $ccShowDay = $this->ccShow->getFirstCcShowDay(); + $showTimezone = new DateTimeZone($ccShowDay->getDbTimezone()); $ccShowDay - ->setDbFirstShow($newStartsDateTime) - ->setDbLastShow($newEndsDateTime) - ->save(); + ->setDbFirstShow($newStartsDateTime->setTimezone($showTimezone)->format("Y-m-d")) + ->setDbStartTime($newStartsDateTime->format("H:i")) + ->save($con); } - + + $diff = $newStartsDateTime->getTimestamp() - $oldStartDateTime->getTimestamp(); + Application_Service_SchedulerService::updateScheduleStartTime( - array($this->ccShowInstance->getDbId()), null, $newStartsDateTime); + array($this->ccShowInstance->getDbId()), $diff); $con->commit(); Application_Model_RabbitMq::PushSchedule(); @@ -320,6 +379,7 @@ class Application_Service_CalendarService } } + //TODO move the method resizeShow from Application_Model_Show here. public function resizeShow($deltaDay, $deltaMin) { try { diff --git a/airtime_mvc/application/services/HistoryService.php b/airtime_mvc/application/services/HistoryService.php index 10e559c3b..c8ac571bc 100644 --- a/airtime_mvc/application/services/HistoryService.php +++ b/airtime_mvc/application/services/HistoryService.php @@ -290,9 +290,12 @@ class Application_Service_HistoryService $dateTime->setTimezone($timezoneLocal); $result["starts"] = $dateTime->format("Y-m-d H:i:s"); - $dateTime = new DateTime($result["ends"], $timezoneUTC); - $dateTime->setTimezone($timezoneLocal); - $result["ends"] = $dateTime->format("Y-m-d H:i:s"); + //if ends is null we don't want it to default to "now" + if (isset($result["ends"])) { + $dateTime = new DateTime($result["ends"], $timezoneUTC); + $dateTime->setTimezone($timezoneLocal); + $result["ends"] = $dateTime->format("Y-m-d H:i:s"); + } if (isset($result[MDATA_KEY_DURATION])) { $formatter = new LengthFormatter($result[MDATA_KEY_DURATION]); @@ -427,8 +430,10 @@ class Application_Service_HistoryService //----------------------------------------------------------------- //processing the results foreach ($rows as &$row) { - $formatter = new LengthFormatter($row['length']); - $row['length'] = $formatter->format(); + if (isset($row[MDATA_KEY_DURATION])) { + $formatter = new LengthFormatter($row[MDATA_KEY_DURATION]); + $row[MDATA_KEY_DURATION] = $formatter->format(); + } } return array( @@ -505,6 +510,48 @@ class Application_Service_HistoryService return $filteredShows; } + + public function insertWebstreamMetadata($schedId, $startDT, $data) { + + $this->con->beginTransaction(); + + try { + + $item = CcScheduleQuery::create()->findPK($schedId, $this->con); + + //TODO figure out how to combine these all into 1 query. + $showInstance = $item->getCcShowInstances($this->con); + $show = $showInstance->getCcShow($this->con); + + $webstream = $item->getCcWebstream($this->con); + + $metadata = array(); + $metadata["showname"] = $show->getDbName(); + $metadata[MDATA_KEY_TITLE] = $data->title; + $metadata[MDATA_KEY_CREATOR] = $webstream->getDbName(); + + $history = new CcPlayoutHistory(); + $history->setDbStarts($startDT); + $history->setDbEnds(null); + $history->setDbInstanceId($item->getDbInstanceId()); + + foreach ($metadata as $key => $val) { + $meta = new CcPlayoutHistoryMetaData(); + $meta->setDbKey($key); + $meta->setDbValue($val); + + $history->addCcPlayoutHistoryMetaData($meta); + } + + $history->save($this->con); + + $this->con->commit(); + } + catch (Exception $e) { + $this->con->rollback(); + throw $e; + } + } public function insertPlayedItem($schedId) { @@ -528,26 +575,38 @@ class Application_Service_HistoryService $instanceEnd = $showInstance->getDbEnds(null); $itemEnd = $item->getDbEnds(null); + $recordStart = $item->getDbStarts(null); $recordEnd = ($instanceEnd < $itemEnd) ? $instanceEnd : $itemEnd; - - $history = new CcPlayoutHistory(); - $history->setDbFileId($fileId); - $history->setDbStarts($item->getDbStarts(null)); - $history->setDbEnds($recordEnd); - $history->setDbInstanceId($item->getDbInstanceId()); - - foreach ($metadata as $key => $val) { - $meta = new CcPlayoutHistoryMetaData(); - $meta->setDbKey($key); - $meta->setDbValue($val); - - $history->addCcPlayoutHistoryMetaData($meta); - } - - $history->save($this->con); - } - - $this->con->commit(); + + //first check if this is a duplicate + // (caused by restarting liquidsoap) + + $prevRecord = CcPlayoutHistoryQuery::create() + ->filterByDbStarts($recordStart) + ->filterByDbEnds($recordEnd) + ->filterByDbFileId($fileId) + ->findOne($this->con); + + if (empty($prevRecord)) { + + $history = new CcPlayoutHistory(); + $history->setDbFileId($fileId); + $history->setDbStarts($recordStart); + $history->setDbEnds($recordEnd); + $history->setDbInstanceId($item->getDbInstanceId()); + + foreach ($metadata as $key => $val) { + $meta = new CcPlayoutHistoryMetaData(); + $meta->setDbKey($key); + $meta->setDbValue($val); + + $history->addCcPlayoutHistoryMetaData($meta); + } + + $history->save($this->con); + $this->con->commit(); + } + } } catch (Exception $e) { $this->con->rollback(); @@ -611,7 +670,7 @@ class Application_Service_HistoryService } //need to convert to the station's local time first. - if ($field["type"] == TEMPLATE_DATETIME) { + if ($field["type"] == TEMPLATE_DATETIME && !is_null($value)) { $timezoneUTC = new DateTimeZone("UTC"); $timezoneLocal = new DateTimeZone($this->timezone); @@ -1017,7 +1076,7 @@ class Application_Service_HistoryService TEMPLATE_TIME => "strval", TEMPLATE_DATETIME => "strval", TEMPLATE_STRING => "strval", - TEMPLATE_BOOLEAN => "intval", //boolval only exists in php 5.5+ wtf? + TEMPLATE_BOOLEAN => "intval", //boolval only exists in php 5.5+ TEMPLATE_INT => "intval", TEMPLATE_FLOAT => "floatval", ); diff --git a/airtime_mvc/application/services/SchedulerService.php b/airtime_mvc/application/services/SchedulerService.php index c1dd6fc1f..60206c112 100644 --- a/airtime_mvc/application/services/SchedulerService.php +++ b/airtime_mvc/application/services/SchedulerService.php @@ -44,47 +44,31 @@ class Application_Service_SchedulerService * Applies the show start difference to any scheduled items * * @param $instanceIds - * @param $diff - * @param $newStart + * @param $diff (integer, difference between unix epoch in seconds) */ - public static function updateScheduleStartTime($instanceIds, $diff=null, $newStart=null) + public static function updateScheduleStartTime($instanceIds, $diff) { $con = Propel::getConnection(); if (count($instanceIds) > 0) { $showIdList = implode(",", $instanceIds); - if (is_null($diff)) { - $ccSchedule = CcScheduleQuery::create() - ->filterByDbInstanceId($instanceIds, Criteria::IN) - ->orderByDbStarts() - ->limit(1) - ->findOne(); - - if (!is_null($ccSchedule)) { - $scheduleStartsEpoch = strtotime($ccSchedule->getDbStarts()); - $showStartsEpoch = strtotime($newStart->format("Y-m-d H:i:s")); - - $diff = $showStartsEpoch - $scheduleStartsEpoch; - } - } - $ccSchedules = CcScheduleQuery::create() ->filterByDbInstanceId($instanceIds, Criteria::IN) - ->find(); + ->find($con); $interval = new DateInterval("PT".abs($diff)."S"); if ($diff < 0) { $interval->invert = 1; } foreach ($ccSchedules as $ccSchedule) { - $start = new DateTime($ccSchedule->getDbStarts()); + $start = $ccSchedule->getDbStarts(null); $newStart = $start->add($interval); - $end = new DateTime($ccSchedule->getDbEnds()); + $end = $ccSchedule->getDbEnds(null); $newEnd = $end->add($interval); $ccSchedule - ->setDbStarts($newStart->format("Y-m-d H:i:s")) - ->setDbEnds($newEnd->format("Y-m-d H:i:s")) - ->save(); + ->setDbStarts($newStart) + ->setDbEnds($newEnd) + ->save($con); } } } @@ -173,6 +157,10 @@ class Application_Service_SchedulerService * any other instances with content */ $instanceIds = $ccShow->getInstanceIds(); + if (count($instanceIds) == 0) { + return; + } + $schedule_sql = "SELECT * FROM cc_schedule ". "WHERE instance_id IN (".implode($instanceIds, ",").")"; $ccSchedules = Application_Common_Database::prepareAndExecute( @@ -407,4 +395,24 @@ class Application_Service_SchedulerService return false; } } -} \ No newline at end of file + + /* + * TODO in the future this should probably support webstreams. + */ + public function updateFutureIsScheduled($scheduleId, $status) + { + $sched = CcScheduleQuery::create()->findPk($scheduleId); + $redraw = false; + + if (isset($sched)) { + + $fileId = $sched->getDbFileId(); + + if (isset($fileId)) { + $redraw = Application_Model_StoredFile::setIsScheduled($fileId, $status); + } + } + + return $redraw; + } +} diff --git a/airtime_mvc/application/services/ShowFormService.php b/airtime_mvc/application/services/ShowFormService.php index c651c8ebd..0563c65f8 100644 --- a/airtime_mvc/application/services/ShowFormService.php +++ b/airtime_mvc/application/services/ShowFormService.php @@ -81,9 +81,20 @@ class Application_Service_ShowFormService $this->populateFormLive($forms["live"]); $this->populateFormStyle($forms["style"]); - //no need to populate these forms since the user won't - //be able to see them + /* Only the field on the 'when' form will get updated so we should + * make all other forms disabled or readonly + * + * 'what' needs to be readonly because zendform will not validate + * if they are disabled + * + * All other forms can be disabled because we do not update those values + * when the user edits a repeating instance + */ + $forms["what"]->makeReadonly(); $forms["repeats"]->disable(); + $forms["who"]->disable(); + $forms["style"]->disable(); + $forms["live"]->disable(); $forms["record"]->disable(); $forms["rebroadcast"]->disable(); $forms["abs_rebroadcast"]->disable(); @@ -124,10 +135,14 @@ class Application_Service_ShowFormService private function populateFormWhen($form) { - $ccShowDay = $this->ccShow->getFirstCcShowDay(); + if ($this->ccShow->isRepeating()) { + $ccShowDay = $this->ccShow->getFirstRepeatingCcShowDay(); + } else { + $ccShowDay = $this->ccShow->getFirstCcShowDay(); + } $showStart = $ccShowDay->getLocalStartDateAndTime(); - $showEnd = $ccShowDay->getLocalEndDateAndTime($showStart); + $showEnd = $ccShowDay->getLocalEndDateAndTime(); //check if the first show is in the past if ($ccShowDay->isShowStartInPast()) { @@ -150,6 +165,7 @@ class Application_Service_ShowFormService 'add_show_end_date_no_repeat' => $showEnd->format("Y-m-d"), 'add_show_end_time' => $showEnd->format("H:i"), 'add_show_duration' => $ccShowDay->formatDuration(true), + 'add_show_timezone' => $ccShowDay->getDbTimezone(), 'add_show_repeats' => $ccShowDay->isRepeating() ? 1 : 0)); return $showStart; @@ -159,13 +175,16 @@ class Application_Service_ShowFormService { $ccShowInstance = CcShowInstancesQuery::create()->findPk($this->instanceId); - $timezone = new DateTimeZone(Application_Model_Preference::GetTimezone()); + //get timezone the show is created in + $timezone = $ccShowInstance->getCcShow()->getFirstCcShowDay()->getDbTimezone(); + //$timezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); + //DateTime object in UTC $showStart = $ccShowInstance->getDbStarts(null); - $showStart->setTimezone($timezone); + $showStart->setTimezone(new DateTimeZone($timezone)); $showEnd = $ccShowInstance->getDbEnds(null); - $showEnd->setTimezone($timezone); + $showEnd->setTimezone(new DateTimeZone($timezone)); //if the show has started, do not allow editing on the start time if ($showStart->getTimestamp() <= time()) { @@ -179,7 +198,8 @@ class Application_Service_ShowFormService 'add_show_end_date_no_repeat' => $showEnd->format("Y-m-d"), 'add_show_end_time' => $showEnd->format("H:i"), 'add_show_duration' => $this->calculateDuration( - $showStart->format("Y-m-d H:i:s"), $showEnd->format("Y-m-d H:i:s")), + $showStart->format("Y-m-d H:i:s"), $showEnd->format("Y-m-d H:i:s"), $timezone), + 'add_show_timezone' => $timezone, 'add_show_repeats' => 0)); $form->getElement('add_show_repeats')->setOptions(array("disabled" => true)); @@ -193,7 +213,11 @@ class Application_Service_ShowFormService */ private function populateFormRepeats($form, $nextFutureShowStart) { - $ccShowDays = $this->ccShow->getCcShowDays(); + if ($this->ccShow->isRepeating()) { + $ccShowDays = $this->ccShow->getRepeatingCcShowDays(); + } else { + $ccShowDays = $this->ccShow->getCcShowDayss(); + } $days = array(); foreach ($ccShowDays as $ccShowDay) { @@ -202,12 +226,13 @@ class Application_Service_ShowFormService } $service_show = new Application_Service_ShowService($this->ccShow->getDbId()); - $repeatEndDate = new DateTime($service_show->getRepeatingEndDate(), new DateTimeZone( - $ccShowDays[0]->getDbTimezone())); + $repeatEndDate = $service_show->getRepeatingEndDate(); //end dates are stored non-inclusively so we need to //subtract one day - $repeatEndDate->sub(new DateInterval("P1D")); - + if (!is_null($repeatEndDate)) { + $repeatEndDate->sub(new DateInterval("P1D")); + } + //default monthly repeat type $monthlyRepeatType = 2; $repeatType = $ccShowDays[0]->getDbRepeatType(); @@ -224,20 +249,24 @@ class Application_Service_ShowFormService 'add_show_linked' => $this->ccShow->getDbLinked(), 'add_show_repeat_type' => $repeatType, 'add_show_day_check' => $days, - 'add_show_end_date' => $repeatEndDate->format("Y-m-d"), - 'add_show_no_end' => (!$service_show->getRepeatingEndDate()), + 'add_show_end_date' => (!is_null($repeatEndDate)) ? $repeatEndDate->format("Y-m-d"):null, + 'add_show_no_end' => (is_null($repeatEndDate)), 'add_show_monthly_repeat_type' => $monthlyRepeatType)); if (!$this->ccShow->isLinkable() || $this->ccShow->isRecorded()) { $form->getElement('add_show_linked')->setOptions(array('disabled' => true)); } - /* Because live editing of a linked show is disabled, we will disable - * the linking option if the current show is being edited. We don't - * want the user to suddenly not be able to edit the current show + /* Because live editing of a linked show is disabled, we will make + * the linking option readonly if the current show is being edited. We + * dont' want the user to suddenly not be able to edit the current show + * + * Readonly does not work with checkboxes but we can't disable it + * because the value won't get posted. In add-show.js we stop the + * onclick event from firing by returning false */ if ($this->hasShowStarted($nextFutureShowStart)) { - $form->getElement('add_show_linked')->setOptions(array('disabled' => true)); + $form->getElement('add_show_linked')->setAttrib('readonly', 'readonly'); } } @@ -300,13 +329,14 @@ class Application_Service_ShowFormService private function populateFormRebroadcastAbsolute($form) { $absolutRebroadcasts = $this->ccShow->getRebroadcastsAbsolute(); + $timezone = $this->ccShow->getFirstCcShowDay()->getDbTimezone(); $formValues = array(); $i = 1; foreach ($absolutRebroadcasts as $ar) { //convert dates to user's local time $start = new DateTime($ar->getDbStarts(), new DateTimeZone("UTC")); - $start->setTimezone(new DateTimeZone(Application_Model_Preference::GetTimezone())); + $start->setTimezone(new DateTimeZone($timezone)); $formValues["add_show_rebroadcast_date_absolute_$i"] = $start->format("Y-m-d"); $formValues["add_show_rebroadcast_time_absolute_$i"] = $start->format("H:i"); $i++; @@ -389,10 +419,10 @@ class Application_Service_ShowFormService $starts = new DateTime($ccShowInstance->getDbStarts(), new DateTimeZone("UTC")); $ends = new DateTime($ccShowInstance->getDbEnds(), new DateTimeZone("UTC")); - $userTimezone = Application_Model_Preference::GetTimezone(); + $showTimezone = $this->ccShow->getFirstCcShowDay()->getDbTimezone(); - $starts->setTimezone(new DateTimeZone($userTimezone)); - $ends->setTimezone(new DateTimeZone($userTimezone)); + $starts->setTimezone(new DateTimeZone($showTimezone)); + $ends->setTimezone(new DateTimeZone($showTimezone)); return array($starts, $ends); } @@ -456,16 +486,15 @@ class Application_Service_ShowFormService } } - public function calculateDuration($start, $end) + public function calculateDuration($start, $end, $timezone) { try { - $startDateTime = new DateTime($start); - $endDateTime = new DateTime($end); + + $tz = new DateTimeZone($timezone); + $startDateTime = new DateTime($start, $tz); + $endDateTime = new DateTime($end, $tz); - $UTCStartDateTime = $startDateTime->setTimezone(new DateTimeZone('UTC')); - $UTCEndDateTime = $endDateTime->setTimezone(new DateTimeZone('UTC')); - - $duration = $UTCEndDateTime->diff($UTCStartDateTime); + $duration = $startDateTime->diff($endDateTime); $day = intval($duration->format('%d')); if ($day > 0) { @@ -476,10 +505,31 @@ class Application_Service_ShowFormService $sign = $duration->format('%r'); return sprintf('%s%02dh %02dm', $sign, $hour, $min); } else { - return $duration->format('%Hh %Im'); + return $duration->format('%r%Hh %Im'); } } catch (Exception $e) { + Logging::info($e->getMessage()); return "Invalid Date"; } } + + /** + * When the timezone is changed in add-show form this function + * applies the new timezone to the start and end time + * + * @param $date String + * @param $time String + * @param $timezone String + */ + public static function localizeDateTime($date, $time, $newTimezone, $oldTimezone) + { + $dt = new DateTime($date." ".$time, new DateTimeZone($oldTimezone)); + + $dt->setTimeZone(new DateTimeZone($newTimezone)); + + return array( + "date" => $dt->format("Y-m-d"), + "time" => $dt->format("H:i") + ); + } } \ No newline at end of file diff --git a/airtime_mvc/application/services/ShowService.php b/airtime_mvc/application/services/ShowService.php index f5bed073e..183af4723 100644 --- a/airtime_mvc/application/services/ShowService.php +++ b/airtime_mvc/application/services/ShowService.php @@ -5,6 +5,8 @@ define("REPEAT_WEEKLY", 0); define("REPEAT_BI_WEEKLY", 1); define("REPEAT_MONTHLY_MONTHLY", 2); define("REPEAT_MONTHLY_WEEKLY", 3); +define("REPEAT_TRI_WEEKLY", 4); +define("REPEAT_QUAD_WEEKLY", 5); class Application_Service_ShowService { @@ -14,6 +16,10 @@ class Application_Service_ShowService private $repeatType; private $isUpdate; private $linkedShowContent; + private $oldShowTimezone; + private $localShowStartHour; + private $localShowStartMin; + private $origCcShowDay; public function __construct($showId=null, $showData=null, $isUpdate=false) { @@ -29,12 +35,13 @@ class Application_Service_ShowService } else { $this->repeatType = -1; } + $this->isRecorded = (isset($showData['add_show_record']) && $showData['add_show_record']) ? 1 : 0; $this->isRebroadcast = (isset($showData['add_show_rebroadcast']) && $showData['add_show_rebroadcast']) ? 1 : 0; $this->isUpdate = $isUpdate; } - public function createShowFromRepeatingInstance($showData) { + public function editRepeatingShowInstance($showData) { $service_user = new Application_Service_UserService(); $currentUser = $service_user->getCurrentUser(); @@ -48,63 +55,88 @@ class Application_Service_ShowService throw new Exception("Permission denied"); } + $showId = $showData["add_show_id"]; + /****** UPDATE SCHEDULE START TIME ******/ //get the ccShow object to which this instance belongs //so we can get the original start date and time - $oldCcShow = CcShowQuery::create() - ->findPk($showData["add_show_id"]); + $this->ccShow = CcShowQuery::create() + ->findPk($showId); - //DateTime in user's local time + //DateTime in shows's local time $newStartDateTime = new DateTime($showData["add_show_start_date"]." ". $showData["add_show_start_time"], - new DateTimeZone(Application_Model_Preference::GetTimezone())); + new DateTimeZone($showData["add_show_timezone"])); $ccShowInstanceOrig = CcShowInstancesQuery::create() ->findPk($showData["add_show_instance_id"]); - $diff = $this->calculateShowStartDiff($newStartDateTime, - $ccShowInstanceOrig->getLocalStartDateTime()); - if ($diff > 0) { + //convert original start time into the show's local timezone + $origLocalStartDateTime = $ccShowInstanceOrig->getLocalStartDateTime(); + + $diff = $this->calculateShowStartDiff($newStartDateTime, + $origLocalStartDateTime); + + if ($diff != 0) { Application_Service_SchedulerService::updateScheduleStartTime( array($showData["add_show_instance_id"]), $diff); } /****** UPDATE SCHEDULE START TIME ENDS******/ - $this->setCcShow($showData); - $this->setCcShowDays($showData); - $this->setCcShowHosts($showData); - $this->delegateInstanceCreation(); + /* + * In the case where an instance is being edited for a second + * (or third, fourth, etc.) time we need to delete the old + * cc_show_day record + * + * Since we don't store the cc_show_day ids we need to use the + * original start time from cc_show_instances, convert it to the show's + * local timezone, and find the record in cc_show_days + * + * *** There is a flaw here: We have to assume the show timezone has + * *** not changed (make timezone readonly??) + */ + $origCcShowDay = CcShowDaysQuery::create() + ->filterByDbShowId($showId) + ->filterByDbRepeatType(-1) + ->filterByDbFirstShow($origLocalStartDateTime->format("Y-m-d")) + ->filterByDbStartTime($origLocalStartDateTime->format("H:i:s")) + ->delete(); - //get the new instance id - $ccShowInstance = CcShowInstancesQuery::create() - ->filterByDbShowId($this->ccShow->getDbId()) + /* + * Set the new cc_show_day record + * Associates it with the current show_id and sets it to non-repeating + */ + $this->setCcShowDays($showData); + + /* + * We need to find the new show day rule we just created by passing + * in the first show and start time in case multiple single + * instances have been edited out of the repeating sequence. + */ + $showDay = CcShowDaysQuery::create() + ->filterByDbShowId($showId) + ->filterByDbRepeatType(-1) + ->filterByDbFirstShow($showData["add_show_start_date"]) + ->filterByDbStartTime($showData["add_show_start_time"].":00") ->findOne(); - $newInstanceId = $ccShowInstance->getDbId(); + $ccShowInstance = $this->createNonRepeatingInstance($showDay, + $this->getPopulateShowUntilDateTIme()); //update cc_schedule with the new instance id - $ccSchedules = CcScheduleQuery::create() - ->filterByDbInstanceId($showData["add_show_instance_id"]) - ->find(); - - foreach ($ccSchedules as $ccSchedule) { - $ccSchedule->setDbInstanceId($newInstanceId); - $ccSchedule->save(); - } - $con = Propel::getConnection(CcShowInstancesPeer::DATABASE_NAME); + $selectCriteria = new Criteria(); + $selectCriteria->add(CcSchedulePeer::INSTANCE_ID, $showData["add_show_instance_id"]); + $updateCriteria = new Criteria(); + $updateCriteria->add(CcSchedulePeer::INSTANCE_ID, $ccShowInstance->getDbId()); + BasePeer::doUpdate($selectCriteria, $updateCriteria, $con); + $ccShowInstance->updateDbTimeFilled($con); + $ccShowInstance->updateScheduleStatus($con); //delete the edited instance from the repeating sequence $ccShowInstanceOrig->setDbModifiedInstance(true)->save(); - $service_showForm = new Application_Service_ShowFormService($showData["add_show_id"]); - list($start, $end) = $service_showForm->getNextFutureRepeatShowTime(); - $oldCcShowDay = $oldCcShow->getFirstCcShowDay(); - $oldCcShowDay - ->setDbFirstShow($start->setTimezone(new DateTimeZone("UTC"))->format("Y-m-d")) - ->save(); - $con->commit(); Application_Model_RabbitMq::PushSchedule(); } catch (Exception $e) { @@ -114,6 +146,27 @@ class Application_Service_ShowService } } + /** + * + * If a user is editing a show we need to store the original timezone and + * start time in case the show's timezone is changed and we are crossing + * over DST + */ + private function storeOrigLocalShowInfo() + { + if ($this->ccShow->isRepeating()) { + $this->origCcShowDay = $this->ccShow->getFirstRepeatingCcShowDay(); + } else { + $this->origCcShowDay = $this->ccShow->getFirstCcShowDay(); + } + + $this->oldShowTimezone = $this->origCcShowDay->getDbTimezone(); + + $origStartTime = explode(":", $this->origCcShowDay->getDbStartTime()); + $this->localShowStartHour = $origStartTime[0]; + $this->localShowStartMin = $origStartTime[1]; + } + public function addUpdateShow($showData) { $service_user = new Application_Service_UserService(); @@ -132,14 +185,16 @@ class Application_Service_ShowService $this->setCcShow($showData); $daysAdded = array(); + if ($this->isUpdate) { $daysAdded = $this->delegateInstanceCleanup($showData); - // updates cc_show_instances start/end times, and updates - // schedule start/end times - $this->applyShowStartEndDifference($showData); + + $this->storeOrigLocalShowInfo(); + $this->deleteRebroadcastInstances(); - $this->deleteCcShowDays(); + $this->deleteCcShowHosts(); + if ($this->isRebroadcast) { //delete entry in cc_show_rebroadcast $this->deleteCcShowRebroadcasts(); @@ -158,6 +213,10 @@ class Application_Service_ShowService //create new ccShowInstances $this->delegateInstanceCreation($daysAdded); + if ($this->isUpdate) { + $this->adjustSchedule($showData); + } + $con->commit(); Application_Model_RabbitMq::PushSchedule(); } catch (Exception $e) { @@ -168,9 +227,23 @@ class Application_Service_ShowService } } + private function adjustSchedule($showData) + { + $con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME); + $ccShowInstances = CcShowInstancesQuery::create() + ->filterByDbShowId($this->ccShow->getDbId()) + ->find(); + + $this->updateScheduleStartEndTimes($showData); + + foreach ($ccShowInstances as $instance) { + $instance->updateScheduleStatus($con); + } + } + /** - * - * Receives a cc_show id and determines whether to create a + * + * Receives a cc_show id and determines whether to create a * single show instance or repeating show instances */ public function delegateInstanceCreation($daysAdded=null, $end=null, $fillInstances=false) @@ -180,7 +253,11 @@ class Application_Service_ShowService if (is_null($this->ccShow)) { $ccShowDays = $this->getShowDaysInRange($populateUntil, $end); } else { - $ccShowDays = $this->ccShow->getCcShowDays(); + if ($this->ccShow->isRepeating()) { + $ccShowDays = $this->ccShow->getRepeatingCcShowDays(); + } else { + $ccShowDays = $this->ccShow->getCcShowDayss(); + } } if (!is_null($end)) { @@ -190,11 +267,15 @@ class Application_Service_ShowService /* In case the user is moving forward in the calendar and there are * linked shows in the schedule we need to keep track of each cc_show * so we know which shows need to be filled with content - */ + */ $ccShows = array(); foreach ($ccShowDays as $day) { + $this->ccShow = $day->getCcShow(); + $this->isRecorded = $this->ccShow->isRecorded(); + $this->isRebroadcast = $this->ccShow->isRebroadcast(); + if (!isset($ccShows[$day->getDbShowId()])) { $ccShows[$day->getDbShowId()] = $day->getccShow(); } @@ -211,6 +292,14 @@ class Application_Service_ShowService $this->createWeeklyRepeatInstances($day, $populateUntil, REPEAT_BI_WEEKLY, new DateInterval("P14D"), $daysAdded); break; + case REPEAT_TRI_WEEKLY: + $this->createWeeklyRepeatInstances($day, $populateUntil, REPEAT_TRI_WEEKLY, + new DateInterval("P21D"), $daysAdded); + break; + case REPEAT_QUAD_WEEKLY: + $this->createWeeklyRepeatInstances($day, $populateUntil, REPEAT_QUAD_WEEKLY, + new DateInterval("P28D"), $daysAdded); + break; case REPEAT_MONTHLY_MONTHLY: $this->createMonthlyRepeatInstances($day, $populateUntil); break; @@ -273,7 +362,7 @@ class Application_Service_ShowService } /** - * + * * Deletes all the cc_show_days entries for a specific show * that is currently being edited. They will get recreated with * the new show day specs @@ -298,12 +387,12 @@ SQL; /** * TODO: This function is messy. Needs refactoring - * + * * When editing a show we may need to perform some actions to reflect the new specs: * - Delete some show instances * - Update duration * - Update start and end time - * + * * @param $showData edit show form values in raw form * @param $isRecorded value computed from the edit show form * @param $repeatType value computed from the edit show form @@ -315,16 +404,15 @@ SQL; $daysAdded = array(); //CcShowDay object - $currentShowDay = $this->ccShow->getFirstCcShowDay(); - - //new end date in users' local time - $endDateTime = $this->calculateEndDate($showData); - if (!is_null($endDateTime)) { - $endDate = $endDateTime->format("Y-m-d"); + if ($this->ccShow->isRepeating()) { + $currentShowDay = $this->ccShow->getFirstRepeatingCcShowDay(); } else { - $endDate = $endDateTime; + $currentShowDay = $this->ccShow->getFirstCcShowDay(); } + //new end date in the show's timezone (from the select box) + $endDateTime = $this->calculateEndDate($showData); + //repeat option was toggled if ($showData['add_show_repeats'] != $currentShowDay->isRepeating()) { $this->deleteAllRepeatInstances($currentShowDay, $showId); @@ -342,7 +430,7 @@ SQL; //if the start date changes, these are the repeat types //that require show instance deletion - $deleteRepeatTypes = array(REPEAT_BI_WEEKLY, REPEAT_MONTHLY_MONTHLY, + $deleteRepeatTypes = array(REPEAT_BI_WEEKLY, REPEAT_TRI_WEEKLY, REPEAT_QUAD_WEEKLY, REPEAT_MONTHLY_MONTHLY, REPEAT_MONTHLY_WEEKLY); if (in_array($this->repeatType, $deleteRepeatTypes) && @@ -359,11 +447,17 @@ SQL; //and the repeat type changed if ($currentRepeatType != -1 && $this->repeatType != $currentRepeatType) { $this->deleteAllInstances($showId); - } else { + // when repeating by day of the month (1st, 2nd, etc.) we do not store the repeat week days + } elseif ($currentRepeatType != 2) { //repeat type is the same, check if the days of the week are the same $repeatingDaysChanged = false; - $ccShowDays = $this->ccShow->getCcShowDays(); + if ($this->ccShow->isRepeating()) { + $ccShowDays = $this->ccShow->getRepeatingCcShowDays(); + } else { + $ccShowDays = $this->ccShow->getCcShowDayss(); + } + $showDays = array(); foreach ($ccShowDays as $day) { $showDays[] = $day->getDbDay(); @@ -406,32 +500,31 @@ SQL; $this->deleteInstancesBeforeDate($showData['add_show_start_date'], $showId); } - + } } - $currentShowEndDate = $this->getRepeatingEndDate(); - //check if "no end" option has changed - if ($currentShowEndDate != $showData['add_show_no_end']) { + //get the endate from the past for this show. + //check if this is null if "no end" + $currentShowEndDateTime = $this->getRepeatingEndDate(); + + if ($currentShowEndDateTime != $endDateTime) { + + $endDate = clone $endDateTime; + $endDate->setTimezone(new DateTimeZone("UTC")); + //show "No End" option was toggled - if (!$showData['add_show_no_end']) { + //or the end date comes earlier + if (is_null($currentShowEndDateTime) || ($endDateTime < $currentShowEndDateTime)) { //"No End" option was unchecked so we need to delete the //repeat instances that are scheduled after the new end date - $this->deleteInstancesFromDate($endDate, $showId); + //OR + //end date was pushed back so we have to delete any + //instances of this show scheduled after the new end date + $this->deleteInstancesFromDate($endDate->format("Y-m-d"), $showId); } } - - if ($currentShowEndDate != $showData['add_show_end_date']) { - //end date was changed - $newEndDate = strtotime($showData['add_show_end_date']); - $oldEndDate = strtotime($currentShowEndDate); - if ($newEndDate < $oldEndDate) { - //end date was pushed back so we have to delete any - //instances of this show scheduled after the new end date - $this->deleteInstancesFromDate($endDate, $showId); - } - } - }//if repeats + } return $daysAdded; } @@ -452,19 +545,32 @@ SQL; } } + /* + * returns a DateTime of the current show end date set to the timezone of the show. + */ public function getRepeatingEndDate() { $sql = << $this->ccShow->getDbId() ), 'column' ); - - return ($query !== false) ? $query : false; + array( 'showId' => $this->ccShow->getDbId() ), 'single'); + + $date = null; + + if ($query !== false && isset($query["last_show"])) { + $date = new DateTime( + $query["last_show"], + new DateTimeZone($query["timezone"]) + ); + } + + return $date; } private function deleteInstancesFromDate($endDate, $showId) @@ -496,7 +602,7 @@ SQL; } /** - * + * * Enter description here ... * @param $daysRemoved array of days (days of the week) removed * (days of the week are represented numerically @@ -638,14 +744,23 @@ SQL; */ else if (count($ccShowInstances) >= 1) { $lastShowDays = array(); + + //get the show's timezone + $ccShow = CcShowQuery::create()->findPk($showId); + if ($ccShow->isRepeating()) { + $showTimezone = $ccShow->getFirstRepeatingCcShowDay()->getDbTimezone(); + } else { + $showTimezone = $ccShow->getFirstCcShowDay()->getDbTimezone(); + } + /* Creates an array where the key is the day of the week (monday, * tuesday, etc.) and the value is the last show date for each * day of the week. We will use this array to update the last_show * for each cc_show_days entry of a cc_show */ foreach ($ccShowInstances as $instance) { - $instanceStartDT = new DateTime($instance->getDbStarts(), - new DateTimeZone("UTC")); + $instanceStartDT = $instance->getDbStarts(null); + $instanceStartDT->setTimezone(new DateTimeZone($showTimezone)); $lastShowDays[$instanceStartDT->format("w")] = $instanceStartDT; } @@ -653,6 +768,7 @@ SQL; $ccShowDay = CcShowDaysQuery::create() ->filterByDbShowId($showId) ->filterByDbDay($dayOfWeek) + ->filterByDbRepeatType(-1, Criteria::NOT_EQUAL) ->findOne(); if (isset($ccShowDay)) { @@ -666,12 +782,6 @@ SQL; ->save(); } } - - //remove the old repeating deleted instances. - CcShowInstancesQuery::create() - ->filterByDbShowId($showId) - ->filterByDbModifiedInstance(true) - ->delete(); } return false; @@ -708,43 +818,51 @@ SQL; } /** - * + * * Determines what the show end date should be based on * the form data - * + * * @param $showData add/edit show form data * @return DateTime object in user's local timezone */ private function calculateEndDate($showData) { + //if no end return null if ($showData['add_show_no_end']) { - $endDate = NULL; - } elseif ($showData['add_show_repeats']) { - $endDate = new DateTime($showData['add_show_end_date']); + $endDate = null; + } + //if the show is repeating & ends, then return the end date + elseif ($showData['add_show_repeats']) { + $endDate = new DateTime( + $showData['add_show_end_date'], + new DateTimeZone($showData["add_show_timezone"]) + ); $endDate->add(new DateInterval("P1D")); - } else { - $endDate = new DateTime($showData['add_show_start_date']); + } + //the show doesn't repeat, so add one day to the start date. + else { + $endDate = new DateTime( + $showData['add_show_start_date'], + new DateTimeZone($showData["add_show_timezone"]) + ); $endDate->add(new DateInterval("P1D")); } return $endDate; } - private function applyShowStartEndDifference($showData) + private function updateScheduleStartEndTimes($showData) { $showId = $this->ccShow->getDbId(); - //CcShowDay object - $currentShowDay = $this->ccShow->getFirstCcShowDay(); - //DateTime in user's local time + //DateTime in show's local time $newStartDateTime = new DateTime($showData["add_show_start_date"]." ". $showData["add_show_start_time"], - new DateTimeZone(Application_Model_Preference::GetTimezone())); + new DateTimeZone($showData["add_show_timezone"])); $diff = $this->calculateShowStartDiff($newStartDateTime, - $currentShowDay->getLocalStartDateAndTime()); + $this->origCcShowDay->getLocalStartDateAndTime()); - $this->updateInstanceStartEndTime($diff); $ccShowInstances = $this->ccShow->getFutureCcShowInstancess(); $instanceIds = array(); foreach ($ccShowInstances as $ccShowInstance) { @@ -754,10 +872,10 @@ SQL; } /** - * + * * Returns the difference in seconds between a show's new and * old start time - * + * * @param $newStartDateTime DateTime object * @param $oldStartDateTime DateTime object */ @@ -767,9 +885,9 @@ SQL; } /** - * + * * Updates the start and end time for cc_show_instances - * + * * @param $showData edit show form data */ private function updateInstanceStartEndTime($diff) @@ -783,13 +901,13 @@ WHERE show_id = :showId SQL; Application_Common_Database::prepareAndExecute($sql, - array(':diff1' => $diff, ':diff2' => $diff, + array(':diff1' => $diff, ':diff2' => $diff, ':showId' => $this->ccShow->getDbId(), ':timestamp' => gmdate("Y-m-d H:i:s")), 'execute'); } /** - * + * * Enter description here ... * @param ccShowDays $showDay * @param DateTime $showStartDate user's local time @@ -826,7 +944,7 @@ SQL; } /** - * + * * Sets a single cc_show_instance table row * @param $showDay * @param $populateUntil @@ -842,7 +960,13 @@ SQL; if ($utcStartDateTime->getTimestamp() < $populateUntil->getTimestamp()) { $ccShowInstance = new CcShowInstances(); if ($this->isUpdate) { - $ccShowInstance = $this->getInstance($utcStartDateTime); + //use original cc_show_day object to get the current cc_show_instance + $origStartDateTime = new DateTime( + $this->origCcShowDay->getDbFirstShow()." ".$this->origCcShowDay->getDbStartTime(), + new DateTimeZone($this->origCcShowDay->getDbTimezone()) + ); + $origStartDateTime->setTimezone(new DateTimeZone("UTC")); + $ccShowInstance = $this->getInstance($origStartDateTime); } $ccShowInstance->setDbShowId($this->ccShow->getDbId()); @@ -851,19 +975,15 @@ SQL; $ccShowInstance->setDbRecord($showDay->getDbRecord()); $ccShowInstance->save(); - if ($this->isUpdate) { - $con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME); - $ccShowInstance->updateScheduleStatus($con); - } - if ($this->isRebroadcast) { $this->createRebroadcastInstances($showDay, $start, $ccShowInstance->getDbId()); } } + return $ccShowInstance; } /** - * + * * Sets multiple cc_show_instances table rows * @param unknown_type $showDay * @param unknown_type $populateUntil @@ -893,8 +1013,13 @@ SQL; $datePeriod = $this->getDatePeriod($start, $timezone, $last_show, $repeatInterval, $populateUntil); - $utcLastShowDateTime = $last_show ? - Application_Common_DateHelper::ConvertToUtcDateTime($last_show, $timezone) : null; + if ($last_show) { + $utcLastShowDateTime = new DateTime($last_show, new DateTimeZone($timezone)); + $utcLastShowDateTime->setTimezone(new DateTimeZone("UTC")); + } + else { + $utcLastShowDateTime = null; + } $previousDate = clone $start; @@ -904,13 +1029,9 @@ SQL; /* * Make sure start date is less than populate until date AND * last show date is null OR start date is less than last show date - * - * (NOTE: We cannot call getTimestamp() to compare the dates because of - * a PHP 5.3.3 bug with DatePeriod objects - See CC-5159 for more details) */ - if ($utcStartDateTime->format("Y-m-d H:i:s") <= $populateUntil->format("Y-m-d H:i:s") && - ( is_null($utcLastShowDateTime) || - $utcStartDateTime->format("Y-m-d H:i:s") < $utcLastShowDateTime->format("Y-m-d H:i:s")) ) { + if ($utcStartDateTime <= $populateUntil && + ( is_null($utcLastShowDateTime) || $utcStartDateTime < $utcLastShowDateTime) ) { $lastCreatedShow = clone $utcStartDateTime; /* There may not always be an instance when editing a show @@ -934,19 +1055,14 @@ SQL; } /* When editing the start/end time of a repeating show, we don't want to - * change shows that started in the past. So check the start time. + * change shows that are in the past so we check the end time. */ - if ($newInstance || $ccShowInstance->getDbStarts() > gmdate("Y-m-d H:i:s")) { + if ($newInstance || $ccShowInstance->getDbEnds() > gmdate("Y-m-d H:i:s")) { $ccShowInstance->setDbShowId($show_id); $ccShowInstance->setDbStarts($utcStartDateTime); $ccShowInstance->setDbEnds($utcEndDateTime); $ccShowInstance->setDbRecord($record); $ccShowInstance->save(); - - if ($updateScheduleStatus) { - $con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME); - $ccShowInstance->updateScheduleStatus($con); - } } if ($this->isRebroadcast) { @@ -964,8 +1080,9 @@ SQL; */ if (isset($lastCreatedShow)) { /* Set UTC to local time before setting the next repeat date. If we don't - * the next repeat date might be scheduled for the following day */ - $lastCreatedShow->setTimezone(new DateTimeZone(Application_Model_Preference::GetTimezone())); + * the next repeat date might be scheduled for the following day + * THIS MUST BE IN THE TIMEZONE THE SHOW WAS CREATED IN */ + $lastCreatedShow->setTimezone(new DateTimeZone($timezone)); $nextDate = $lastCreatedShow->add($repeatInterval); $this->setNextRepeatingShowDate($nextDate->format("Y-m-d"), $day, $show_id); } @@ -996,8 +1113,13 @@ SQL; $this->repeatType = $showDay->getDbRepeatType(); - $utcLastShowDateTime = $last_show ? - Application_Common_DateHelper::ConvertToUtcDateTime($last_show, $timezone) : null; + if ($last_show) { + $utcLastShowDateTime = new DateTime($last_show, new DateTimeZone($timezone)); + $utcLastShowDateTime->setTimezone(new DateTimeZone("UTC")); + } + else { + $utcLastShowDateTime = null; + } while ($start->getTimestamp() < $end->getTimestamp()) { list($utcStartDateTime, $utcEndDateTime) = $this->createUTCStartEndDateTime( @@ -1034,11 +1156,6 @@ SQL; $ccShowInstance->setDbEnds($utcEndDateTime); $ccShowInstance->setDbRecord($record); $ccShowInstance->save(); - - if ($updateScheduleStatus) { - $con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME); - $ccShowInstance->updateScheduleStatus($con); - } } if ($this->isRebroadcast) { @@ -1064,10 +1181,10 @@ SQL; } /** - * + * * i.e. last thursday of each month * i.e. second monday of each month - * + * * @param string $showStart * @param string $timezone user's local timezone */ @@ -1112,7 +1229,7 @@ SQL; } /** - * + * * Enter description here ... * @param $start user's local time */ @@ -1147,6 +1264,7 @@ SQL; do { $nextDT = date_create($weekNumberOfMonth." ".$dayOfWeek. " of ".$tempDT->format("F")." ".$tempDT->format("Y")); + $nextDT->setTimezone(new DateTimeZone($timezone)); /* We have to check if the next date is in the same month in case * the repeat day is in the fifth week of the month. @@ -1182,7 +1300,7 @@ SQL; } /** - * + * * Create a DatePeriod object in the user's local time * It will get converted to UTC before the show instance gets created */ @@ -1203,20 +1321,26 @@ SQL; } /** - * + * * Attempts to retrieve the cc_show_instance belonging to a cc_show * that starts at $starts. We have to pass in the start * time in case the show is repeating - * + * * Returns the instance if one was found (one that is not a recording * and modified instance is false (has not been deleted)) */ private function getInstance($starts) { + $temp = clone($starts); + $temp->setTimezone(new DateTimeZone($this->oldShowTimezone)); + $temp->setTime($this->localShowStartHour, $this->localShowStartMin); + + $temp->setTimezone(new DateTimeZone("UTC")); + $ccShowInstance = CcShowInstancesQuery::create() - ->filterByDbStarts($starts->format("Y-m-d H:i:s"), Criteria::EQUAL) + ->filterByDbStarts($temp->format("Y-m-d H:i:s"), Criteria::EQUAL) ->filterByDbShowId($this->ccShow->getDbId(), Criteria::EQUAL) - ->filterByDbModifiedInstance(false, Criteria::EQUAL) + //->filterByDbModifiedInstance(false, Criteria::EQUAL) ->filterByDbRebroadcast(0, Criteria::EQUAL) ->limit(1) ->find(); @@ -1250,7 +1374,7 @@ SQL; } /** - * + * * Sets the fields for a cc_show table row * @param $ccShow * @param $showData @@ -1285,7 +1409,7 @@ SQL; } /** - * + * * Sets the fields for a cc_show_days table row * @param $showData * @param $showId @@ -1298,34 +1422,42 @@ SQL; { $showId = $this->ccShow->getDbId(); - $startDateTime = new DateTime($showData['add_show_start_date']." ".$showData['add_show_start_time']); + $startDateTime = new DateTime( + $showData['add_show_start_date']." ".$showData['add_show_start_time'], + new DateTimeZone($showData['add_show_timezone']) + ); $endDateTime = $this->calculateEndDate($showData); if (!is_null($endDateTime)) { $endDate = $endDateTime->format("Y-m-d"); - } else { - $endDate = $endDateTime; + } + else { + $endDate = null; } - /* What we are doing here is checking if the show repeats or if - * any repeating days have been checked. If not, then by default - * the "selected" DOW is the initial day. - * DOW in local time. - */ - $startDow = date("w", $startDateTime->getTimestamp()); + //Our calculated start DOW must be used for non repeating since a day has not been selected. + //For all repeating shows, only the selected days of the week will be repeated on. + $startDow = $startDateTime->format("w"); if (!$showData['add_show_repeats']) { $showData['add_show_day_check'] = array($startDow); - } elseif ($showData['add_show_repeats'] && $showData['add_show_day_check'] == "") { - $showData['add_show_day_check'] = array($startDow); } // Don't set day for monthly repeat type, it's invalid if ($showData['add_show_repeats'] && $showData['add_show_repeat_type'] == 2) { - $showDay = new CcShowDays(); + + if ($this->isUpdate) { + $showDay = CcShowDaysQuery::create() + ->filterByDbShowId($showId) + ->filterByDbRepeatType($showData['add_show_repeat_type']) + ->findOne(); + } else { + $showDay = new CcShowDays(); + } + $showDay->setDbFirstShow($startDateTime->format("Y-m-d")); $showDay->setDbLastShow($endDate); $showDay->setDbStartTime($startDateTime->format("H:i:s")); - $showDay->setDbTimezone(Application_Model_Preference::GetTimezone()); + $showDay->setDbTimezone($showData['add_show_timezone']); $showDay->setDbDuration($showData['add_show_duration']); $showDay->setDbRepeatType($this->repeatType); $showDay->setDbShowId($showId); @@ -1347,11 +1479,26 @@ SQL; $startDateTimeClone->add(new DateInterval("P".$daysAdd."D")); } if (is_null($endDate) || $startDateTimeClone->getTimestamp() <= $endDateTime->getTimestamp()) { - $showDay = new CcShowDays(); + + if ($this->isUpdate) { + $showDay = CcShowDaysQuery::create() + ->filterByDbShowId($showId) + ->filterByDbRepeatType($this->repeatType) + ->filterByDbDay($day) + ->findOne(); + if (!$showDay) { + //if no show day object was found it is because a new + //repeating day of the week was added + $showDay = new CcShowDays(); + } + } else { + $showDay = new CcShowDays(); + } + $showDay->setDbFirstShow($startDateTimeClone->format("Y-m-d")); $showDay->setDbLastShow($endDate); $showDay->setDbStartTime($startDateTimeClone->format("H:i")); - $showDay->setDbTimezone(Application_Model_Preference::GetTimezone()); + $showDay->setDbTimezone($showData['add_show_timezone']); $showDay->setDbDuration($showData['add_show_duration']); $showDay->setDbDay($day); $showDay->setDbRepeatType($this->repeatType); @@ -1367,7 +1514,7 @@ SQL; } /** - * + * * Deletes all the cc_show_rebroadcast entries for a specific show * that is currently being edited. They will get recreated with * the new show specs @@ -1378,7 +1525,7 @@ SQL; } /** - * + * * Sets the fields for a cc_show_rebroadcast table row * @param $showData * @param $showId @@ -1417,7 +1564,7 @@ SQL; } /** - * + * * Deletes all the cc_show_hosts entries for a specific show * that is currently being edited. They will get recreated with * the new show specs @@ -1428,7 +1575,7 @@ SQL; } /** - * + * * Sets the fields for a cc_show_hosts table row * @param $showData * @param $showId @@ -1446,10 +1593,10 @@ SQL; } /** - * + * * Gets the date and time shows (particularly repeating shows) * can be populated until. - * + * * @return DateTime object */ private static function getPopulateShowUntilDateTIme() @@ -1464,44 +1611,46 @@ SQL; } /** - * + * * Enter description here ... * @param DateTime $showStart user's local time * @param string $duration time interval (h)h:(m)m(:ss) * @param string $timezone "Europe/Prague" * @param array $offset (days, hours, mins) used for rebroadcast shows - * + * * @return array of 2 DateTime objects, start/end time of the show in UTC */ private function createUTCStartEndDateTime($showStart, $duration, $offset=null) { $startDateTime = clone $showStart; + $timezone = $startDateTime->getTimezone(); if (isset($offset)) { //$offset["hours"] and $offset["mins"] represents the start time //of a rebroadcast show $startDateTime = new DateTime($startDateTime->format("Y-m-d")." ". - $offset["hours"].":".$offset["mins"]); + $offset["hours"].":".$offset["mins"], $timezone); $startDateTime->add(new DateInterval("P{$offset["days"]}D")); } - //convert time to UTC - $startDateTime->setTimezone(new DateTimeZone('UTC')); $endDateTime = clone $startDateTime; $duration = explode(":", $duration); list($hours, $mins) = array_slice($duration, 0, 2); $endDateTime->add(new DateInterval("PT{$hours}H{$mins}M")); + $startDateTime->setTimezone(new DateTimeZone('UTC')); + $endDateTime->setTimezone(new DateTimeZone('UTC')); + return array($startDateTime, $endDateTime); } /** - * + * * Show instances for repeating shows only get created up * until what is visible on the calendar. We need to set the * date for when the next repeating show instance should be created * as the user browses the calendar further. - * + * * @param $nextDate * @param $showId * @param $day @@ -1513,6 +1662,7 @@ SQL; $repeatInfo = CcShowDaysQuery::create() ->filterByDbShowId($showId) ->filterByDbDay($day) + ->filterByDbRepeatType(-1, Criteria::NOT_EQUAL) ->findOne(); $repeatInfo->setDbNextPopDate($nextInfo[0]) diff --git a/airtime_mvc/application/validate/NotDemoValidate.php b/airtime_mvc/application/validate/NotDemoValidate.php new file mode 100644 index 000000000..2ebdfcc14 --- /dev/null +++ b/airtime_mvc/application/validate/NotDemoValidate.php @@ -0,0 +1,24 @@ + "Cannot be changed in demo mode" + ); + + public function isValid($value) + { + $this->_setValue($value); + + $CC_CONFIG = Config::getConfig(); + if (isset($CC_CONFIG['demo']) && $CC_CONFIG['demo'] == 1) { + $this->_error(self::NOTDEMO); + return false; + } else { + return true; + } + } +} + diff --git a/airtime_mvc/application/views/scripts/audiopreview/audio-preview.phtml b/airtime_mvc/application/views/scripts/audiopreview/audio-preview.phtml index fcade27d9..27aac26eb 100644 --- a/airtime_mvc/application/views/scripts/audiopreview/audio-preview.phtml +++ b/airtime_mvc/application/views/scripts/audiopreview/audio-preview.phtml @@ -20,26 +20,26 @@ @@ -76,8 +76,8 @@ diff --git a/airtime_mvc/application/views/scripts/dashboard/about.phtml b/airtime_mvc/application/views/scripts/dashboard/about.phtml index 3f50f42f8..05f65cd80 100644 --- a/airtime_mvc/application/views/scripts/dashboard/about.phtml +++ b/airtime_mvc/application/views/scripts/dashboard/about.phtml @@ -1,5 +1,5 @@
-

+

-

+