Added live-info-v2 and station metadata api calls

This commit is contained in:
Duncan Sommerville 2014-10-24 15:11:27 -04:00
parent 0c538ff1ae
commit 598b18b65a
5 changed files with 669 additions and 221 deletions

View File

@ -70,18 +70,18 @@ class Application_Common_DateHelper
public static function getUserTimezoneOffset() public static function getUserTimezoneOffset()
{ {
$userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); $userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone());
$now = new DateTime("now", $userTimezone); $now = new DateTime("now", $userTimezone);
return $now->format("Z"); return $now->format("Z");
} }
public static function getStationTimezoneOffset() public static function getStationTimezoneOffset()
{ {
$stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone());
$now = new DateTime("now", $stationTimezone); $now = new DateTime("now", $stationTimezone);
return $now->format("Z"); return $now->format("Z");
} }
/** /**
@ -90,12 +90,12 @@ class Application_Common_DateHelper
*/ */
public static function getTodayStationStartDateTime() public static function getTodayStationStartDateTime()
{ {
$stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone());
$now = new DateTime("now", $stationTimezone); $now = new DateTime("now", $stationTimezone);
$now->setTime(0, 0, 0); $now->setTime(0, 0, 0);
return $now; return $now;
} }
/** /**
@ -104,13 +104,13 @@ class Application_Common_DateHelper
*/ */
public static function getTodayStationEndDateTime() public static function getTodayStationEndDateTime()
{ {
$stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone());
$now = new DateTime("now", $stationTimezone); $now = new DateTime("now", $stationTimezone);
$now->add(new DateInterval("P1D")); $now->add(new DateInterval("P1D"));
$now->setTime(0, 0, 0); $now->setTime(0, 0, 0);
return $now; return $now;
} }
/** /**
@ -119,17 +119,17 @@ class Application_Common_DateHelper
*/ */
public static function getWeekStartDateTime() public static function getWeekStartDateTime()
{ {
$now = self::getTodayStationStartDateTime(); $now = self::getTodayStationStartDateTime();
// our week starts on monday, but php week starts on sunday. // our week starts on monday, but php week starts on sunday.
$day = $now->format('w'); $day = $now->format('w');
if ($day == 0) { if ($day == 0) {
$day = 7; $day = 7;
} }
$dayDiff = $day - 1; $dayDiff = $day - 1;
if ($dayDiff > 0) { if ($dayDiff > 0) {
$now->sub(new DateInterval("P{$dayDiff}D")); $now->sub(new DateInterval("P{$dayDiff}D"));
} }
return $now; return $now;
@ -250,13 +250,13 @@ class Application_Common_DateHelper
* @return string in $format default Y-m-d H:i:s in station 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") { public static function UTCStringToStationTimezoneString($datetime, $format="Y-m-d H:i:s") {
$stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); $stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone());
$utcTimezone = new DateTimeZone("UTC"); $utcTimezone = new DateTimeZone("UTC");
$d = new DateTime($datetime, $utcTimezone); $d = new DateTime($datetime, $utcTimezone);
$d->setTimezone($stationTimezone); $d->setTimezone($stationTimezone);
return $d->format($format); return $d->format($format);
} }
/* /*
@ -265,13 +265,13 @@ class Application_Common_DateHelper
* @return string Y-m-d H:i:s in user's 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") { public static function UTCStringToUserTimezoneString($datetime, $format="Y-m-d H:i:s") {
$userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); $userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone());
$utcTimezone = new DateTimeZone("UTC"); $utcTimezone = new DateTimeZone("UTC");
$d = new DateTime($datetime, $utcTimezone); $d = new DateTime($datetime, $utcTimezone);
$d->setTimezone($userTimezone); $d->setTimezone($userTimezone);
return $d->format($format); return $d->format($format);
} }
/* /*
@ -280,13 +280,13 @@ class Application_Common_DateHelper
* @return string Y-m-d H:i:s in UTC timezone * @return string Y-m-d H:i:s in UTC timezone
*/ */
public static function UserTimezoneStringToUTCString($datetime, $format="Y-m-d H:i:s") { public static function UserTimezoneStringToUTCString($datetime, $format="Y-m-d H:i:s") {
$userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone()); $userTimezone = new DateTimeZone(Application_Model_Preference::GetUserTimezone());
$utcTimezone = new DateTimeZone("UTC"); $utcTimezone = new DateTimeZone("UTC");
$d = new DateTime($datetime, $userTimezone); $d = new DateTime($datetime, $userTimezone);
$d->setTimezone($utcTimezone); $d->setTimezone($utcTimezone);
return $d->format($format); return $d->format($format);
} }
/** /**
@ -299,19 +299,99 @@ class Application_Common_DateHelper
*/ */
public static function convertTimestamps(&$rows, $columnsToConvert, $domain="station") public static function convertTimestamps(&$rows, $columnsToConvert, $domain="station")
{ {
if (!is_array($rows)) { if (!is_array($rows)) {
return; return;
} }
$converter = "UTCStringTo".ucfirst($domain)."TimezoneString"; $converter = "UTCStringTo".ucfirst($domain)."TimezoneString";
foreach ($rows as &$row) { foreach ($rows as &$row) {
foreach ($columnsToConvert as $column) { foreach ($columnsToConvert as $column) {
$row[$column] = self::$converter($row[$column]); $row[$column] = self::$converter($row[$column]);
} }
} }
} }
/**
* 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 $timezone convert to the given timezone.
* @param string $format time format to convert to
*/
public static function convertTimestampsToTimezone(&$rows, $columnsToConvert, $timezone, $format="Y-m-d H:i:s")
{
$timezone = strtolower($timezone);
// Check that the timezone is valid and rows is an array
if (!is_array($rows)) {
return;
}
foreach ($rows as &$row) {
if (is_array($row)) {
foreach ($columnsToConvert as $column) {
if (array_key_exists($column, $row)) {
$newTimezone = new DateTimeZone($timezone);
$utcTimezone = new DateTimeZone("UTC");
$d = new DateTime($row[$column], $utcTimezone);
$d->setTimezone($newTimezone);
$row[$column] = $d->format($format);
}
}
self::convertTimestampsToTimezone($row, $columnsToConvert, $timezone, $format);
}
}
}
/**
* Return the end date time in the given timezone
*
* @return DateTime
*/
public static function getEndDateTime($timezoneString, $days)
{
$timezone = new DateTimeZone($timezoneString);
$now = new DateTime("now", $timezone);
$now->add(new DateInterval("P".$days."D"));
$now->setTime(0, 0, 0);
return $now;
}
/**
* Return a formatted string representing the
* given datetime in the given timezone
*
* @param unknown $datetime the time to convert
* @param unknown $timezone the timezone to convert to
* @param string $format the formatted string
*/
public static function UTCStringToTimezoneString($datetime, $timezone, $format="Y-m-d H:i:s") {
$d = new DateTime($datetime, new DateTimeZone("UTC"));
$timezone = strtolower($timezone);
$newTimezone = new DateTimeZone($timezone);
$d->setTimezone($newTimezone);
return $d->format($format);
}
/**
* Return the timezone offset in seconds for the given timezone
*
* @param unknown $userDefinedTimezone the timezone used to determine the offset
*/
public static function getTimezoneOffset($userDefinedTimezone) {
$now = new DateTimeZone($userDefinedTimezone);
$d = new DateTime("now", $now);
return $d->format("Z");
}
/** /**
* This function is used for calculations! Don't modify for display purposes! * This function is used for calculations! Don't modify for display purposes!
* *

View File

@ -80,4 +80,17 @@ class Application_Common_OsPath{
return $baseUrl; return $baseUrl;
} }
public static function formatDirectoryWithDirectorySeparators($dir)
{
if ($dir[0] != "/") {
$dir = "/".$dir;
}
if ($dir[strlen($dir) -1] != "/") {
$dir = $dir."/";
}
return $dir;
}
} }

View File

@ -5,7 +5,8 @@ class ApiController extends Zend_Controller_Action
public function init() public function init()
{ {
$ignoreAuth = array("live-info", "week-info"); $ignoreAuth = array("live-info", "live-info-v2", "week-info",
"station-metadata", "station-logo");
$params = $this->getRequest()->getParams(); $params = $this->getRequest()->getParams();
if (!in_array($params['action'], $ignoreAuth)) { if (!in_array($params['action'], $ignoreAuth)) {
@ -246,70 +247,72 @@ class ApiController extends Zend_Controller_Action
$this->view->layout()->disableLayout(); $this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true); $this->_helper->viewRenderer->setNoRender(true);
$request = $this->getRequest();
$utcTimeNow = gmdate("Y-m-d H:i:s"); $utcTimeNow = gmdate("Y-m-d H:i:s");
$utcTimeEnd = ""; // if empty, getNextShows will use interval instead of end of day $utcTimeEnd = ""; // if empty, getNextShows will use interval instead of end of day
$request = $this->getRequest(); // default to the station timezone
$timezone = Application_Model_Preference::GetDefaultTimezone();
$userDefinedTimezone = strtolower($request->getParam('timezone'));
$upcase = false; // only upcase the timezone abbreviations
$this->checkTimezone($userDefinedTimezone, $timezone, $upcase);
$type = $request->getParam('type'); $type = $request->getParam('type');
/* This is some *extremely* lazy programming that needs to bi fixed. For some reason $limit = $request->getParam('limit');
if ($limit == "" || !is_numeric($limit)) {
$limit = "5";
}
/* This is some *extremely* lazy programming that needs to be fixed. For some reason
* we are using two entirely different codepaths for very similar functionality (type = endofday * we are using two entirely different codepaths for very similar functionality (type = endofday
* vs type = interval). Needs to be fixed for 2.3 - MK */ * vs type = interval). Needs to be fixed for 2.3 - MK */
if ($type == "endofday") { if ($type == "endofday") {
$limit = $request->getParam('limit');
if ($limit == "" || !is_numeric($limit)) {
$limit = "5";
}
// make getNextShows use end of day // make getNextShows use end of day
$end = Application_Common_DateHelper::getTodayStationEndDateTime(); $end = Application_Common_DateHelper::getTodayStationEndDateTime();
$end->setTimezone(new DateTimeZone("UTC")); $end->setTimezone(new DateTimeZone("UTC"));
$utcTimeEnd = $end->format("Y-m-d H:i:s"); $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 = array(
$result["previous"]["name"] = htmlspecialchars($result["previous"]["name"]); "env" => APPLICATION_ENV,
$result["current"]["name"] = htmlspecialchars($result["current"]["name"]); "schedulerTime" => $utcTimeNow,
$result["next"]["name"] = htmlspecialchars($result["next"]["name"]); "currentShow" => Application_Model_Show::getCurrentShow($utcTimeNow),
"nextShow" => Application_Model_Show::getNextShows($utcTimeNow, $limit, $utcTimeEnd)
);
} else {
$result = Application_Model_Schedule::GetPlayOrderRangeOld($limit);
} }
// XSS exploit prevention // XSS exploit prevention
foreach ($result["currentShow"] as &$current) { $this->convertSpecialChars($result, array("name", "url"));
$current["name"] = htmlspecialchars($current["name"]); // apply user-defined timezone, or default to station
} Application_Common_DateHelper::convertTimestampsToTimezone(
foreach ($result["nextShow"] as &$next) { $result['currentShow'],
$next["name"] = htmlspecialchars($next["name"]); array("starts", "ends", "start_timestamp","end_timestamp"),
} $timezone
);
Application_Common_DateHelper::convertTimestampsToTimezone(
$result['nextShow'],
array("starts", "ends", "start_timestamp","end_timestamp"),
$timezone
);
//For consistency, all times here are being sent in the station timezone, which //Convert the UTC scheduler time ("now") to the user-defined timezone.
//seems to be what we've normalized everything to. $result["schedulerTime"] = Application_Common_DateHelper::UTCStringToTimezoneString($result["schedulerTime"], $timezone);
$result["timezone"] = $upcase ? strtoupper($timezone) : $timezone;
$result["timezoneOffset"] = Application_Common_DateHelper::getTimezoneOffset($timezone);
//Convert the UTC scheduler time ("now") to the station timezone. // used by caller to determine if the airtime they are running or widgets in use is out of date.
$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; $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION;
header("Content-Type: application/json"); header("Content-Type: application/json");
if (version_compare(phpversion(), '5.4.0', '<')) {
$js = json_encode($result);
} else {
$js = json_encode($result, JSON_PRETTY_PRINT);
}
// If a callback is not given, then just provide the raw JSON. // If a callback is not given, then just provide the raw JSON.
echo isset($_GET['callback']) ? $_GET['callback'].'('.json_encode($result).')' : json_encode($result); echo isset($_GET['callback']) ? $_GET['callback'].'('.$js.')' : $js;
} else { } else {
header('HTTP/1.0 401 Unauthorized'); header('HTTP/1.0 401 Unauthorized');
print _('You are not allowed to access this resource. '); print _('You are not allowed to access this resource. ');
@ -317,6 +320,125 @@ class ApiController extends Zend_Controller_Action
} }
} }
/**
* Retrieve the currently playing show as well as upcoming shows.
* Number of shows returned and the time interval in which to
* get the next shows can be configured as GET parameters.
*
* Possible parameters:
* days - How many days to retrieve.
* Default is 2 (today + tomorrow).
* shows - How many shows to retrieve
* Default is 5.
* timezone - The timezone to send the times in
* Defaults to the station timezone
*/
public function liveInfoV2Action()
{
if (Application_Model_Preference::GetAllow3rdPartyApi()) {
// disable the view and the layout
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$request = $this->getRequest();
$utcTimeNow = gmdate("Y-m-d H:i:s");
$utcTimeEnd = ""; // if empty, getNextShows will use interval instead of end of day
// default to the station timezone
$timezone = Application_Model_Preference::GetDefaultTimezone();
$userDefinedTimezone = strtolower($request->getParam('timezone'));
$upcase = false; // only upcase the timezone abbreviations
$this->checkTimezone($userDefinedTimezone, $timezone, $upcase);
$daysToRetrieve = $request->getParam('days');
$showsToRetrieve = $request->getParam('shows');
if ($daysToRetrieve == "" || !is_numeric($daysToRetrieve)) {
$daysToRetrieve = "2";
}
if ($showsToRetrieve == "" || !is_numeric($showsToRetrieve)) {
$showsToRetrieve = "5";
}
// set the end time to the day's start n days from now.
// days=1 will return shows until the end of the current day,
// days=2 will return shows until the end of tomorrow, etc.
$end = Application_Common_DateHelper::getEndDateTime($timezone, $daysToRetrieve);
$end->setTimezone(new DateTimeZone("UTC"));
$utcTimeEnd = $end->format("Y-m-d H:i:s");
$result = Application_Model_Schedule::GetPlayOrderRange($utcTimeEnd, $showsToRetrieve);
// XSS exploit prevention
$this->convertSpecialChars($result, array("name", "url"));
// apply user-defined timezone, or default to station
$this->applyLiveTimezoneAdjustments($result, $timezone, $upcase);
// used by caller to determine if the airtime they are running or widgets in use is out of date.
$result["station"]["AIRTIME_API_VERSION"] = AIRTIME_API_VERSION;
header("Content-Type: application/json");
if (version_compare(phpversion(), '5.4.0', '<')) {
$js = json_encode($result);
} else {
$js = json_encode($result, JSON_PRETTY_PRINT);
}
// If a callback is not given, then just provide the raw JSON.
echo isset($_GET['callback']) ? $_GET['callback'].'('.$js.')' : $js;
} else {
header('HTTP/1.0 401 Unauthorized');
print _('You are not allowed to access this resource. ');
exit;
}
}
/**
* Check that the value for the timezone the user gave is valid.
* If it is, override the default (station) timezone.
* If it's an abbreviation (pst, edt) we upcase the output.
*
* @param string $userDefinedTimezone the requested timezone value
* @param string $timezone the default timezone
* @param boolean $upcase whether the timezone output should be upcased
*/
private function checkTimezone($userDefinedTimezone, &$timezone, &$upcase)
{
$delimiter = "/";
// if the user passes in a timezone in standard form ("Continent/City")
// we need to fix the downcased string by upcasing each word delimited by a /
if (strpos($userDefinedTimezone, $delimiter) !== false) {
$userDefinedTimezone = implode($delimiter, array_map('ucfirst', explode($delimiter, $userDefinedTimezone)));
}
// if the timezone defined by the user exists, use that
if (array_key_exists($userDefinedTimezone, timezone_abbreviations_list())) {
$timezone = $userDefinedTimezone;
$upcase = true;
} else if (in_array($userDefinedTimezone, timezone_identifiers_list())) {
$timezone = $userDefinedTimezone;
}
}
/**
* If the user passed in a timezone parameter, adjust timezone-dependent
* variables in the result to reflect the given timezone.
*
* @param object $result reference to the object to send back to the user
* @param string $timezone the user's timezone parameter value
* @param boolean $upcase whether the timezone output should be upcased
*/
private function applyLiveTimezoneAdjustments(&$result, $timezone, $upcase)
{
Application_Common_DateHelper::convertTimestampsToTimezone(
$result,
array("starts", "ends", "start_timestamp","end_timestamp"),
$timezone
);
//Convert the UTC scheduler time ("now") to the user-defined timezone.
$result["station"]["schedulerTime"] = Application_Common_DateHelper::UTCStringToTimezoneString($result["station"]["schedulerTime"], $timezone);
$result["station"]["timezone"] = $upcase ? strtoupper($timezone) : $timezone;
}
public function weekInfoAction() public function weekInfoAction()
{ {
if (Application_Model_Preference::GetAllow3rdPartyApi()) { if (Application_Model_Preference::GetAllow3rdPartyApi()) {
@ -328,50 +450,55 @@ class ApiController extends Zend_Controller_Action
$weekStartDateTime = Application_Common_DateHelper::getWeekStartDateTime(); $weekStartDateTime = Application_Common_DateHelper::getWeekStartDateTime();
$dow = array("monday", "tuesday", "wednesday", "thursday", "friday", $dow = array("monday", "tuesday", "wednesday", "thursday", "friday",
"saturday", "sunday", "nextmonday", "nexttuesday", "nextwednesday", "saturday", "sunday", "nextmonday", "nexttuesday", "nextwednesday",
"nextthursday", "nextfriday", "nextsaturday", "nextsunday"); "nextthursday", "nextfriday", "nextsaturday", "nextsunday");
$result = array(); $result = array();
// default to the station timezone
$timezone = Application_Model_Preference::GetDefaultTimezone();
$userDefinedTimezone = strtolower($this->getRequest()->getParam("timezone"));
// if the timezone defined by the user exists, use that
if (array_key_exists($userDefinedTimezone, timezone_abbreviations_list())) {
$timezone = $userDefinedTimezone;
}
$utcTimezone = new DateTimeZone("UTC"); $utcTimezone = new DateTimeZone("UTC");
$stationTimezone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone());
$weekStartDateTime->setTimezone($utcTimezone); $weekStartDateTime->setTimezone($utcTimezone);
$utcDayStart = $weekStartDateTime->format("Y-m-d H:i:s"); $utcDayStart = $weekStartDateTime->format("Y-m-d H:i:s");
for ($i = 0; $i < 14; $i++) { for ($i = 0; $i < 14; $i++) {
//have to be in station timezone when adding 1 day for daylight savings. //have to be in station timezone when adding 1 day for daylight savings.
$weekStartDateTime->setTimezone($stationTimezone); $weekStartDateTime->setTimezone(new DateTimeZone($timezone));
$weekStartDateTime->add(new DateInterval('P1D')); $weekStartDateTime->add(new DateInterval('P1D'));
//convert back to UTC to get the actual timestamp used for search. //convert back to UTC to get the actual timestamp used for search.
$weekStartDateTime->setTimezone($utcTimezone); $weekStartDateTime->setTimezone($utcTimezone);
$utcDayEnd = $weekStartDateTime->format("Y-m-d H:i:s"); $utcDayEnd = $weekStartDateTime->format("Y-m-d H:i:s");
$shows = Application_Model_Show::getNextShows($utcDayStart, "ALL", $utcDayEnd); $shows = Application_Model_Show::getNextShows($utcDayStart, "ALL", $utcDayEnd);
$utcDayStart = $utcDayEnd; $utcDayStart = $utcDayEnd;
Application_Common_DateHelper::convertTimestamps( // convert to user-defined timezone, or default to station
$shows, Application_Common_DateHelper::convertTimestampsToTimezone(
$shows,
array("starts", "ends", "start_timestamp","end_timestamp"), array("starts", "ends", "start_timestamp","end_timestamp"),
"station" $timezone
); );
$result[$dow[$i]] = $shows; $result[$dow[$i]] = $shows;
} }
// XSS exploit prevention // XSS exploit prevention
foreach ($dow as $d) { $this->convertSpecialChars($result, array("name", "url"));
foreach ($result[$d] as &$show) {
$show["name"] = htmlspecialchars($show["name"]);
$show["url"] = htmlspecialchars($show["url"]);
}
}
//used by caller to determine if the airtime they are running or widgets in use is out of date. //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; $result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION;
header("Content-type: text/javascript"); header("Content-type: text/javascript");
$js = json_encode($result, JSON_PRETTY_PRINT);
// If a callback is not given, then just provide the raw JSON. // If a callback is not given, then just provide the raw JSON.
echo isset($_GET['callback']) ? $_GET['callback'].'('.json_encode($result).')' : json_encode($result); echo isset($_GET['callback']) ? $_GET['callback'].'('.$js.')' : $js;
} else { } else {
header('HTTP/1.0 401 Unauthorized'); header('HTTP/1.0 401 Unauthorized');
print _('You are not allowed to access this resource. '); print _('You are not allowed to access this resource. ');
@ -428,6 +555,94 @@ class ApiController extends Zend_Controller_Action
$this->_helper->json->sendJson(array("status"=>1, "message"=>"")); $this->_helper->json->sendJson(array("status"=>1, "message"=>""));
} }
/**
* Go through a given array and sanitize any potentially exploitable fields
* by passing them through htmlspecialchars
*
* @param unknown $arr the array to sanitize
* @param unknown $keys indexes of values to be sanitized
*/
private function convertSpecialChars(&$arr, $keys)
{
foreach ($arr as &$a) {
if (is_array($a)) {
foreach ($keys as &$key) {
if (array_key_exists($key, $a)) {
$a[$key] = htmlspecialchars($a[$key]);
}
}
$this->convertSpecialChars($a, $keys);
}
}
}
/**
* API endpoint to provide station metadata
*/
public function stationMetadataAction()
{
if (Application_Model_Preference::GetAllow3rdPartyApi()) {
// disable the view and the layout
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$CC_CONFIG = Config::getConfig();
$baseDir = Application_Common_OsPath::formatDirectoryWithDirectorySeparators($CC_CONFIG['baseDir']);
$path = 'http://'.$_SERVER['HTTP_HOST'].$baseDir."api/station-logo";
$result["name"] = Application_Model_Preference::GetStationName();
$result["logo"] = $path;
$result["description"] = Application_Model_Preference::GetStationDescription();
$result["timezone"] = Application_Model_Preference::GetDefaultTimezone();
$result["locale"] = Application_Model_Preference::GetDefaultLocale();
// 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;
header("Content-type: text/javascript");
$js = json_encode($result, JSON_PRETTY_PRINT);
// If a callback is not given, then just provide the raw JSON.
echo isset($_GET['callback']) ? $_GET['callback'].'('.$js.')' : $js;
} else {
header('HTTP/1.0 401 Unauthorized');
print _('You are not allowed to access this resource. ');
exit;
}
}
/**
* API endpoint to display the current station logo
*/
public function stationLogoAction()
{
if (Application_Model_Preference::GetAllow3rdPartyApi()) {
// disable the view and the layout
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$logo = Application_Model_Preference::GetStationLogo();
// if there's no logo, just die - redirects to a 404
if (!$logo || $logo === '') {
return;
}
// we're passing this as an image instead of using it in a data uri, so decode it
$blob = base64_decode($logo);
// use finfo to get the mimetype from the decoded blob
$f = finfo_open();
$mime_type = finfo_buffer($f, $blob, FILEINFO_MIME_TYPE);
finfo_close($f);
header("Content-type: " . $mime_type);
echo $blob;
} else {
header('HTTP/1.0 401 Unauthorized');
print _('You are not allowed to access this resource. ');
exit;
}
}
public function recordedShowsAction() public function recordedShowsAction()
{ {
$utcTimezone = new DateTimeZone("UTC"); $utcTimezone = new DateTimeZone("UTC");
@ -1044,7 +1259,7 @@ class ApiController extends Zend_Controller_Action
if (isset($data_arr->title)) { if (isset($data_arr->title)) {
$data_title = substr($data_arr->title, 0, 1024); $data_title = substr($data_arr->title, 0, 1024);
$previous_metadata = CcWebstreamMetadataQuery::create() $previous_metadata = CcWebstreamMetadataQuery::create()
->orderByDbStartTime('desc') ->orderByDbStartTime('desc')
@ -1061,7 +1276,7 @@ class ApiController extends Zend_Controller_Action
if ($do_insert) { if ($do_insert) {
$startDT = new DateTime("now", new DateTimeZone("UTC")); $startDT = new DateTime("now", new DateTimeZone("UTC"));
$webstream_metadata = new CcWebstreamMetadata(); $webstream_metadata = new CcWebstreamMetadata();
$webstream_metadata->setDbInstanceId($media_id); $webstream_metadata->setDbInstanceId($media_id);

View File

@ -56,41 +56,77 @@ SQL;
return $real_streams; return $real_streams;
} }
/** /**
* Returns data related to the scheduled items. * Returns data related to the scheduled items.
*
* @param int $p_prev
* @param int $p_next
* @return date
*/ */
public static function GetPlayOrderRange($p_prev = 1, $p_next = 1) public static function GetPlayOrderRange($utcTimeEnd = null, $showsToRetrieve = 5)
{ {
//Everything in this function must be done in UTC. You will get a swift kick in the pants if you mess that up. // 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)) { // when timeEnd is unspecified, return to the default behaviour - set a range of 48 hours from current time
//must enter integers to specify ranges if (!$utcTimeEnd) {
Logging::info("Invalid range parameters: $p_prev or $p_next"); $end = new DateTime();
$end->add(new DateInterval("P2D")); // Add 2 days
return array(); $end->setTimezone(new DateTimeZone("UTC"));
$utcTimeEnd = $end->format("Y-m-d H:i:s");
} }
$utcNow = new DateTime("now", new DateTimeZone("UTC")); $utcNow = new DateTime("now", new DateTimeZone("UTC"));
$shows = Application_Model_Show::getPrevCurrentNext($utcNow); $shows = Application_Model_Show::getPrevCurrentNext($utcNow, $utcTimeEnd, $showsToRetrieve);
$previousShowID = count($shows['previousShow'])>0?$shows['previousShow'][0]['instance_id']:null;
$currentShowID = count($shows['currentShow'])>0?$shows['currentShow']['instance_id']:null;
$nextShowID = count($shows['nextShow'])>0?$shows['nextShow'][0]['instance_id']:null;
$results = self::GetPrevCurrentNext($previousShowID, $currentShowID, $nextShowID, $utcNow);
$range = array(
"station" => array (
"env" => APPLICATION_ENV,
"schedulerTime" => $utcNow->format("Y-m-d H:i:s")
),
//Previous, current, next songs!
"tracks" => array(
"previous" => $results['previous'],
"current" => $results['current'],
"next" => $results['next']
),
//Current and next shows
"shows" => array (
"previous" => $shows['previousShow'],
"current" => $shows['currentShow'],
"next" => $shows['nextShow']
)
);
return $range;
}
/**
* Old version of the function for backwards compatibility
*/
public static function GetPlayOrderRangeOld()
{
// Everything in this function must be done in UTC. You will get a swift kick in the pants if you mess that up.
$utcNow = new DateTime("now", new DateTimeZone("UTC"));
$shows = Application_Model_Show::getPrevCurrentNextOld($utcNow);
$previousShowID = count($shows['previousShow'])>0?$shows['previousShow'][0]['instance_id']:null; $previousShowID = count($shows['previousShow'])>0?$shows['previousShow'][0]['instance_id']:null;
$currentShowID = count($shows['currentShow'])>0?$shows['currentShow'][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; $nextShowID = count($shows['nextShow'])>0?$shows['nextShow'][0]['instance_id']:null;
$results = self::GetPrevCurrentNext($previousShowID, $currentShowID, $nextShowID, $utcNow); $results = self::GetPrevCurrentNext($previousShowID, $currentShowID, $nextShowID, $utcNow);
$range = array("env"=>APPLICATION_ENV, $range = array(
"schedulerTime"=> $utcNow->format("Y-m-d H:i:s"), "env" => APPLICATION_ENV,
//Previous, current, next songs! "schedulerTime" => $utcNow->format("Y-m-d H:i:s"),
"previous"=>$results['previous'] !=null?$results['previous']:(count($shows['previousShow'])>0?$shows['previousShow'][0]:null), //Previous, current, next songs!
"current"=>$results['current'] !=null?$results['current']:((count($shows['currentShow'])>0 && $shows['currentShow'][0]['record'] == 1)?$shows['currentShow'][0]:null), "previous"=>$results['previous'] !=null?$results['previous']:(count($shows['previousShow'])>0?$shows['previousShow'][0]:null),
"next"=> $results['next'] !=null?$results['next']:(count($shows['nextShow'])>0?$shows['nextShow'][0]:null), "current"=>$results['current'] !=null?$results['current']:((count($shows['currentShow'])>0 && $shows['currentShow'][0]['record'] == 1)?$shows['currentShow'][0]:null),
//Current and next shows "next"=> $results['next'] !=null?$results['next']:(count($shows['nextShow'])>0?$shows['nextShow'][0]:null),
"currentShow"=>$shows['currentShow'], //Current and next shows
"nextShow"=>$shows['nextShow'], "currentShow"=>$shows['currentShow'],
"nextShow"=>$shows['nextShow']
); );
return $range; return $range;

View File

@ -1106,14 +1106,14 @@ SQL;
} }
/** /**
* Gets the current show, previous and next with an 2day window from * Gets the current show, previous and next with an n-day window from
* the given timeNow, so timeNow-2days and timeNow+2days. * the given timeNow, so timeNow-2days and timeNow+$daysToRetrieve days.
* *
* @param $utcNow A DateTime object containing the current time in UTC. * @param $utcNow A DateTime object containing the current time in UTC.
* @return An array (with stupid sub-arrays) containing the previous show id, * @return An array containing the previous show,
* current show id, and next show id. * current show, and next show.
*/ */
public static function getPrevCurrentNext($utcNow) public static function getPrevCurrentNext($utcNow, $utcEndStr, $showsToRetrieve)
{ {
$timeZone = new DateTimeZone("UTC"); //This function works entirely in UTC. $timeZone = new DateTimeZone("UTC"); //This function works entirely in UTC.
assert(get_class($utcNow) === "DateTime"); assert(get_class($utcNow) === "DateTime");
@ -1121,16 +1121,108 @@ SQL;
$CC_CONFIG = Config::getConfig(); $CC_CONFIG = Config::getConfig();
$con = Propel::getConnection(); $con = Propel::getConnection();
//
// This will fetch the currently playing show first, then any
// upcoming shows within our interval, and finally move on to
// previous shows in the past 2 days.
$sql = <<<SQL
SELECT s.name,
s.description,
s.genre,
s.id,
si.id AS instance_id,
si.description AS instance_description,
si.record,
s.url,
s.image_path,
starts,
ends
FROM cc_show_instances si
LEFT JOIN cc_show s
ON si.show_id = s.id
WHERE si.show_id = s.id
AND si.starts >= :timeNow::timestamp - INTERVAL '2 days'
AND si.starts < :timeEnd::timestamp
AND modified_instance != TRUE
ORDER BY
CASE
WHEN si.ends > :timeNow::timestamp
AND si.starts < :timeNow::timestamp THEN 1
WHEN si.starts > :timeNow::timestamp THEN 2
ELSE 3
END
LIMIT :lim
SQL;
$stmt = $con->prepare($sql);
$utcNowStr = $utcNow->format("Y-m-d H:i:s");
$stmt->bindValue(':timeNow', $utcNowStr);
$stmt->bindValue(':timeEnd', $utcEndStr);
$stmt->bindValue(':lim', $showsToRetrieve);
if ($stmt->execute()) {
// use PDO::FETCH_ASSOC to only get the associative values
// note that fetchAll() defaults to PDO::FETCH_BOTH, which we don't want
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
$msg = implode(',', $stmt->errorInfo());
throw new Exception("Error: $msg");
}
$numberOfRows = count($rows);
$results['previousShow'] = array();
$results['currentShow'] = null;
$results['nextShow'] = array();
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 (($showStartTime <= $utcNow) && ($showEndTime > $utcNow)) {
$results['currentShow'] = $rows[$i];
} else if ($showEndTime < $utcNow ) {
array_push($results['previousShow'], $rows[$i]);
} else if ($showStartTime > $utcNow) {
array_push($results['nextShow'], $rows[$i]);
}
}
return $results;
}
/**
* Gets the current show, previous and next with an 2day window from
* the given timeNow, so timeNow-2days and timeNow+2days.
*
* @param $utcNow A DateTime object containing the current time in UTC.
* @return An array (with stupid sub-arrays) containing the previous show id,
* current show id, and next show id.
*/
public static function getPrevCurrentNextOld($utcNow)
{
$timeZone = new DateTimeZone("UTC"); //This function works entirely in UTC.
assert(get_class($utcNow) === "DateTime");
assert($utcNow->getTimeZone() == $timeZone);
$CC_CONFIG = Config::getConfig();
$con = Propel::getConnection();
//TODO, returning starts + ends twice (once with an alias). Unify this after the 2.0 release. --Martin //TODO, returning starts + ends twice (once with an alias). Unify this after the 2.0 release. --Martin
$sql = <<<SQL $sql = <<<SQL
SELECT si.starts AS start_timestamp, SELECT si.starts AS start_timestamp,
si.ends AS end_timestamp, si.ends AS end_timestamp,
s.name, s.name,
s.description,
s.id, s.id,
si.id AS instance_id, si.id AS instance_id,
si.description AS instance_description,
si.record, si.record,
s.url, s.url,
s.image_path,
starts, starts,
ends ends
FROM cc_show_instances si FROM cc_show_instances si
@ -1150,7 +1242,7 @@ SQL;
$stmt->bindValue(':timeNow2', $utcNowStr); $stmt->bindValue(':timeNow2', $utcNowStr);
if ($stmt->execute()) { if ($stmt->execute()) {
$rows = $stmt->fetchAll(); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else { } else {
$msg = implode(',', $stmt->errorInfo()); $msg = implode(',', $stmt->errorInfo());
throw new Exception("Error: $msg"); throw new Exception("Error: $msg");
@ -1172,32 +1264,38 @@ SQL;
{ {
if ($i-1 >= 0) { if ($i-1 >= 0) {
$results['previousShow'][0] = array( $results['previousShow'][0] = array(
"id" => $rows[$i-1]['id'], "id" => $rows[$i-1]['id'],
"instance_id" => $rows[$i-1]['instance_id'], "instance_id" => $rows[$i-1]['instance_id'],
"name" => $rows[$i-1]['name'], "instance_description" => $rows[$i-1]['instance_description'],
"url" => $rows[$i-1]['url'], "name" => $rows[$i-1]['name'],
"start_timestamp" => $rows[$i-1]['start_timestamp'], "description" => $rows[$i-1]['description'],
"end_timestamp" => $rows[$i-1]['end_timestamp'], "url" => $rows[$i-1]['url'],
"starts" => $rows[$i-1]['starts'], "start_timestamp" => $rows[$i-1]['start_timestamp'],
"ends" => $rows[$i-1]['ends'], "end_timestamp" => $rows[$i-1]['end_timestamp'],
"record" => $rows[$i-1]['record'], "starts" => $rows[$i-1]['starts'],
"type" => "show"); "ends" => $rows[$i-1]['ends'],
"record" => $rows[$i-1]['record'],
"image_path" => $rows[$i-1]['image_path'],
"type" => "show");
} }
$results['currentShow'][0] = $rows[$i]; $results['currentShow'][0] = $rows[$i];
if (isset($rows[$i+1])) { if (isset($rows[$i+1])) {
$results['nextShow'][0] = array( $results['nextShow'][0] = array(
"id" => $rows[$i+1]['id'], "id" => $rows[$i+1]['id'],
"instance_id" => $rows[$i+1]['instance_id'], "instance_id" => $rows[$i+1]['instance_id'],
"name" => $rows[$i+1]['name'], "instance_description" => $rows[$i+1]['instance_description'],
"url" => $rows[$i+1]['url'], "name" => $rows[$i+1]['name'],
"start_timestamp" => $rows[$i+1]['start_timestamp'], "description" => $rows[$i+1]['description'],
"end_timestamp" => $rows[$i+1]['end_timestamp'], "url" => $rows[$i+1]['url'],
"starts" => $rows[$i+1]['starts'], "start_timestamp" => $rows[$i+1]['start_timestamp'],
"ends" => $rows[$i+1]['ends'], "end_timestamp" => $rows[$i+1]['end_timestamp'],
"record" => $rows[$i+1]['record'], "starts" => $rows[$i+1]['starts'],
"type" => "show"); "ends" => $rows[$i+1]['ends'],
"record" => $rows[$i+1]['record'],
"image_path" => $rows[$i+1]['image_path'],
"type" => "show");
} }
break; break;
} }
@ -1208,32 +1306,38 @@ SQL;
//if we hit this we know we've gone to far and can stop looping. //if we hit this we know we've gone to far and can stop looping.
if ($showStartTime > $utcNow) { if ($showStartTime > $utcNow) {
$results['nextShow'][0] = array( $results['nextShow'][0] = array(
"id" => $rows[$i]['id'], "id" => $rows[$i]['id'],
"instance_id" => $rows[$i]['instance_id'], "instance_id" => $rows[$i]['instance_id'],
"name" => $rows[$i]['name'], "instance_description" => $rows[$i]['instance_description'],
"url" => $rows[$i]['url'], "name" => $rows[$i]['name'],
"start_timestamp" => $rows[$i]['start_timestamp'], "description" => $rows[$i]['description'],
"end_timestamp" => $rows[$i]['end_timestamp'], "url" => $rows[$i]['url'],
"starts" => $rows[$i]['starts'], "start_timestamp" => $rows[$i]['start_timestamp'],
"ends" => $rows[$i]['ends'], "end_timestamp" => $rows[$i]['end_timestamp'],
"record" => $rows[$i]['record'], "starts" => $rows[$i]['starts'],
"type" => "show"); "ends" => $rows[$i]['ends'],
"record" => $rows[$i]['record'],
"image_path" => $rows[$i]['image_path'],
"type" => "show");
break; break;
} }
} }
//If we didn't find a a current show because the time didn't fit we may still have //If we didn't find a a current show because the time didn't fit we may still have
//found a previous show so use it. //found a previous show so use it.
if (count($results['previousShow']) == 0 && isset($previousShowIndex)) { if (count($results['previousShow']) == 0 && isset($previousShowIndex)) {
$results['previousShow'][0] = array( $results['previousShow'][0] = array(
"id" => $rows[$previousShowIndex]['id'], "id" => $rows[$previousShowIndex]['id'],
"instance_id" => $rows[$previousShowIndex]['instance_id'], "instance_id" => $rows[$previousShowIndex]['instance_id'],
"name" => $rows[$previousShowIndex]['name'], "instance_description" => $rows[$previousShowIndex]['instance_description'],
"start_timestamp" => $rows[$previousShowIndex]['start_timestamp'], "name" => $rows[$previousShowIndex]['name'],
"end_timestamp" => $rows[$previousShowIndex]['end_timestamp'], "description" => $rows[$previousShowIndex]['description'],
"starts" => $rows[$previousShowIndex]['starts'], "start_timestamp" => $rows[$previousShowIndex]['start_timestamp'],
"ends" => $rows[$previousShowIndex]['ends'], "end_timestamp" => $rows[$previousShowIndex]['end_timestamp'],
"record" => $rows[$previousShowIndex]['record'], "starts" => $rows[$previousShowIndex]['starts'],
"type" => "show"); "ends" => $rows[$previousShowIndex]['ends'],
"record" => $rows[$previousShowIndex]['record'],
"image_path" => $rows[$previousShowIndex]['image_path'],
"type" => "show");
} }
return $results; return $results;