Rename airtime_mvc/ to legacy/
This commit is contained in:
parent
f0879322c2
commit
3e18d42c8b
1316 changed files with 0 additions and 0 deletions
109
legacy/application/common/AutoPlaylistManager.php
Normal file
109
legacy/application/common/AutoPlaylistManager.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
class AutoPlaylistManager {
|
||||
/**
|
||||
* @var int how often, in seconds, to check for and ingest new podcast episodes
|
||||
*/
|
||||
private static $_AUTOPLAYLIST_POLL_INTERVAL_SECONDS = 60; // 10 minutes
|
||||
|
||||
/**
|
||||
* Check whether $_AUTOPLAYLIST_POLL_INTERVAL_SECONDS have passed since the last call to
|
||||
* buildAutoPlaylist
|
||||
*
|
||||
* @return bool true if $_AUTOPLAYLIST_POLL_INTERVAL_SECONDS has passed since the last check
|
||||
*/
|
||||
public static function hasAutoPlaylistPollIntervalPassed() {
|
||||
$lastPolled = Application_Model_Preference::getAutoPlaylistPollLock();
|
||||
return empty($lastPolled) || (microtime(true) > $lastPolled + self::$_AUTOPLAYLIST_POLL_INTERVAL_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all shows with autoplaylists who have yet to have their playlists built and added to the schedule
|
||||
*
|
||||
*/
|
||||
public static function buildAutoPlaylist() {
|
||||
$autoPlaylists = static::_upcomingAutoPlaylistShows();
|
||||
foreach ($autoPlaylists as $autoplaylist) {
|
||||
// creates a ShowInstance object to build the playlist in from the ShowInstancesQuery Object
|
||||
$si = new Application_Model_ShowInstance($autoplaylist->getDbId());
|
||||
$playlistid = $si->GetAutoPlaylistId();
|
||||
// call the addPlaylist to show function and don't check for user permission to avoid call to non-existant user object
|
||||
$sid = $si->getShowId();
|
||||
$playlistrepeat = new Application_Model_Show($sid);
|
||||
$introplaylistid = Application_Model_Preference::GetIntroPlaylist();
|
||||
$outroplaylistid = Application_Model_Preference::GetOutroPlaylist();
|
||||
|
||||
// we want to check and see if we need to repeat this process until the show is 100% scheduled
|
||||
// so we create a while loop and break it immediately if repeat until full isn't enabled
|
||||
// otherwise we continue to go through adding playlists, including the intro and outro if enabled
|
||||
$full = false;
|
||||
$repeatuntilfull = $playlistrepeat->getAutoPlaylistRepeat();
|
||||
$tempPercentScheduled = 0;
|
||||
$si = new Application_Model_ShowInstance($autoplaylist->getDbId());
|
||||
// the intro playlist should be added exactly once
|
||||
if ($introplaylistid != null) {
|
||||
//Logging::info('adding intro');
|
||||
$si->addPlaylistToShowStart($introplaylistid, false);
|
||||
}
|
||||
while(!$full) {
|
||||
// we do not want to try to schedule an empty playlist
|
||||
if ($playlistid != null) {
|
||||
$si->addPlaylistToShow($playlistid, false);
|
||||
}
|
||||
$ps = $si->getPercentScheduled();
|
||||
if ($ps > 100) {
|
||||
$full = true;
|
||||
}
|
||||
elseif (!$repeatuntilfull) {
|
||||
break;
|
||||
}
|
||||
// we want to avoid an infinite loop if all of the playlists are null
|
||||
if ($playlistid == null) {
|
||||
break;
|
||||
}
|
||||
// another possible issue would be if the show isn't increasing in length each loop
|
||||
// ie if all of the playlists being added are zero lengths this could cause an infinite loop
|
||||
if ($tempPercentScheduled == $ps) {
|
||||
break;
|
||||
}
|
||||
//now reset it to the current percent scheduled
|
||||
$tempPercentScheduled = $ps;
|
||||
}
|
||||
// the outroplaylist is added at the end, it will always overbook
|
||||
// shows that have repeat until full enabled because they will
|
||||
// never have time remaining for the outroplaylist to be added
|
||||
// this is done outside the content loop to avoid a scenario
|
||||
// where a time remaining smartblock in a outro playlist
|
||||
// prevents the repeat until full from functioning by filling up the show
|
||||
if ($outroplaylistid != null) {
|
||||
$si->addPlaylistToShow($outroplaylistid, false);
|
||||
}
|
||||
$si->setAutoPlaylistBuilt(true);
|
||||
}
|
||||
Application_Model_Preference::setAutoPlaylistPollLock(microtime(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all show instances starting in the next hour with autoplaylists not yet added to the schedule
|
||||
*
|
||||
* @return PropelObjectCollection collection of ShowInstance objects
|
||||
* that have unbuilt autoplaylists
|
||||
*/
|
||||
protected static function _upcomingAutoPlaylistShows() {
|
||||
//setting now so that past shows aren't referenced
|
||||
$now = new DateTime("now", new DateTimeZone("UTC"));
|
||||
// only build playlists for shows that start up to an hour from now
|
||||
$future = clone $now;
|
||||
$future->add(new DateInterval('PT1H'));
|
||||
|
||||
return CcShowInstancesQuery::create()
|
||||
->filterByDbModifiedInstance(false)
|
||||
->filterByDbStarts($now,Criteria::GREATER_THAN)
|
||||
->filterByDbStarts($future,Criteria::LESS_THAN)
|
||||
->useCcShowQuery('a', 'left join')
|
||||
->filterByDbHasAutoPlaylist(true)
|
||||
->endUse()
|
||||
->filterByDbAutoPlaylistBuilt(false)
|
||||
->find();
|
||||
}
|
||||
}
|
59
legacy/application/common/CORSHelper.php
Normal file
59
legacy/application/common/CORSHelper.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
|
||||
class CORSHelper
|
||||
{
|
||||
public static function enableCrossOriginRequests(&$request, &$response)
|
||||
{
|
||||
//Chrome sends the Origin header for all requests, so we whitelist the webserver's hostname as well.
|
||||
$origin = $request->getHeader('Origin');
|
||||
$allowedOrigins = self::getAllowedOrigins($request);
|
||||
|
||||
if ((!(preg_match("/https?:\/\/localhost/", $origin) === 1)) && ($origin != "") &&
|
||||
(!in_array($origin, $allowedOrigins))
|
||||
) {
|
||||
//Don't allow CORS from other domains to prevent XSS.
|
||||
Logging::error("request origin '{$origin}' is not in allowed '" . implode(', ', $allowedOrigins) . "'!");
|
||||
throw new Zend_Controller_Action_Exception('Forbidden', 403);
|
||||
}
|
||||
//Allow AJAX requests from configured websites. We use this to allow other pages to use LibreTimes API.
|
||||
if ($origin) {
|
||||
$response = $response->setHeader('Access-Control-Allow-Origin', $origin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all allowed origins
|
||||
*
|
||||
* @param Request $request request object
|
||||
*/
|
||||
public static function getAllowedOrigins($request)
|
||||
{
|
||||
$allowedCorsUrls = array_map(
|
||||
function($v) { return trim($v); },
|
||||
explode(PHP_EOL, Application_Model_Preference::GetAllowedCorsUrls())
|
||||
);
|
||||
|
||||
// always allow the configured server in (as reported by the server and not what is i baseUrl)
|
||||
$scheme = $request->getServer('REQUEST_SCHEME');
|
||||
$host = $request->getServer('SERVER_NAME');
|
||||
$port = $request->getServer('SERVER_PORT');
|
||||
|
||||
$portString = '';
|
||||
if (
|
||||
$scheme == 'https' && $port != 443 ||
|
||||
$scheme == 'http' && $port != 80
|
||||
) {
|
||||
$portString = sprintf(':%s', $port);
|
||||
}
|
||||
$requestedUrl = sprintf(
|
||||
'%s://%s%s',
|
||||
$scheme,
|
||||
$host,
|
||||
$portString
|
||||
);
|
||||
return array_merge($allowedCorsUrls, array(
|
||||
$requestedUrl
|
||||
));
|
||||
}
|
||||
}
|
211
legacy/application/common/CeleryManager.php
Normal file
211
legacy/application/common/CeleryManager.php
Normal file
|
@ -0,0 +1,211 @@
|
|||
<?php
|
||||
|
||||
class CeleryManager {
|
||||
|
||||
/**
|
||||
* @var int milliseconds (for compatibility with celery) until we consider a message to have timed out
|
||||
*/
|
||||
private static $_CELERY_MESSAGE_TIMEOUT = 900000; // 15 minutes
|
||||
|
||||
/**
|
||||
* We have to use celeryresults (the default results exchange) because php-celery
|
||||
* doesn't support named results exchanges.
|
||||
*
|
||||
* @var string exchange for celery task results
|
||||
*/
|
||||
private static $_CELERY_RESULTS_EXCHANGE = 'celeryresults';
|
||||
|
||||
/**
|
||||
* @var PropelCollection cache of any pending CeleryTasks results for a service or task
|
||||
*/
|
||||
private static $_pendingTasks;
|
||||
|
||||
/**
|
||||
* Connect to the Celery daemon via amqp
|
||||
*
|
||||
* @param $config array the airtime configuration array
|
||||
* @param $exchange string the amqp exchange name
|
||||
* @param $queue string the amqp queue name
|
||||
*
|
||||
* @return Celery the Celery connection object
|
||||
*
|
||||
* @throws Exception when a connection error occurs
|
||||
*/
|
||||
private static function _setupCeleryExchange($config, $exchange, $queue) {
|
||||
return new Celery($config["rabbitmq"]["host"],
|
||||
$config["rabbitmq"]["user"],
|
||||
$config["rabbitmq"]["password"],
|
||||
$config["rabbitmq"]["vhost"],
|
||||
$exchange, // Exchange name
|
||||
$queue, // Binding/queue
|
||||
$config["rabbitmq"]["port"],
|
||||
false,
|
||||
true, // Persistent messages
|
||||
self::$_CELERY_MESSAGE_TIMEOUT); // Result expiration
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an amqp message to Celery the airtime-celery daemon to perform a task
|
||||
*
|
||||
* @param $task string the Celery task name
|
||||
* @param $exchange string the amqp exchange name
|
||||
* @param $data array an associative array containing arguments for the Celery task
|
||||
*
|
||||
* @return string the task identifier for the started Celery task so we can fetch the
|
||||
* results asynchronously later
|
||||
*/
|
||||
public static function sendCeleryMessage($task, $exchange, $data) {
|
||||
$config = Config::getConfig();
|
||||
$queue = $routingKey = $exchange;
|
||||
$c = self::_setupCeleryExchange($config, $exchange, $queue); // Use the exchange name for the queue
|
||||
$result = $c->PostTask($task, $data, true, $routingKey); // and routing key
|
||||
return $result->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a task name and identifier, check the Celery results queue for any
|
||||
* corresponding messages
|
||||
*
|
||||
* @param $task CeleryTasks the Celery task object
|
||||
*
|
||||
* @return array the message response array
|
||||
*
|
||||
* @throws CeleryException when no message is found
|
||||
* @throws CeleryTimeoutException when no message is found and more than
|
||||
* $_CELERY_MESSAGE_TIMEOUT milliseconds have passed
|
||||
*/
|
||||
private static function getAsyncResultMessage($task) {
|
||||
$config = Config::getConfig();
|
||||
$queue = self::$_CELERY_RESULTS_EXCHANGE . "." . $task;
|
||||
$c = self::_setupCeleryExchange($config, self::$_CELERY_RESULTS_EXCHANGE, $queue);
|
||||
$message = $c->getAsyncResultMessage($task->getDbName(), $task->getDbTaskId());
|
||||
|
||||
// If the message isn't ready yet (Celery hasn't finished the task), throw an exception.
|
||||
if ($message == FALSE) {
|
||||
if (static::_checkMessageTimeout($task)) {
|
||||
// If the task times out, mark it as failed. We don't want to remove the
|
||||
// track reference here in case it was a deletion that failed, for example.
|
||||
$task->setDbStatus(CELERY_FAILED_STATUS)->save();
|
||||
throw new CeleryTimeoutException("Celery task " . $task->getDbName()
|
||||
. " with ID " . $task->getDbTaskId() . " timed out");
|
||||
} else {
|
||||
// The message hasn't timed out, but it's still false, which means it hasn't been
|
||||
// sent back from Celery yet.
|
||||
throw new CeleryException("Waiting on Celery task " . $task->getDbName()
|
||||
. " with ID " . $task->getDbTaskId());
|
||||
}
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if there are any pending tasks for this service
|
||||
*
|
||||
* @param string $taskName the name of the task to poll for
|
||||
* @param string $serviceName the name of the service to poll for
|
||||
*
|
||||
* @return bool true if there are any pending tasks, otherwise false
|
||||
*/
|
||||
public static function isBrokerTaskQueueEmpty($taskName = "", $serviceName = "") {
|
||||
self::$_pendingTasks = static::_getPendingTasks($taskName, $serviceName);
|
||||
return empty(self::$_pendingTasks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the message queue for this service to see if any tasks with the given name have completed
|
||||
*
|
||||
* If we find any completed tasks, adjust the ThirdPartyTrackReferences table accordingly
|
||||
*
|
||||
* If no task name is passed, we poll all tasks for this service
|
||||
*
|
||||
* @param string $taskName the name of the task to poll for
|
||||
* @param string $serviceName the name of the service to poll for
|
||||
*/
|
||||
public static function pollBrokerTaskQueue($taskName = "", $serviceName = "") {
|
||||
$pendingTasks = empty(self::$_pendingTasks) ? static::_getPendingTasks($taskName, $serviceName)
|
||||
: self::$_pendingTasks;
|
||||
foreach ($pendingTasks as $task) {
|
||||
try {
|
||||
$message = static::_getTaskMessage($task);
|
||||
static::_processTaskMessage($task, $message);
|
||||
} catch (CeleryTimeoutException $e) {
|
||||
Logging::warn($e->getMessage());
|
||||
} catch (CeleryException $e) {
|
||||
// Don't log these - they end up clogging up the logs
|
||||
} catch (Exception $e) {
|
||||
// Because $message->result can be either an object or a string, sometimes
|
||||
// we get a json_decode error and end up here
|
||||
Logging::info($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a collection of all pending CeleryTasks for this service or task
|
||||
*
|
||||
* @param string $taskName the name of the task to find
|
||||
* @param string $serviceName the name of the service to find
|
||||
*
|
||||
* @return PropelCollection any pending CeleryTasks results for this service
|
||||
* or task if taskName is provided
|
||||
*/
|
||||
protected static function _getPendingTasks($taskName, $serviceName) {
|
||||
$query = CeleryTasksQuery::create()
|
||||
->filterByDbStatus(CELERY_PENDING_STATUS)
|
||||
->filterByDbTaskId('', Criteria::NOT_EQUAL);
|
||||
if (!empty($taskName)) {
|
||||
$query->filterByDbName($taskName);
|
||||
}
|
||||
if (!empty($serviceName)) {
|
||||
$query->useThirdPartyTrackReferencesQuery()
|
||||
->filterByDbService($serviceName)->endUse();
|
||||
}
|
||||
return $query->joinThirdPartyTrackReferences()
|
||||
->with('ThirdPartyTrackReferences')->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Celery task message from the results queue
|
||||
*
|
||||
* @param $task CeleryTasks the Celery task object
|
||||
*
|
||||
* @return object the task message object
|
||||
*
|
||||
* @throws CeleryException when the result message for this task is still pending
|
||||
* @throws CeleryTimeoutException when the result message for this task no longer exists
|
||||
*/
|
||||
protected static function _getTaskMessage($task) {
|
||||
$message = self::getAsyncResultMessage($task);
|
||||
return json_decode($message['body']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a message from the results queue
|
||||
*
|
||||
* @param $task CeleryTasks Celery task object
|
||||
* @param $message mixed async message object from php-celery
|
||||
*/
|
||||
protected static function _processTaskMessage($task, $message) {
|
||||
$ref = $task->getThirdPartyTrackReferences(); // ThirdPartyTrackReferences join
|
||||
$service = CeleryServiceFactory::getService($ref->getDbService());
|
||||
$service->updateTrackReference($task, $ref->getDbId(), json_decode($message->result), $message->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a task message has been unreachable for more our timeout time
|
||||
*
|
||||
* @param $task CeleryTasks the Celery task object
|
||||
*
|
||||
* @return bool true if the dispatch time is empty or it's been more than our timeout time
|
||||
* since the message was dispatched, otherwise false
|
||||
*/
|
||||
protected static function _checkMessageTimeout($task) {
|
||||
$utc = new DateTimeZone("UTC");
|
||||
$dispatchTime = new DateTime($task->getDbDispatchTime(), $utc);
|
||||
$now = new DateTime("now", $utc);
|
||||
$timeoutSeconds = self::$_CELERY_MESSAGE_TIMEOUT / 1000; // Convert from milliseconds
|
||||
$timeoutInterval = new DateInterval("PT" . $timeoutSeconds . "S");
|
||||
return (empty($dispatchTime) || $dispatchTime->add($timeoutInterval) <= $now);
|
||||
}
|
||||
|
||||
}
|
70
legacy/application/common/Database.php
Normal file
70
legacy/application/common/Database.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
class Application_Common_Database
|
||||
{
|
||||
const SINGLE = 'single';
|
||||
const COLUMN = 'column';
|
||||
const ALL = 'all';
|
||||
const EXECUTE = 'execute';
|
||||
const ROW_COUNT = 'row_count';
|
||||
|
||||
public static function prepareAndExecute($sql,
|
||||
array $paramValueMap = array(),
|
||||
$type=self::ALL,
|
||||
$fetchType=PDO::FETCH_ASSOC,
|
||||
$con=null)
|
||||
{
|
||||
if (is_null($con)) {
|
||||
$con = Propel::getConnection();
|
||||
}
|
||||
$stmt = $con->prepare($sql);
|
||||
foreach ($paramValueMap as $param => $v) {
|
||||
$stmt->bindValue($param, $v);
|
||||
}
|
||||
$rows = array();
|
||||
if ($stmt->execute()) {
|
||||
if ($type == self::SINGLE) {
|
||||
$rows = $stmt->fetch($fetchType);
|
||||
} else if ($type == self::COLUMN){
|
||||
$rows = $stmt->fetchColumn();
|
||||
} else if ($type == self::ALL) {
|
||||
$rows = $stmt->fetchAll($fetchType);
|
||||
} else if ($type == self::EXECUTE) {
|
||||
$rows = null;
|
||||
} else if ($type == self::ROW_COUNT) {
|
||||
$rows = $stmt->rowCount();
|
||||
} else {
|
||||
$msg = "bad type passed: type($type)";
|
||||
throw new Exception("Error: $msg");
|
||||
}
|
||||
} else {
|
||||
$msg = implode(',', $stmt->errorInfo());
|
||||
throw new Exception("Error: $msg");
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
/*
|
||||
Wrapper around prepareAndExecute that allows you to use multipe :xx's
|
||||
in one query. Transforms $sql to :xx1, :xx2, ....
|
||||
*/
|
||||
public static function smartPrepareAndExecute($sql, array $params,
|
||||
$type='all', $fetchType=PDO::FETCH_ASSOC)
|
||||
{
|
||||
$new_params = array();
|
||||
$new_sql = $sql;
|
||||
foreach ($params as $k => $v) {
|
||||
$matches_count = substr_count($sql, $k);
|
||||
if ($matches_count == 0) {
|
||||
throw new Exception("Argument $k is not inside $sql");
|
||||
} elseif ($matches_count == 1) {
|
||||
$new_params[$k] = $new_params[$v];
|
||||
} else {
|
||||
foreach ( range(1,$matches_count) as $i ) {
|
||||
preg_replace( "/$k(\D)/", "$k$i${1}", $sql, 1);
|
||||
$new_params[ $k.$i ] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Application_Common_Database::prepareAndExecute( $new_sql,
|
||||
$new_params, $type, $fetchType);
|
||||
}
|
||||
}
|
501
legacy/application/common/DateHelper.php
Normal file
501
legacy/application/common/DateHelper.php
Normal file
|
@ -0,0 +1,501 @@
|
|||
<?php
|
||||
|
||||
class Application_Common_DateHelper
|
||||
{
|
||||
private $_dateTime;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->_dateTime = date("U");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time of object construction in the format
|
||||
* YYYY-MM-DD HH:mm:ss
|
||||
*/
|
||||
function getTimestamp()
|
||||
{
|
||||
return date(DEFAULT_TIMESTAMP_FORMAT, $this->_dateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time of object construction in the format
|
||||
* YYYY-MM-DD HH:mm:ss
|
||||
*/
|
||||
function getUtcTimestamp()
|
||||
{
|
||||
return gmdate(DEFAULT_TIMESTAMP_FORMAT, $this->_dateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date of object construction in the format
|
||||
* YYYY-MM-DD
|
||||
*/
|
||||
function getDate()
|
||||
{
|
||||
return gmdate("Y-m-d", $this->_dateTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time of object construction in the format
|
||||
* HH:mm:ss
|
||||
*/
|
||||
function getTime()
|
||||
{
|
||||
return gmdate("H:i:s", $this->_dateTime);
|
||||
}
|
||||
|
||||
/** 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.)
|
||||
*/
|
||||
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.
|
||||
$day = $now->format('w');
|
||||
if ($day == 0) {
|
||||
$day = 7;
|
||||
}
|
||||
|
||||
$dayDiff = $day - 1;
|
||||
if ($dayDiff > 0) {
|
||||
$now->sub(new DateInterval("P{$dayDiff}D"));
|
||||
}
|
||||
|
||||
return $now;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function formats a time by removing seconds
|
||||
*
|
||||
* When we receive a time from the database we get the
|
||||
* format "hh:mm:ss". But when dealing with show times, we
|
||||
* do not care about the seconds.
|
||||
*
|
||||
* @param int $p_dateTime
|
||||
* The value which to format.
|
||||
* @return int
|
||||
* The timestamp with the new format "hh:mm", or
|
||||
* the original input parameter, if it does not have
|
||||
* the correct format.
|
||||
*/
|
||||
public static function removeSecondsFromTime($p_dateTime)
|
||||
{
|
||||
//Format is in hh:mm:ss. We want hh:mm
|
||||
$timeExplode = explode(":", $p_dateTime);
|
||||
|
||||
if (count($timeExplode) == 3)
|
||||
return $timeExplode[0].":".$timeExplode[1];
|
||||
else
|
||||
return $p_dateTime;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
* For example, 00:06:31.444, should be converted to 391.444 seconds
|
||||
* @param int $p_time
|
||||
* The time interval in format HH:MM:SS.mm we wish to
|
||||
* convert to seconds.
|
||||
* @return float
|
||||
* The input parameter converted to seconds.
|
||||
*/
|
||||
public static function calculateLengthInSeconds($p_time){
|
||||
|
||||
if (2 !== substr_count($p_time, ":")){
|
||||
return false;
|
||||
}
|
||||
|
||||
if (1 === substr_count($p_time, ".")){
|
||||
list($hhmmss, $ms) = explode(".", $p_time);
|
||||
} else {
|
||||
$hhmmss = $p_time;
|
||||
$ms = 0;
|
||||
}
|
||||
|
||||
list($hours, $minutes, $seconds) = explode(":", $hhmmss);
|
||||
|
||||
$totalSeconds = ($hours*3600 + $minutes*60 + $seconds).".$ms";
|
||||
return round($totalSeconds, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true or false depending on input is wether in
|
||||
* valid range of SQL date/time
|
||||
* @param string $p_datetime
|
||||
* should be in format of '0000-00-00 00:00:00'
|
||||
*/
|
||||
public static function checkDateTimeRangeForSQL($p_datetime){
|
||||
$info = explode(' ', $p_datetime);
|
||||
$dateInfo = explode('-', $info[0]);
|
||||
if (isset($info[1])) {
|
||||
$timeInfo = explode(':', $info[1]);
|
||||
}
|
||||
$retVal = array();
|
||||
$retVal["success"] = true;
|
||||
|
||||
$year = $dateInfo[0];
|
||||
$month = $dateInfo[1];
|
||||
$day = $dateInfo[2];
|
||||
// if year is < 1753 or > 9999 it's out of range
|
||||
if ($year < 1753) {
|
||||
$retVal['success'] = false;
|
||||
$retVal['errMsg'] = sprintf(_("The year %s must be within the range of 1753 - 9999"), $year);
|
||||
} else if (!checkdate($month, $day, $year)) {
|
||||
$retVal['success'] = false;
|
||||
$retVal['errMsg'] = sprintf(_("%s-%s-%s is not a valid date"), $year, $month, $day);
|
||||
} else {
|
||||
// check time
|
||||
if (isset($timeInfo)) {
|
||||
if (isset($timeInfo[0]) && $timeInfo[0] != "") {
|
||||
$hour = intval($timeInfo[0]);
|
||||
} else {
|
||||
$hour = -1;
|
||||
}
|
||||
|
||||
if (isset($timeInfo[1]) && $timeInfo[1] != "") {
|
||||
$min = intval($timeInfo[1]);
|
||||
} else {
|
||||
$min = -1;
|
||||
}
|
||||
|
||||
if (isset($timeInfo[2]) && $timeInfo[2] != "") {
|
||||
$sec = intval($timeInfo[2]);
|
||||
} else {
|
||||
$sec = -1;
|
||||
}
|
||||
|
||||
if ( ($hour < 0 || $hour > 23) || ($min < 0 || $min > 59) || ($sec < 0 || $sec > 59) ) {
|
||||
$retVal['success'] = false;
|
||||
$retVal['errMsg'] = sprintf(_("%s:%s:%s is not a valid time"), $timeInfo[0], $timeInfo[1] ,$timeInfo[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
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=DEFAULT_TIMESTAMP_FORMAT) {
|
||||
$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=DEFAULT_TIMESTAMP_FORMAT) {
|
||||
$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=DEFAULT_TIMESTAMP_FORMAT) {
|
||||
$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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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=DEFAULT_TIMESTAMP_FORMAT)
|
||||
{
|
||||
$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=DEFAULT_TIMESTAMP_FORMAT) {
|
||||
$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!
|
||||
*
|
||||
* Convert playlist time value to float seconds
|
||||
*
|
||||
* @param string $plt
|
||||
* playlist interval value (HH:mm:ss.dddddd)
|
||||
* @return int
|
||||
* seconds
|
||||
*/
|
||||
public static function playlistTimeToSeconds($plt)
|
||||
{
|
||||
$arr = preg_split('/:/', $plt);
|
||||
if (isset($arr[2])) {
|
||||
return (intval($arr[0])*60 + intval($arr[1]))*60 + floatval($arr[2]);
|
||||
}
|
||||
if (isset($arr[1])) {
|
||||
return intval($arr[0])*60 + floatval($arr[1]);
|
||||
}
|
||||
|
||||
return floatval($arr[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function is used for calculations! Don't modify for display purposes!
|
||||
*
|
||||
* Convert float seconds value to playlist time format
|
||||
*
|
||||
* @param float $seconds
|
||||
* @return string
|
||||
* interval in playlist time format (HH:mm:ss.d)
|
||||
*/
|
||||
public static function secondsToPlaylistTime($p_seconds)
|
||||
{
|
||||
$info = explode('.', $p_seconds);
|
||||
$seconds = $info[0];
|
||||
if (!isset($info[1])) {
|
||||
$milliStr = 0;
|
||||
} else {
|
||||
$milliStr = $info[1];
|
||||
}
|
||||
$hours = floor($seconds / 3600);
|
||||
$seconds -= $hours * 3600;
|
||||
$minutes = floor($seconds / 60);
|
||||
$seconds -= $minutes * 60;
|
||||
|
||||
$res = sprintf("%02d:%02d:%02d.%s", $hours, $minutes, $seconds, $milliStr);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns date fields from give start and end teimstamp strings
|
||||
* if no start or end parameter is passed start will be set to 1
|
||||
* in the past and end to now
|
||||
*
|
||||
* @param string startTimestamp Y-m-d H:i:s
|
||||
* @param string endTImestamp Y-m-d H:i:s
|
||||
* @param string timezone (ex UTC) of the start and end parameters
|
||||
* @return array (start DateTime, end DateTime) in UTC timezone
|
||||
*/
|
||||
public static function getStartEnd($startTimestamp, $endTimestamp, $timezone)
|
||||
{
|
||||
$prefTimezone = Application_Model_Preference::GetTimezone();
|
||||
$utcTimezone = new DateTimeZone("UTC");
|
||||
$utcNow = new DateTime("now", $utcTimezone);
|
||||
|
||||
if (empty($timezone)) {
|
||||
$userTimezone = new DateTimeZone($prefTimezone);
|
||||
} else {
|
||||
$userTimezone = new DateTimeZone($timezone);
|
||||
}
|
||||
|
||||
// default to 1 day
|
||||
if (empty($startTimestamp) || empty($endTimestamp)) {
|
||||
$startsDT = clone $utcNow;
|
||||
$startsDT->sub(new DateInterval("P1D"));
|
||||
$endsDT = clone $utcNow;
|
||||
} else {
|
||||
|
||||
try {
|
||||
$startsDT = new DateTime($startTimestamp, $userTimezone);
|
||||
$startsDT->setTimezone($utcTimezone);
|
||||
|
||||
$endsDT = new DateTime($endTimestamp, $userTimezone);
|
||||
$endsDT->setTimezone($utcTimezone);
|
||||
|
||||
if ($startsDT > $endsDT) {
|
||||
throw new Exception("start greater than end");
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
Logging::info($e);
|
||||
Logging::info($e->getMessage());
|
||||
|
||||
$startsDT = clone $utcNow;
|
||||
$startsDT->sub(new DateInterval("P1D"));
|
||||
$endsDT = clone $utcNow;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array($startsDT, $endsDT);
|
||||
}
|
||||
}
|
||||
|
452
legacy/application/common/FileDataHelper.php
Normal file
452
legacy/application/common/FileDataHelper.php
Normal file
|
@ -0,0 +1,452 @@
|
|||
<?php
|
||||
|
||||
class FileDataHelper {
|
||||
|
||||
public static function getAudioMimeTypeArray() {
|
||||
return array(
|
||||
"audio/ogg" => "ogg",
|
||||
"application/ogg" => "ogg",
|
||||
"audio/vorbis" => "ogg",
|
||||
"audio/mp3" => "mp3",
|
||||
"audio/mpeg" => "mp3",
|
||||
"audio/mpeg3" => "mp3",
|
||||
"audio/x-aac" => "aac",
|
||||
"audio/aac" => "aac",
|
||||
"audio/aacp" => "aac",
|
||||
"audio/mp4" => "m4a",
|
||||
"video/mp4" => "mp4",
|
||||
"audio/x-flac" => "flac",
|
||||
"audio/flac" => "flac",
|
||||
"audio/wav" => "wav",
|
||||
"audio/x-wav" => "wav",
|
||||
"audio/mp2" => "mp2",
|
||||
"audio/mp1" => "mp1",
|
||||
"audio/x-ms-wma" => "wma",
|
||||
"audio/basic" => "au",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to throw out invalid data and process the upload successfully
|
||||
* at all costs, so check the data and sanitize it if necessary
|
||||
* @param array $data array containing new file metadata
|
||||
*/
|
||||
public static function sanitizeData(&$data)
|
||||
{
|
||||
if (array_key_exists("track_number", $data)) {
|
||||
// If the track number isn't numeric, this will return 0
|
||||
$data["track_number"] = intval($data["track_number"]);
|
||||
}
|
||||
if (array_key_exists("year", $data)) {
|
||||
// If the track number isn't numeric, this will return 0
|
||||
$data["year"] = intval($data["year"]);
|
||||
}
|
||||
if (array_key_exists("bpm", $data)) {
|
||||
//Some BPM tags are silly and include the word "BPM". Let's strip that...
|
||||
$data["bpm"] = str_ireplace("BPM", "", $data["bpm"]);
|
||||
// This will convert floats to ints too.
|
||||
$data["bpm"] = intval($data["bpm"]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a suitable extension for the given file
|
||||
*
|
||||
* @param string $mime
|
||||
*
|
||||
* @return string file extension with(!) a dot (for convenience)
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getFileExtensionFromMime($mime)
|
||||
{
|
||||
$mime = trim(strtolower($mime));
|
||||
try {
|
||||
return ('.' . static::getAudioMimeTypeArray()[$mime]);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("Unknown file type: $mime");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data URI from artwork file
|
||||
*
|
||||
* @param string $file
|
||||
* @param int $size
|
||||
* @param string $filepath
|
||||
*
|
||||
* @return string Data URI for artwork
|
||||
*/
|
||||
public static function getArtworkData($file, $size, $filepath = false)
|
||||
{
|
||||
$baseUrl = Application_Common_HTTPHelper::getStationUrl();
|
||||
$default = $baseUrl . "css/images/no-cover.jpg";
|
||||
|
||||
if ($filepath != false) {
|
||||
$path = $filepath . $file . "-" . $size;
|
||||
if (!file_exists($path)) {
|
||||
$get_file_content = $default;
|
||||
} else {
|
||||
$get_file_content = file_get_contents($path);
|
||||
}
|
||||
} else {
|
||||
$storDir = Application_Model_MusicDir::getStorDir();
|
||||
$path = $storDir->getDirectory() . $file . "-" . $size;
|
||||
if (!file_exists($path)) {
|
||||
$get_file_content = $default;
|
||||
} else {
|
||||
$get_file_content = file_get_contents($path);
|
||||
}
|
||||
}
|
||||
return $get_file_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add artwork file
|
||||
*
|
||||
* @param string $analyzeFile
|
||||
* @param string $filename
|
||||
* @param string $importDir
|
||||
* @param string $DbPath
|
||||
*
|
||||
* @return string Path to artwork
|
||||
*/
|
||||
public static function saveArtworkData($analyzeFile, $filename, $importDir = null, $DbPath = null)
|
||||
{
|
||||
if (class_exists('getID3')) {
|
||||
$getID3 = new \getID3();
|
||||
$getFileInfo = $getID3->analyze($analyzeFile);
|
||||
} else {
|
||||
$getFileInfo = [];
|
||||
Logging::error("Failed to load getid3 library. Please upgrade Libretime.");
|
||||
}
|
||||
|
||||
if(isset($getFileInfo['comments']['picture'][0])) {
|
||||
|
||||
$get_img = "";
|
||||
$timestamp = time();
|
||||
$mime = $getFileInfo['comments']['picture'][0]['image_mime'];
|
||||
$Image = 'data:'.$mime.';charset=utf-8;base64,'.base64_encode($getFileInfo['comments']['picture'][0]['data']);
|
||||
$base64 = @$Image;
|
||||
|
||||
if (!file_exists($importDir . "/" . "artwork/")) {
|
||||
if (!mkdir($importDir . "/" . "artwork/", 0777, true)) {
|
||||
Logging::error("Failed to create artwork directory.");
|
||||
throw new Exception("Failed to create artwork directory.");
|
||||
}
|
||||
}
|
||||
|
||||
$path_parts = pathinfo($filename);
|
||||
$file = $importDir . "artwork/" . $path_parts['filename'];
|
||||
|
||||
//Save Data URI
|
||||
if (file_put_contents($file, $base64)) {
|
||||
$get_img = $DbPath . "artwork/". $path_parts['filename'];
|
||||
} else {
|
||||
Logging::error("Could not save Data URI");
|
||||
}
|
||||
|
||||
if ($mime == "image/png") {
|
||||
$ext = 'png';
|
||||
} elseif ($mime == "image/gif") {
|
||||
$ext = 'gif';
|
||||
} elseif ($mime == "image/bmp") {
|
||||
$ext = 'bmp';
|
||||
} else {
|
||||
$ext = 'jpg';
|
||||
}
|
||||
self::resizeGroup($file, $ext);
|
||||
|
||||
} else {
|
||||
$get_img = '';
|
||||
}
|
||||
return $get_img;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset artwork
|
||||
*
|
||||
* @param string $trackid
|
||||
*
|
||||
* @return string $get_img Path to artwork
|
||||
*/
|
||||
public static function resetArtwork($trackid)
|
||||
{
|
||||
$file = Application_Model_StoredFile::RecallById($trackid);
|
||||
$md = $file->getMetadata();
|
||||
|
||||
$storDir = Application_Model_MusicDir::getStorDir();
|
||||
$fp = $storDir->getDirectory();
|
||||
|
||||
$dbAudioPath = $md["MDATA_KEY_FILEPATH"];
|
||||
$fullpath = $fp . $dbAudioPath;
|
||||
|
||||
if (class_exists('getID3')) {
|
||||
$getID3 = new \getID3();
|
||||
$getFileInfo = $getID3->analyze($fullpath);
|
||||
} else {
|
||||
$getFileInfo = [];
|
||||
Logging::error("Failed to load getid3 library. Please upgrade Libretime.");
|
||||
}
|
||||
|
||||
if(isset($getFileInfo['comments']['picture'][0])) {
|
||||
|
||||
$get_img = "";
|
||||
$mime = $getFileInfo['comments']['picture'][0]['image_mime'];
|
||||
$Image = 'data:'.$getFileInfo['comments']['picture'][0]['image_mime'].';charset=utf-8;base64,'.base64_encode($getFileInfo['comments']['picture'][0]['data']);
|
||||
$base64 = @$Image;
|
||||
|
||||
$audioPath = dirname($fullpath);
|
||||
$dbPath = dirname($dbAudioPath);
|
||||
$path_parts = pathinfo($fullpath);
|
||||
$file = $path_parts['filename'];
|
||||
|
||||
//Save Data URI
|
||||
if (file_put_contents($audioPath . "/" . $file, $base64)) {
|
||||
$get_img = $dbPath . "/" . $file;
|
||||
} else {
|
||||
Logging::error("Could not save Data URI");
|
||||
}
|
||||
|
||||
$rfile = $audioPath . "/" . $file;
|
||||
|
||||
if ($mime == "image/png") {
|
||||
$ext = 'png';
|
||||
} elseif ($mime == "image/gif") {
|
||||
$ext = 'gif';
|
||||
} elseif ($mime == "image/bmp") {
|
||||
$ext = 'bmp';
|
||||
} else {
|
||||
$ext = 'jpg';
|
||||
}
|
||||
self::resizeGroup($rfile, $ext);
|
||||
|
||||
} else {
|
||||
$get_img = "";
|
||||
}
|
||||
return $get_img;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload artwork
|
||||
*
|
||||
* @param string $trackid
|
||||
* @param string $data
|
||||
*
|
||||
* @return string Path to artwork
|
||||
*/
|
||||
public static function setArtwork($trackid, $data)
|
||||
{
|
||||
$file = Application_Model_StoredFile::RecallById($trackid);
|
||||
$md = $file->getMetadata();
|
||||
|
||||
$storDir = Application_Model_MusicDir::getStorDir();
|
||||
$fp = $storDir->getDirectory();
|
||||
|
||||
$dbAudioPath = $md["MDATA_KEY_FILEPATH"];
|
||||
$fullpath = $fp . $dbAudioPath;
|
||||
|
||||
if ($data == "0") {
|
||||
|
||||
$get_img = "";
|
||||
self::removeArtwork($trackid, $data);
|
||||
|
||||
} else {
|
||||
|
||||
$base64 = @$data;
|
||||
$mime = explode(';', $base64)[0];
|
||||
|
||||
$audioPath = dirname($fullpath);
|
||||
$dbPath = dirname($dbAudioPath);
|
||||
$path_parts = pathinfo($fullpath);
|
||||
$file = $path_parts['filename'];
|
||||
|
||||
//Save Data URI
|
||||
if (file_put_contents($audioPath . "/" . $file, $base64)) {
|
||||
$get_img = $dbPath . "/" . $file;
|
||||
} else {
|
||||
Logging::error("Could not save Data URI");
|
||||
}
|
||||
|
||||
$rfile = $audioPath . "/" . $file;
|
||||
|
||||
if ($mime == "data:image/png") {
|
||||
$ext = 'png';
|
||||
} elseif ($mime == "data:image/gif") {
|
||||
$ext = 'gif';
|
||||
} elseif ($mime == "data:image/bmp") {
|
||||
$ext = 'bmp';
|
||||
} else {
|
||||
$ext = 'jpg';
|
||||
}
|
||||
self::resizeGroup($rfile, $ext);
|
||||
|
||||
}
|
||||
return $get_img;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Deletes just the artwork
|
||||
*/
|
||||
public static function removeArtwork($trackid)
|
||||
{
|
||||
$file = Application_Model_StoredFile::RecallById($trackid);
|
||||
$md = $file->getMetadata();
|
||||
|
||||
$storDir = Application_Model_MusicDir::getStorDir();
|
||||
$fp = $storDir->getDirectory();
|
||||
|
||||
$dbAudioPath = $md["MDATA_KEY_ARTWORK"];
|
||||
$fullpath = $fp . $dbAudioPath;
|
||||
|
||||
if (file_exists($fullpath)) {
|
||||
foreach (glob("$fullpath*", GLOB_NOSORT) as $filename) {
|
||||
unlink($filename);
|
||||
}
|
||||
} else {
|
||||
throw new Exception("Could not locate file ".$filepath);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize artwork group
|
||||
*
|
||||
* @param string $file
|
||||
* @param string $ext
|
||||
*/
|
||||
public static function resizeGroup($file, $ext)
|
||||
{
|
||||
if (file_exists($file)) {
|
||||
self::resizeImage($file, $file . '-32.jpg', $ext, 32, 100);
|
||||
self::resizeImage($file, $file . '-64.jpg', $ext, 64, 100);
|
||||
self::resizeImage($file, $file . '-128.jpg', $ext, 128, 100);
|
||||
self::resizeImage($file, $file . '-256.jpg', $ext, 256, 100);
|
||||
self::resizeImage($file, $file . '-512.jpg', $ext, 512, 100);
|
||||
self::imgToDataURI($file . '-32.jpg', $file . '-32');
|
||||
self::imgToDataURI($file . '-64.jpg', $file . '-64');
|
||||
self::imgToDataURI($file . '-128.jpg', $file . '-128');
|
||||
self::imgToDataURI($file . '-256.jpg', $file . '-256');
|
||||
} else {
|
||||
Logging::error("The file $file does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render image
|
||||
* Used in API to render JPEG
|
||||
*
|
||||
* @param string $file
|
||||
*/
|
||||
public static function renderImage($file)
|
||||
{
|
||||
$im = @imagecreatefromjpeg($file);
|
||||
header('Content-Type: image/jpeg');
|
||||
$img = $im;
|
||||
imagejpeg($img);
|
||||
imagedestroy($img);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render Data URI
|
||||
* Used in API to render Data URI
|
||||
*
|
||||
* @param string $dataFile
|
||||
*/
|
||||
public static function renderDataURI($dataFile)
|
||||
{
|
||||
if($filecontent = file_get_contents($dataFile) !== false){
|
||||
$image = @file_get_contents($dataFile);
|
||||
$image = base64_encode($image);
|
||||
if (!$image || $image === '') {
|
||||
return;
|
||||
}
|
||||
$blob = base64_decode($image);
|
||||
$f = finfo_open();
|
||||
$mime_type = finfo_buffer($f, $blob, FILEINFO_MIME_TYPE);
|
||||
finfo_close($f);
|
||||
header("Content-Type: " . $mime_type);
|
||||
echo $blob;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize Image
|
||||
*
|
||||
* @param string $orig_filename
|
||||
* @param string $converted_filename
|
||||
* @param string $ext
|
||||
* @param string $size Default: 500
|
||||
* @param string $quality Default: 75
|
||||
*
|
||||
*/
|
||||
public static function resizeImage($orig_filename, $converted_filename, $ext, $size=500, $quality=75)
|
||||
{
|
||||
$get_cont = file_get_contents($orig_filename);
|
||||
if ($ext == "png") {
|
||||
$im = @imagecreatefrompng($get_cont);
|
||||
} elseif ($ext == "gif") {
|
||||
$im = @imagecreatefromgif($get_cont);
|
||||
} else {
|
||||
$im = @imagecreatefromjpeg($get_cont);
|
||||
}
|
||||
|
||||
// if one of those bombs, create an error image instead
|
||||
if (!$im) {
|
||||
$im = imagecreatetruecolor(150, 30);
|
||||
$bgc = imagecolorallocate($im, 255, 255, 255);
|
||||
$tc = imagecolorallocate($im, 0, 0, 0);
|
||||
imagefilledrectangle($im, 0, 0, 150, 30, $bgc);
|
||||
imagestring($im, 1, 5, 5, 'Error loading ' . $converted_filename, $tc);
|
||||
}
|
||||
|
||||
// scale if appropriate
|
||||
if ($size){
|
||||
$im = imagescale($im , $size);
|
||||
}
|
||||
|
||||
$img = $im;
|
||||
imagejpeg($img, $converted_filename, $quality);
|
||||
imagedestroy($img);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert image to Data URI
|
||||
*
|
||||
* @param string $orig_filename
|
||||
* @param string $conv_filename
|
||||
*/
|
||||
public static function imgToDataURI($orig_filename, $conv_filename)
|
||||
{
|
||||
$file = file_get_contents($orig_filename);
|
||||
$Image = 'data:image/jpeg;charset=utf-8;base64,'.base64_encode($file);
|
||||
$base64 = @$Image;
|
||||
|
||||
//Save Data URI
|
||||
if (file_put_contents($conv_filename, $base64)) {
|
||||
|
||||
} else {
|
||||
Logging::error("Could not save Data URI");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track Type
|
||||
*
|
||||
* @return string Track type key value
|
||||
*/
|
||||
public static function saveTrackType()
|
||||
{
|
||||
if (isset($_COOKIE['tt_upload'])) {
|
||||
$tt = $_COOKIE['tt_upload'];
|
||||
} else {
|
||||
// Use default track type
|
||||
$tt = Application_Model_Preference::GetTrackTypeDefault();
|
||||
}
|
||||
return $tt;
|
||||
}
|
||||
|
||||
}
|
92
legacy/application/common/FileIO.php
Normal file
92
legacy/application/common/FileIO.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class Application_Common_FileIO contains helper functions for reading and writing files, and sending them over HTTP.
|
||||
*/
|
||||
class Application_Common_FileIO
|
||||
{
|
||||
/**
|
||||
* Reads the requested portion of a file and sends its contents to the client with the appropriate headers.
|
||||
*
|
||||
* This HTTP_RANGE compatible read file function is necessary for allowing streaming media to be skipped around in.
|
||||
*
|
||||
* @param string $filePath - the full filepath or URL pointing to the location of the file
|
||||
* @param string $mimeType - the file's mime type. Defaults to 'audio/mp3'
|
||||
* @param integer $size - the file size, in bytes
|
||||
* @return void
|
||||
*
|
||||
* @link https://groups.google.com/d/msg/jplayer/nSM2UmnSKKA/Hu76jDZS4xcJ
|
||||
* @link http://php.net/manual/en/function.readfile.php#86244
|
||||
*/
|
||||
public static function smartReadFile($filePath, $size, $mimeType)
|
||||
{
|
||||
$fm = @fopen($filePath, 'rb');
|
||||
if (!$fm) {
|
||||
throw new LibreTimeFileNotFoundException($filePath);
|
||||
}
|
||||
|
||||
//Note that $size is allowed to be zero. If that's the case, it means we don't
|
||||
//know the filesize, and we need to figure one out so modern browsers don't get
|
||||
//confused. This should only affect files imported by legacy upstream since
|
||||
//media monitor did not always set the proper size in the database but analyzer
|
||||
//seems to always have a value for this.
|
||||
if ($size === 0) {
|
||||
$fstats = fstat($fm);
|
||||
$size = $fstats['size'];
|
||||
}
|
||||
|
||||
if ($size <= 0) {
|
||||
throw new Exception("Invalid file size returned for file at $filePath");
|
||||
}
|
||||
|
||||
|
||||
$begin = 0;
|
||||
$end = $size - 1;
|
||||
|
||||
ob_start(); //Must start a buffer here for these header() functions
|
||||
|
||||
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||
if (preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) {
|
||||
$begin = intval($matches[1]);
|
||||
if (!empty($matches[2])) {
|
||||
$end = intval($matches[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||
header('HTTP/1.1 206 Partial Content');
|
||||
} else {
|
||||
header('HTTP/1.1 200 OK');
|
||||
}
|
||||
header("Content-Type: $mimeType");
|
||||
header("Content-Transfer-Encoding: binary");
|
||||
header('Cache-Control: public, must-revalidate, max-age=0');
|
||||
header('Pragma: no-cache');
|
||||
header('Accept-Ranges: bytes');
|
||||
header('Content-Length:' . (($end - $begin) + 1));
|
||||
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||
header("Content-Range: bytes $begin-$end/$size");
|
||||
}
|
||||
|
||||
//We can have multiple levels of output buffering. Need to
|
||||
//keep looping until all have been disabled!!!
|
||||
//http://www.php.net/manual/en/function.ob-end-flush.php
|
||||
while (ob_get_level() > 0) {
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
|
||||
//These two lines were removed from Airtime 2.5.x at some point after Libretime forked from Airtime.
|
||||
//These lines allow seek to work for files.
|
||||
//Issue #349
|
||||
$cur = $begin;
|
||||
fseek($fm,$begin,0);
|
||||
|
||||
|
||||
while(!feof($fm) && (connection_status() == 0) && ($cur <= $end)) {
|
||||
echo fread($fm, 1024 * 8);
|
||||
}
|
||||
fclose($fm);
|
||||
}
|
||||
}
|
117
legacy/application/common/HTTPHelper.php
Normal file
117
legacy/application/common/HTTPHelper.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
class Application_Common_HTTPHelper
|
||||
{
|
||||
/**
|
||||
* Returns start and end DateTime vars from given
|
||||
* HTTP Request object
|
||||
*
|
||||
* @param Request
|
||||
* @return array(start DateTime, end DateTime)
|
||||
*/
|
||||
public static function getStartEndFromRequest($request)
|
||||
{
|
||||
return Application_Common_DateHelper::getStartEnd(
|
||||
$request->getParam("start", null),
|
||||
$request->getParam("end", null),
|
||||
$request->getParam("timezone", null)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the base station URL
|
||||
*
|
||||
* @param boolean $secured whether or not to use HTTPS
|
||||
*
|
||||
* @return string the station URL
|
||||
*/
|
||||
public static function getStationUrl($secured = true)
|
||||
{
|
||||
$CC_CONFIG = Config::getConfig();
|
||||
$baseUrl = $CC_CONFIG['baseUrl'];
|
||||
$baseDir = $CC_CONFIG['baseDir'];
|
||||
$basePort = $CC_CONFIG['basePort'];
|
||||
$forceSSL = $CC_CONFIG['forceSSL'];
|
||||
$configProtocol = $CC_CONFIG['protocol'];
|
||||
if (empty($baseDir)) {
|
||||
$baseDir = "/";
|
||||
}
|
||||
if ($baseDir[0] != "/") {
|
||||
$baseDir = "/" . $baseDir;
|
||||
}
|
||||
if (substr($baseDir, -1) != "/") {
|
||||
$baseDir = $baseDir . "/";
|
||||
}
|
||||
|
||||
# Set in reverse order of preference. ForceSSL configuration takes absolute preference, then
|
||||
# the protocol set in config. If neither are set, the port is used to determine the scheme
|
||||
$scheme = "http";
|
||||
if ($secured && $basePort == "443") {
|
||||
$scheme = "https";
|
||||
}
|
||||
if (!empty($configProtocol)) {
|
||||
$scheme = $configProtocol;
|
||||
}
|
||||
if ($forceSSL) {
|
||||
$scheme = "https";
|
||||
}
|
||||
|
||||
$portStr = "";
|
||||
if (($scheme == "http" && $basePort !== "80")
|
||||
|| ($scheme == "https" && $basePort !== "443")) {
|
||||
$portStr = ":${basePort}";
|
||||
}
|
||||
$stationUrl = "$scheme://${baseUrl}${portStr}${baseDir}";
|
||||
|
||||
return $stationUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a cURL POST
|
||||
*
|
||||
* @param string $url the URL to POST to
|
||||
* @param string[] $userPwd array of user args of the form ['user', 'pwd']
|
||||
* @param array $formData array of form data kwargs
|
||||
*
|
||||
* @return mixed the cURL result
|
||||
*/
|
||||
public static function doPost($url, $userPwd, $formData) {
|
||||
$params = "";
|
||||
foreach($formData as $key=>$value) {
|
||||
$params .= $key.'='.$value.'&';
|
||||
}
|
||||
rtrim($params, '&');
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, TRUE);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, implode(':', $userPwd));
|
||||
$result = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
class ZendActionHttpException extends Exception {
|
||||
|
||||
/**
|
||||
* @param Zend_Controller_Action $action
|
||||
* @param int $statusCode
|
||||
* @param string $message
|
||||
* @param int $code
|
||||
* @param Exception $previous
|
||||
*
|
||||
* @throws Zend_Controller_Response_Exception
|
||||
*/
|
||||
public function __construct(Zend_Controller_Action $action, $statusCode, $message,
|
||||
$code = 0, Exception $previous = null) {
|
||||
Logging::error("Error in action " . $action->getRequest()->getActionName()
|
||||
. " with status code $statusCode: $message");
|
||||
$action->getResponse()
|
||||
->setHttpResponseCode($statusCode)
|
||||
->appendBody($message);
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
}
|
160
legacy/application/common/LocaleHelper.php
Normal file
160
legacy/application/common/LocaleHelper.php
Normal file
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
// Global functions for translating domain-specific strings
|
||||
|
||||
class Application_Common_LocaleHelper {
|
||||
|
||||
/**
|
||||
* Return an array of all ISO 639-1 language codes and their corresponding translated language names
|
||||
*
|
||||
* @return array the array of language codes to names
|
||||
*/
|
||||
public static function getISO6391LanguageCodes() {
|
||||
/**
|
||||
* From: http://www.binarytides.com/php-array-of-iso-639-1-language-codes-and-names/
|
||||
*
|
||||
* ISO 639-1 Language Codes
|
||||
* References :
|
||||
* 1. http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
|
||||
* 2. http://blog.xoundboy.com/?p=235
|
||||
*/
|
||||
return array(
|
||||
'en' => _('English'),
|
||||
'aa' => _('Afar'),
|
||||
'ab' => _('Abkhazian'),
|
||||
'af' => _('Afrikaans'),
|
||||
'am' => _('Amharic'),
|
||||
'ar' => _('Arabic'),
|
||||
'as' => _('Assamese'),
|
||||
'ay' => _('Aymara'),
|
||||
'az' => _('Azerbaijani'),
|
||||
'ba' => _('Bashkir'),
|
||||
'be' => _('Belarusian'),
|
||||
'bg' => _('Bulgarian'),
|
||||
'bh' => _('Bihari'),
|
||||
'bi' => _('Bislama'),
|
||||
'bn' => _('Bengali/Bangla'),
|
||||
'bo' => _('Tibetan'),
|
||||
'br' => _('Breton'),
|
||||
'ca' => _('Catalan'),
|
||||
'co' => _('Corsican'),
|
||||
'cs' => _('Czech'),
|
||||
'cy' => _('Welsh'),
|
||||
'da' => _('Danish'),
|
||||
'de' => _('German'),
|
||||
'dz' => _('Bhutani'),
|
||||
'el' => _('Greek'),
|
||||
'eo' => _('Esperanto'),
|
||||
'es' => _('Spanish'),
|
||||
'et' => _('Estonian'),
|
||||
'eu' => _('Basque'),
|
||||
'fa' => _('Persian'),
|
||||
'fi' => _('Finnish'),
|
||||
'fj' => _('Fiji'),
|
||||
'fo' => _('Faeroese'),
|
||||
'fr' => _('French'),
|
||||
'fy' => _('Frisian'),
|
||||
'ga' => _('Irish'),
|
||||
'gd' => _('Scots/Gaelic'),
|
||||
'gl' => _('Galician'),
|
||||
'gn' => _('Guarani'),
|
||||
'gu' => _('Gujarati'),
|
||||
'ha' => _('Hausa'),
|
||||
'hi' => _('Hindi'),
|
||||
'hr' => _('Croatian'),
|
||||
'hu' => _('Hungarian'),
|
||||
'hy' => _('Armenian'),
|
||||
'ia' => _('Interlingua'),
|
||||
'ie' => _('Interlingue'),
|
||||
'ik' => _('Inupiak'),
|
||||
'in' => _('Indonesian'),
|
||||
'is' => _('Icelandic'),
|
||||
'it' => _('Italian'),
|
||||
'iw' => _('Hebrew'),
|
||||
'ja' => _('Japanese'),
|
||||
'ji' => _('Yiddish'),
|
||||
'jw' => _('Javanese'),
|
||||
'ka' => _('Georgian'),
|
||||
'kk' => _('Kazakh'),
|
||||
'kl' => _('Greenlandic'),
|
||||
'km' => _('Cambodian'),
|
||||
'kn' => _('Kannada'),
|
||||
'ko' => _('Korean'),
|
||||
'ks' => _('Kashmiri'),
|
||||
'ku' => _('Kurdish'),
|
||||
'ky' => _('Kirghiz'),
|
||||
'la' => _('Latin'),
|
||||
'ln' => _('Lingala'),
|
||||
'lo' => _('Laothian'),
|
||||
'lt' => _('Lithuanian'),
|
||||
'lv' => _('Latvian/Lettish'),
|
||||
'mg' => _('Malagasy'),
|
||||
'mi' => _('Maori'),
|
||||
'mk' => _('Macedonian'),
|
||||
'ml' => _('Malayalam'),
|
||||
'mn' => _('Mongolian'),
|
||||
'mo' => _('Moldavian'),
|
||||
'mr' => _('Marathi'),
|
||||
'ms' => _('Malay'),
|
||||
'mt' => _('Maltese'),
|
||||
'my' => _('Burmese'),
|
||||
'na' => _('Nauru'),
|
||||
'ne' => _('Nepali'),
|
||||
'nl' => _('Dutch'),
|
||||
'no' => _('Norwegian'),
|
||||
'oc' => _('Occitan'),
|
||||
'om' => _('(Afan)/Oromoor/Oriya'),
|
||||
'pa' => _('Punjabi'),
|
||||
'pl' => _('Polish'),
|
||||
'ps' => _('Pashto/Pushto'),
|
||||
'pt' => _('Portuguese'),
|
||||
'qu' => _('Quechua'),
|
||||
'rm' => _('Rhaeto-Romance'),
|
||||
'rn' => _('Kirundi'),
|
||||
'ro' => _('Romanian'),
|
||||
'ru' => _('Russian'),
|
||||
'rw' => _('Kinyarwanda'),
|
||||
'sa' => _('Sanskrit'),
|
||||
'sd' => _('Sindhi'),
|
||||
'sg' => _('Sangro'),
|
||||
'sh' => _('Serbo-Croatian'),
|
||||
'si' => _('Singhalese'),
|
||||
'sk' => _('Slovak'),
|
||||
'sl' => _('Slovenian'),
|
||||
'sm' => _('Samoan'),
|
||||
'sn' => _('Shona'),
|
||||
'so' => _('Somali'),
|
||||
'sq' => _('Albanian'),
|
||||
'sr' => _('Serbian'),
|
||||
'ss' => _('Siswati'),
|
||||
'st' => _('Sesotho'),
|
||||
'su' => _('Sundanese'),
|
||||
'sv' => _('Swedish'),
|
||||
'sw' => _('Swahili'),
|
||||
'ta' => _('Tamil'),
|
||||
'te' => _('Tegulu'),
|
||||
'tg' => _('Tajik'),
|
||||
'th' => _('Thai'),
|
||||
'ti' => _('Tigrinya'),
|
||||
'tk' => _('Turkmen'),
|
||||
'tl' => _('Tagalog'),
|
||||
'tn' => _('Setswana'),
|
||||
'to' => _('Tonga'),
|
||||
'tr' => _('Turkish'),
|
||||
'ts' => _('Tsonga'),
|
||||
'tt' => _('Tatar'),
|
||||
'tw' => _('Twi'),
|
||||
'uk' => _('Ukrainian'),
|
||||
'ur' => _('Urdu'),
|
||||
'uz' => _('Uzbek'),
|
||||
'vi' => _('Vietnamese'),
|
||||
'vo' => _('Volapuk'),
|
||||
'wo' => _('Wolof'),
|
||||
'xh' => _('Xhosa'),
|
||||
'yo' => _('Yoruba'),
|
||||
'zh' => _('Chinese'),
|
||||
'zu' => _('Zulu'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
96
legacy/application/common/OsPath.php
Normal file
96
legacy/application/common/OsPath.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
class Application_Common_OsPath{
|
||||
// this function is from http://stackoverflow.com/questions/2670299/is-there-a-php-equivalent-function-to-the-python-os-path-normpath
|
||||
public static function normpath($path)
|
||||
{
|
||||
if (empty($path))
|
||||
return '.';
|
||||
|
||||
if (strpos($path, '/') === 0)
|
||||
$initial_slashes = true;
|
||||
else
|
||||
$initial_slashes = false;
|
||||
if (
|
||||
($initial_slashes) &&
|
||||
(strpos($path, '//') === 0) &&
|
||||
(strpos($path, '///') === false)
|
||||
)
|
||||
$initial_slashes = 2;
|
||||
$initial_slashes = (int) $initial_slashes;
|
||||
|
||||
$comps = explode('/', $path);
|
||||
$new_comps = array();
|
||||
foreach ($comps as $comp)
|
||||
{
|
||||
if (in_array($comp, array('', '.')))
|
||||
continue;
|
||||
if (
|
||||
($comp != '..') ||
|
||||
(!$initial_slashes && !$new_comps) ||
|
||||
($new_comps && (end($new_comps) == '..'))
|
||||
)
|
||||
array_push($new_comps, $comp);
|
||||
elseif ($new_comps)
|
||||
array_pop($new_comps);
|
||||
}
|
||||
$comps = $new_comps;
|
||||
$path = implode('/', $comps);
|
||||
if ($initial_slashes)
|
||||
$path = str_repeat('/', $initial_slashes) . $path;
|
||||
if ($path)
|
||||
return $path;
|
||||
else
|
||||
return '.';
|
||||
}
|
||||
|
||||
/* Similar to the os.path.join python method
|
||||
* http://stackoverflow.com/a/1782990/276949 */
|
||||
public static function join() {
|
||||
$args = func_get_args();
|
||||
$paths = array();
|
||||
|
||||
foreach($args as $arg) {
|
||||
$paths = array_merge($paths, (array)$arg);
|
||||
}
|
||||
|
||||
foreach($paths as &$path) {
|
||||
$path = trim($path, DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
if (substr($args[0], 0, 1) == DIRECTORY_SEPARATOR) {
|
||||
$paths[0] = DIRECTORY_SEPARATOR . $paths[0];
|
||||
}
|
||||
|
||||
return join(DIRECTORY_SEPARATOR, $paths);
|
||||
}
|
||||
|
||||
public static function getBaseDir() {
|
||||
|
||||
$CC_CONFIG = Config::getConfig();
|
||||
$baseUrl = $CC_CONFIG['baseDir'];
|
||||
|
||||
if ($baseUrl[0] != "/") {
|
||||
$baseUrl = "/".$baseUrl;
|
||||
}
|
||||
|
||||
if ($baseUrl[strlen($baseUrl) -1] != "/") {
|
||||
$baseUrl = $baseUrl."/";
|
||||
}
|
||||
|
||||
|
||||
return $baseUrl;
|
||||
}
|
||||
|
||||
public static function formatDirectoryWithDirectorySeparators($dir)
|
||||
{
|
||||
if ($dir[0] != "/") {
|
||||
$dir = "/".$dir;
|
||||
}
|
||||
|
||||
if ($dir[strlen($dir) -1] != "/") {
|
||||
$dir = $dir."/";
|
||||
}
|
||||
|
||||
return $dir;
|
||||
}
|
||||
}
|
104
legacy/application/common/PodcastManager.php
Normal file
104
legacy/application/common/PodcastManager.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
class PodcastManager {
|
||||
|
||||
/**
|
||||
* @var int how often, in seconds, to check for and ingest new podcast episodes
|
||||
*/
|
||||
private static $_PODCAST_POLL_INTERVAL_SECONDS = 3600; // 1 hour
|
||||
|
||||
/**
|
||||
* Check whether $_PODCAST_POLL_INTERVAL_SECONDS have passed since the last call to
|
||||
* downloadNewestEpisodes
|
||||
*
|
||||
* @return bool true if $_PODCAST_POLL_INTERVAL_SECONDS has passed since the last check
|
||||
*/
|
||||
public static function hasPodcastPollIntervalPassed() {
|
||||
$lastPolled = Application_Model_Preference::getPodcastPollLock();
|
||||
return empty($lastPolled) || (microtime(true) > $lastPolled + self::$_PODCAST_POLL_INTERVAL_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all podcasts flagged for automatic ingest whose most recent episode has
|
||||
* yet to be downloaded and download it with Celery
|
||||
*
|
||||
* @throws InvalidPodcastException
|
||||
* @throws PodcastNotFoundException
|
||||
*/
|
||||
public static function downloadNewestEpisodes() {
|
||||
$autoIngestPodcasts = static::_getAutoIngestPodcasts();
|
||||
$service = new Application_Service_PodcastEpisodeService();
|
||||
foreach ($autoIngestPodcasts as $podcast) {
|
||||
$episodes = static::_findUningestedEpisodes($podcast, $service);
|
||||
// Since episodes don't have to be uploaded with a time (H:i:s) component,
|
||||
// store the timestamp of the most recent (first pushed to the array) episode
|
||||
// that we're ingesting.
|
||||
// Note that this folds to the failure case (Celery task timeout/download failure)
|
||||
// but will at least continue to ingest new episodes.
|
||||
if (!empty($episodes)) {
|
||||
$podcast->setDbAutoIngestTimestamp(gmdate('r', strtotime($episodes[0]->getDbPublicationDate())))->save();
|
||||
$service->downloadEpisodes($episodes);
|
||||
}
|
||||
}
|
||||
|
||||
Application_Model_Preference::setPodcastPollLock(microtime(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an ImportedPodcast, find all uningested episodes since the last automatic ingest,
|
||||
* and add them to a given episodes array
|
||||
*
|
||||
* @param ImportedPodcast $podcast the podcast to search
|
||||
* @param Application_Service_PodcastEpisodeService $service podcast episode service object
|
||||
*
|
||||
* @return array array of episodes to append be downloaded
|
||||
*/
|
||||
protected static function _findUningestedEpisodes($podcast, $service) {
|
||||
$episodeList = $service->getPodcastEpisodes($podcast->getDbPodcastId());
|
||||
$episodes = array();
|
||||
usort($episodeList, array(__CLASS__, "_sortByEpisodePubDate"));
|
||||
for ($i = 0; $i < sizeof($episodeList); $i++) {
|
||||
$episodeData = $episodeList[$i];
|
||||
$ts = $podcast->getDbAutoIngestTimestamp();
|
||||
// If the timestamp for this podcast is empty (no previous episodes have been ingested) and there are no
|
||||
// episodes in the list of episodes to ingest, don't skip this episode - we should try to ingest the
|
||||
// most recent episode when the user first sets the podcast to automatic ingest.
|
||||
// If the publication date of this episode is before the ingest timestamp, we don't need to ingest it
|
||||
if ((empty($ts) && ($i > 0)) || strtotime($episodeData["pub_date"]) < strtotime($ts)) {
|
||||
continue;
|
||||
}
|
||||
$episode = PodcastEpisodesQuery::create()->findOneByDbEpisodeGuid($episodeData["guid"]);
|
||||
// Make sure there's no existing episode placeholder or import, and that the data is non-empty
|
||||
if (empty($episode) && !empty($episodeData)) {
|
||||
$placeholder = $service->addPlaceholder($podcast->getDbPodcastId(), $episodeData);
|
||||
array_push($episodes, $placeholder);
|
||||
}
|
||||
}
|
||||
return $episodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all podcasts flagged for automatic ingest
|
||||
*
|
||||
* @return PropelObjectCollection collection of ImportedPodcast objects
|
||||
* flagged for automatic ingest
|
||||
*/
|
||||
protected static function _getAutoIngestPodcasts() {
|
||||
return ImportedPodcastQuery::create()
|
||||
->filterByDbAutoIngest(true)
|
||||
->find();
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom sort function for podcast episodes
|
||||
*
|
||||
* @param array $a first episode array to compare
|
||||
* @param array $b second episode array to compare
|
||||
* @return bool boolean for ordering
|
||||
*/
|
||||
protected static function _sortByEpisodePubDate($a, $b) {
|
||||
if ($a["pub_date"] == $b["pub_date"]) return 0;
|
||||
return (strtotime($a["pub_date"]) < strtotime($b["pub_date"])) ? 1 : -1; // Descending order
|
||||
}
|
||||
|
||||
}
|
23
legacy/application/common/SecurityHelper.php
Normal file
23
legacy/application/common/SecurityHelper.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
class SecurityHelper {
|
||||
|
||||
public static function htmlescape_recursive(&$arr) {
|
||||
foreach ($arr as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
self::htmlescape_recursive($arr[$key]);
|
||||
} else if (is_string($val)) {
|
||||
$arr[$key] = htmlspecialchars($val, ENT_QUOTES);
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
public static function verifyCSRFToken($observedToken) {
|
||||
$current_namespace = new Zend_Session_Namespace('csrf_namespace');
|
||||
$observed_csrf_token = $observedToken;
|
||||
$expected_csrf_token = $current_namespace->authtoken;
|
||||
|
||||
return ($observed_csrf_token == $expected_csrf_token);
|
||||
}
|
||||
}
|
13
legacy/application/common/SessionHelper.php
Normal file
13
legacy/application/common/SessionHelper.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
class SessionHelper
|
||||
{
|
||||
public static function reopenSessionForWriting() {
|
||||
//PHP will send double Set-Cookie headers if we reopen the
|
||||
//session for writing, and this breaks IE8 and some other browsers.
|
||||
//This hacky workaround prevents double headers. Background here:
|
||||
// https://bugs.php.net/bug.php?id=38104
|
||||
ini_set('session.cache_limiter', null);
|
||||
session_start(); // Reopen the session for writing (without resending the Set-Cookie header)
|
||||
}
|
||||
}
|
372
legacy/application/common/TaskManager.php
Normal file
372
legacy/application/common/TaskManager.php
Normal file
|
@ -0,0 +1,372 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class TaskManager
|
||||
*
|
||||
* Background class for 'asynchronous' task management for Airtime stations
|
||||
*/
|
||||
final class TaskManager {
|
||||
|
||||
/**
|
||||
* @var array tasks to be run. Maps task names to a boolean value denoting
|
||||
* whether the task has been checked/run
|
||||
*/
|
||||
protected $_taskList;
|
||||
|
||||
/**
|
||||
* @var TaskManager singleton instance object
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* @var int TASK_INTERVAL_SECONDS how often, in seconds, to run the TaskManager tasks
|
||||
*/
|
||||
const TASK_INTERVAL_SECONDS = 30;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var $con PDO Propel connection object
|
||||
*/
|
||||
private $_con;
|
||||
|
||||
/**
|
||||
* Private constructor so class is uninstantiable
|
||||
*/
|
||||
private function __construct() {
|
||||
foreach (TaskFactory::getTasks() as $k => $task) {
|
||||
$this->_taskList[$task] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton instance of this class
|
||||
*
|
||||
* @return TaskManager the TaskManager instance
|
||||
*/
|
||||
public static function getInstance() {
|
||||
if (!self::$_instance) {
|
||||
self::$_instance = new TaskManager();
|
||||
}
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a single task.
|
||||
*
|
||||
* @param string $taskName the ENUM name of the task to be run
|
||||
*/
|
||||
public function runTask($taskName) {
|
||||
$task = TaskFactory::getTask($taskName);
|
||||
if ($task && $task->shouldBeRun()) {
|
||||
$task->run();
|
||||
}
|
||||
$this->_taskList[$taskName] = true; // Mark that the task has been checked/run.
|
||||
// This is important for prioritized tasks that
|
||||
// we need to run on every request (such as the
|
||||
// schema check/upgrade)
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all tasks that need to be run.
|
||||
*
|
||||
* To prevent blocking and making too many requests to the database,
|
||||
* we implement a row-level, non-blocking, read-protected lock on a
|
||||
* timestamp that we check each time the application is bootstrapped,
|
||||
* which, assuming enough time has passed, is updated before running
|
||||
* the tasks.
|
||||
*/
|
||||
public function runTasks() {
|
||||
// If there is data in auth storage, this could be a user request
|
||||
// so we should just return to avoid blocking
|
||||
if ($this->_isUserSessionRequest()) {
|
||||
return;
|
||||
}
|
||||
$this->_con = Propel::getConnection(CcPrefPeer::DATABASE_NAME);
|
||||
$this->_con->beginTransaction();
|
||||
try {
|
||||
$lock = $this->_getLock();
|
||||
if ($lock && (microtime(true) < ($lock['valstr'] + self::TASK_INTERVAL_SECONDS))) {
|
||||
// Propel caches the database connection and uses it persistently, so if we don't
|
||||
// use commit() here, we end up blocking other queries made within this request
|
||||
$this->_con->commit();
|
||||
return;
|
||||
}
|
||||
$this->_updateLock($lock);
|
||||
$this->_con->commit();
|
||||
} catch (Exception $e) {
|
||||
// We get here if there are simultaneous requests trying to fetch the lock row
|
||||
$this->_con->rollBack();
|
||||
Logging::warn($e->getMessage());
|
||||
return;
|
||||
}
|
||||
foreach ($this->_taskList as $task => $hasTaskRun) {
|
||||
if (!$hasTaskRun) {
|
||||
$this->runTask($task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current session is a user request
|
||||
*
|
||||
* @return bool true if there is a Zend_Auth object in the current session,
|
||||
* otherwise false
|
||||
*/
|
||||
private function _isUserSessionRequest() {
|
||||
if (!Zend_Session::isStarted()) {
|
||||
return false;
|
||||
}
|
||||
$auth = Zend_Auth::getInstance();
|
||||
$data = $auth->getStorage()->read();
|
||||
return !empty($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the task_manager_lock from cc_pref with a row-level lock for atomicity
|
||||
*
|
||||
* The lock is exclusive (prevent reads) and will only last for the duration
|
||||
* of the transaction. We add NOWAIT so reads on the row during the transaction
|
||||
* won't block
|
||||
*
|
||||
* @return array|bool an array containing the row values, or false on failure
|
||||
*/
|
||||
private function _getLock() {
|
||||
$sql = "SELECT * FROM cc_pref WHERE keystr='task_manager_lock' LIMIT 1 FOR UPDATE NOWAIT";
|
||||
$st = $this->_con->prepare($sql);
|
||||
$st->execute();
|
||||
return $st->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update and commit the new lock value, or insert it if it doesn't exist
|
||||
*
|
||||
* @param $lock array cc_pref lock row values
|
||||
*/
|
||||
private function _updateLock($lock) {
|
||||
$sql = empty($lock) ? "INSERT INTO cc_pref (keystr, valstr) VALUES ('task_manager_lock', :value)"
|
||||
: "UPDATE cc_pref SET valstr=:value WHERE keystr='task_manager_lock'";
|
||||
$st = $this->_con->prepare($sql);
|
||||
$st->execute(array(":value" => microtime(true)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface AirtimeTask Interface for task operations
|
||||
*/
|
||||
interface AirtimeTask {
|
||||
|
||||
/**
|
||||
* Check whether the task should be run
|
||||
*
|
||||
* @return bool true if the task needs to be run, otherwise false
|
||||
*/
|
||||
public function shouldBeRun();
|
||||
|
||||
/**
|
||||
* Run the task
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class UpgradeTask
|
||||
*
|
||||
* Checks the current Airtime version and runs any outstanding upgrades
|
||||
*/
|
||||
class UpgradeTask implements AirtimeTask {
|
||||
|
||||
/**
|
||||
* Check the current Airtime schema version to see if an upgrade should be run
|
||||
*
|
||||
* @return bool true if an upgrade is needed
|
||||
*/
|
||||
public function shouldBeRun() {
|
||||
return UpgradeManager::checkIfUpgradeIsNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all upgrades above the current schema version
|
||||
*/
|
||||
public function run() {
|
||||
UpgradeManager::doUpgrade();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class CeleryTask
|
||||
*
|
||||
* Checks the Celery broker task queue and runs callbacks for completed tasks
|
||||
*/
|
||||
class CeleryTask implements AirtimeTask {
|
||||
|
||||
/**
|
||||
* Check the ThirdPartyTrackReferences table to see if there are any pending tasks
|
||||
*
|
||||
* @return bool true if there are pending tasks in ThirdPartyTrackReferences
|
||||
*/
|
||||
public function shouldBeRun() {
|
||||
return !CeleryManager::isBrokerTaskQueueEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll the task queue for any completed Celery tasks
|
||||
*/
|
||||
public function run() {
|
||||
CeleryManager::pollBrokerTaskQueue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class AutoPlaylistTask
|
||||
*
|
||||
* Checks for shows with an autoplaylist that needs to be filled in
|
||||
*
|
||||
*/
|
||||
class AutoPlaylistTask implements AirtimeTask
|
||||
{
|
||||
/**
|
||||
* Checks whether or not the autoplaylist polling interval has passed
|
||||
*
|
||||
* @return bool true if the autoplaylist polling interval has passed
|
||||
*/
|
||||
public function shouldBeRun()
|
||||
{
|
||||
return AutoPlaylistManager::hasAutoPlaylistPollIntervalPassed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the autoplaylist for the shows
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
AutoPlaylistManager::buildAutoPlaylist();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class PodcastTask
|
||||
*
|
||||
* Checks podcasts marked for automatic ingest and downloads any new episodes
|
||||
* since the task was last run
|
||||
*/
|
||||
class PodcastTask implements AirtimeTask {
|
||||
|
||||
/**
|
||||
* Check whether or not the podcast polling interval has passed
|
||||
*
|
||||
* @return bool true if the podcast polling interval has passed
|
||||
*/
|
||||
public function shouldBeRun() {
|
||||
$overQuota = Application_Model_Systemstatus::isDiskOverQuota();
|
||||
return !$overQuota && PodcastManager::hasPodcastPollIntervalPassed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the latest episode for all podcasts flagged for automatic ingest
|
||||
*/
|
||||
public function run() {
|
||||
PodcastManager::downloadNewestEpisodes();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class ImportTask
|
||||
*/
|
||||
class ImportCleanupTask implements AirtimeTask {
|
||||
|
||||
/**
|
||||
* Check if there are any files that have been stuck
|
||||
* in Pending status for over an hour
|
||||
*
|
||||
* @return bool true if there are any files stuck pending,
|
||||
* otherwise false
|
||||
*/
|
||||
public function shouldBeRun() {
|
||||
return Application_Service_MediaService::areFilesStuckInPending();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up stuck imports by changing their import status to Failed
|
||||
*/
|
||||
public function run() {
|
||||
Application_Service_MediaService::clearStuckPendingImports();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class StationPodcastTask
|
||||
*
|
||||
* Checks the Station podcast rollover timer and resets allotted
|
||||
* downloads if enough time has passed (default: 1 month)
|
||||
*/
|
||||
class StationPodcastTask implements AirtimeTask {
|
||||
|
||||
const STATION_PODCAST_RESET_TIMER_SECONDS = 2.628e+6; // 1 month
|
||||
|
||||
/**
|
||||
* Check whether or not the download counter for the station podcast should be reset
|
||||
*
|
||||
* @return bool true if enough time has passed
|
||||
*/
|
||||
public function shouldBeRun() {
|
||||
$lastReset = Application_Model_Preference::getStationPodcastDownloadResetTimer();
|
||||
return empty($lastReset) || (microtime(true) > ($lastReset + self::STATION_PODCAST_RESET_TIMER_SECONDS));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the station podcast download counter
|
||||
*/
|
||||
public function run() {
|
||||
Application_Model_Preference::resetStationPodcastDownloadCounter();
|
||||
Application_Model_Preference::setStationPodcastDownloadResetTimer(microtime(true));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class TaskFactory Factory class to abstract task instantiation
|
||||
*/
|
||||
class TaskFactory {
|
||||
|
||||
/**
|
||||
* Check if the class with the given name implements AirtimeTask
|
||||
*
|
||||
* @param $c string class name
|
||||
*
|
||||
* @return bool true if the class $c implements AirtimeTask
|
||||
*/
|
||||
private static function _isTask($c) {
|
||||
return array_key_exists('AirtimeTask', class_implements($c));
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter all declared classes to get all classes implementing the AirtimeTask interface
|
||||
*
|
||||
* @return array all classes implementing the AirtimeTask interface
|
||||
*/
|
||||
public static function getTasks() {
|
||||
return array_filter(get_declared_classes(), array(__CLASS__, "_isTask"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an AirtimeTask based on class name
|
||||
*
|
||||
* @param $task string name of the class implementing AirtimeTask to construct
|
||||
*
|
||||
* @return AirtimeTask|null return a task of the given type or null if no corresponding task exists
|
||||
*/
|
||||
public static function getTask($task) {
|
||||
// Try to get a valid class name from the given string
|
||||
if (!class_exists($task)) $task = str_replace(' ', '', ucwords($task)) . "Task";
|
||||
return class_exists($task) ? new $task() : null;
|
||||
}
|
||||
|
||||
}
|
32
legacy/application/common/Timezone.php
Normal file
32
legacy/application/common/Timezone.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
class Application_Common_Timezone
|
||||
{
|
||||
public static function getTimezones()
|
||||
{
|
||||
$regions = array(
|
||||
'Africa' => DateTimeZone::AFRICA,
|
||||
'America' => DateTimeZone::AMERICA,
|
||||
'Antarctica' => DateTimeZone::ANTARCTICA,
|
||||
'Arctic' => DateTimeZone::ARCTIC,
|
||||
'Asia' => DateTimeZone::ASIA,
|
||||
'Atlantic' => DateTimeZone::ATLANTIC,
|
||||
'Australia' => DateTimeZone::AUSTRALIA,
|
||||
'Europe' => DateTimeZone::EUROPE,
|
||||
'Indian' => DateTimeZone::INDIAN,
|
||||
'Pacific' => DateTimeZone::PACIFIC,
|
||||
'UTC' => DateTimeZone::UTC
|
||||
);
|
||||
|
||||
$tzlist = array(NULL => _("Use station default"));
|
||||
|
||||
foreach ($regions as $name => $mask) {
|
||||
$ids = DateTimeZone::listIdentifiers($mask);
|
||||
foreach ($ids as $id) {
|
||||
$tzlist[$id] = str_replace("_", " ", $id);
|
||||
}
|
||||
}
|
||||
|
||||
return $tzlist;
|
||||
}
|
||||
}
|
44
legacy/application/common/TuneIn.php
Normal file
44
legacy/application/common/TuneIn.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
class Application_Common_TuneIn
|
||||
{
|
||||
/**
|
||||
* @param $title url encoded string
|
||||
* @param $artist url encoded string
|
||||
*/
|
||||
public static function sendMetadataToTunein($title, $artist)
|
||||
{
|
||||
$credQryStr = self::getCredentialsQueryString();
|
||||
$metadataQryStr = "&title=".$title."&artist=".$artist."&commercial=false";
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, TUNEIN_API_URL . $credQryStr . $metadataQryStr);
|
||||
curl_setopt($ch, CURLOPT_FAILONERROR, 1);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
|
||||
$xmlResponse = curl_exec($ch);
|
||||
if (curl_error($ch)) {
|
||||
Logging::error("Failed to reach TuneIn: ". curl_errno($ch)." - ". curl_error($ch) . " - " . curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
$xmlObj = new SimpleXMLElement($xmlResponse);
|
||||
if (!$xmlObj || $xmlObj->head->status != "200") {
|
||||
Logging::info("Error occurred pushing metadata to TuneIn:");
|
||||
Logging::info($xmlResponse);
|
||||
} else if ($xmlObj->head->status == "200") {
|
||||
Application_Model_Preference::setLastTuneinMetadataUpdate(time());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static function getCredentialsQueryString() {
|
||||
$tuneInStationID = Application_Model_Preference::getTuneinStationId();
|
||||
$tuneInPartnerID = Application_Model_Preference::getTuneinPartnerId();
|
||||
$tuneInPartnerKey = Application_Model_Preference::getTuneinPartnerKey();
|
||||
|
||||
return "?partnerId=".$tuneInPartnerID."&partnerKey=".$tuneInPartnerKey."&id=".$tuneInStationID;
|
||||
}
|
||||
|
||||
}
|
221
legacy/application/common/UsabilityHints.php
Normal file
221
legacy/application/common/UsabilityHints.php
Normal file
|
@ -0,0 +1,221 @@
|
|||
<?php
|
||||
|
||||
class Application_Common_UsabilityHints
|
||||
{
|
||||
|
||||
/**
|
||||
* @param $userPath User's current location in Airtime (i.e. /Plupload)
|
||||
* @return string
|
||||
*/
|
||||
public static function getUsabilityHint($userPath=null)
|
||||
{
|
||||
// We want to display hints in this order:
|
||||
// 1. Check if files are uploaded
|
||||
// 2. Check if a show is scheduled
|
||||
// 3. Check if current or next show needs content
|
||||
|
||||
// Once the user is on the page linked to from the hint we want to
|
||||
// display a new message further describing what to do. Once this
|
||||
// action has been done we can hide the message and get the next
|
||||
// usability hint, if there is one.
|
||||
|
||||
$userIsOnCalendarPage = false;
|
||||
$userIsOnAddMediaPage = false;
|
||||
$userIsOnShowbuilderPage = false;
|
||||
$userIsSuperAdmin = Application_Model_User::getCurrentUser()->isSuperAdmin();
|
||||
|
||||
// If $userPath is set the request came from AJAX so the user's
|
||||
// current location inside Airtime gets passed in to this function.
|
||||
if (!is_null($userPath)) {
|
||||
// We check if the controller names are in the user's current location
|
||||
// so we can ignore leading or trailing slashes, special characters like '#',
|
||||
// and additional controller action names like '/user/add-user'
|
||||
|
||||
if (strpos(strtolower($userPath), 'plupload') !== false) {
|
||||
$userIsOnAddMediaPage = true;
|
||||
}
|
||||
|
||||
if (strpos(strtolower($userPath), 'schedule') !== false) {
|
||||
$userIsOnCalendarPage = true;
|
||||
}
|
||||
|
||||
if (strpos(strtolower($userPath), 'showbuilder') !== false) {
|
||||
$userIsOnShowbuilderPage = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
// If $userPath is not set the request came from inside Airtime so
|
||||
// we can use Zend's Front Controller to get the user's current location.
|
||||
$currentController = strtolower(Zend_Controller_Front::getInstance()->getRequest()->getControllerName());
|
||||
|
||||
if ($currentController == "schedule") {
|
||||
$userIsOnCalendarPage = true;
|
||||
}
|
||||
|
||||
if ($currentController == "plupload") {
|
||||
$userIsOnAddMediaPage = true;
|
||||
}
|
||||
|
||||
if ($currentController == 'showbuilder') {
|
||||
$userIsOnShowbuilderPage = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::zeroFilesUploaded()) {
|
||||
if ($userIsOnAddMediaPage) {
|
||||
return _("Upload some tracks below to add them to your library!");
|
||||
} else {
|
||||
return sprintf(_("It looks like you haven't uploaded any audio files yet. %sUpload a file now%s."),
|
||||
"<a href=\"/plupload\">",
|
||||
"</a>");
|
||||
}
|
||||
} else if (!self::isFutureOrCurrentShowScheduled()) {
|
||||
if ($userIsOnCalendarPage) {
|
||||
return _("Click the 'New Show' button and fill out the required fields.");
|
||||
} else {
|
||||
return sprintf(_("It looks like you don't have any shows scheduled. %sCreate a show now%s."),
|
||||
"<a href=\"/schedule\">",
|
||||
"</a>");
|
||||
}
|
||||
} else if (self::isCurrentShowEmpty()) {
|
||||
// If the current show is linked users cannot add content to it so we have to provide a different message.
|
||||
if (self::isCurrentShowLinked()) {
|
||||
if ($userIsOnCalendarPage) {
|
||||
return _("To start broadcasting, cancel the current linked show by clicking on it and selecting 'Cancel Show'.");
|
||||
} else {
|
||||
return sprintf(_("Linked shows need to be filled with tracks before it starts. To start broadcasting cancel the current linked show and schedule an unlinked show.
|
||||
%sCreate an unlinked show now%s."), "<a href=\"/schedule\">", "</a>");
|
||||
}
|
||||
} else {
|
||||
if ($userIsOnCalendarPage) {
|
||||
return _("To start broadcasting, click on the current show and select 'Schedule Tracks'");
|
||||
} else {
|
||||
return sprintf(_("It looks like the current show needs more tracks. %sAdd tracks to your show now%s."),
|
||||
"<a href=\"/schedule\">",
|
||||
"</a>");
|
||||
}
|
||||
}
|
||||
} else if (!self::getCurrentShow() && self::isNextShowEmpty()) {
|
||||
if ($userIsOnCalendarPage) {
|
||||
return _("Click on the show starting next and select 'Schedule Tracks'");
|
||||
} else {
|
||||
return sprintf(_("It looks like the next show is empty. %sAdd tracks to your show now%s."),
|
||||
"<a href=\"/schedule\">",
|
||||
"</a>");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if no files have been uploaded.
|
||||
*/
|
||||
private static function zeroFilesUploaded()
|
||||
{
|
||||
$fileCount = CcFilesQuery::create()
|
||||
->filterByDbFileExists(true)
|
||||
->filterByDbHidden(false)
|
||||
->count();
|
||||
|
||||
if ($fileCount == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is at least one show currently scheduled
|
||||
* or in the future.
|
||||
*/
|
||||
private static function isFutureOrCurrentShowScheduled()
|
||||
{
|
||||
$futureShow = self::getNextFutureShow();
|
||||
$currentShow = self::getCurrentShow();
|
||||
|
||||
if (is_null($futureShow) && is_null($currentShow)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static function isCurrentShowEmpty()
|
||||
{
|
||||
$currentShow = self::getCurrentShow();
|
||||
|
||||
if (is_null($currentShow)) {
|
||||
return false;
|
||||
} else {
|
||||
$now = new DateTime("now", new DateTimeZone("UTC"));
|
||||
$scheduledTracks = CcScheduleQuery::create()
|
||||
->filterByDbInstanceId($currentShow->getDbId())
|
||||
->filterByDbEnds($now, Criteria::GREATER_EQUAL)
|
||||
->find();
|
||||
if ($scheduledTracks->count() == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function isNextShowEmpty()
|
||||
{
|
||||
$futureShow = self::getNextFutureShow();
|
||||
|
||||
if (is_null($futureShow)) {
|
||||
return false;
|
||||
} else {
|
||||
$now = new DateTime("now", new DateTimeZone("UTC"));
|
||||
$scheduledTracks = CcScheduleQuery::create()
|
||||
->filterByDbInstanceId($futureShow->getDbId())
|
||||
->filterByDbStarts($now, Criteria::GREATER_EQUAL)
|
||||
->find();
|
||||
if ($scheduledTracks->count() == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function getCurrentShow()
|
||||
{
|
||||
$now = new DateTime("now", new DateTimeZone("UTC"));
|
||||
|
||||
return CcShowInstancesQuery::create()
|
||||
->filterByDbStarts($now, Criteria::LESS_THAN)
|
||||
->filterByDbEnds($now, Criteria::GREATER_THAN)
|
||||
->filterByDbModifiedInstance(false)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
private static function getNextFutureShow()
|
||||
{
|
||||
$now = new DateTime("now", new DateTimeZone("UTC"));
|
||||
|
||||
return CcShowInstancesQuery::create()
|
||||
->filterByDbStarts($now, Criteria::GREATER_THAN)
|
||||
->filterByDbModifiedInstance(false)
|
||||
->orderByDbStarts()
|
||||
->findOne();
|
||||
}
|
||||
|
||||
private static function isCurrentShowLinked()
|
||||
{
|
||||
$currentShow = self::getCurrentShow();
|
||||
if (!is_null($currentShow)) {
|
||||
$show = CcShowQuery::create()
|
||||
->filterByDbId($currentShow->getDbShowId())
|
||||
->findOne();
|
||||
if ($show->isLinked()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
175
legacy/application/common/WidgetHelper.php
Normal file
175
legacy/application/common/WidgetHelper.php
Normal file
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
define("DAYS_PER_WEEK", 7);
|
||||
|
||||
class WidgetHelper
|
||||
{
|
||||
public static function getWeekInfo($userDefinedTimezone)
|
||||
{
|
||||
//weekStart is in station time.
|
||||
$weekStartDateTime = Application_Common_DateHelper::getWeekStartDateTime();
|
||||
|
||||
$dow = array("monday", "tuesday", "wednesday", "thursday", "friday",
|
||||
"saturday", "sunday", "nextmonday", "nexttuesday", "nextwednesday",
|
||||
"nextthursday", "nextfriday", "nextsaturday", "nextsunday");
|
||||
|
||||
$result = array();
|
||||
|
||||
// default to the station timezone
|
||||
$timezone = Application_Model_Preference::GetDefaultTimezone();
|
||||
if ($userDefinedTimezone) {
|
||||
$userDefinedTimezone = strtolower($userDefinedTimezone);
|
||||
// if the timezone defined by the user exists, use that
|
||||
if (array_key_exists($userDefinedTimezone, timezone_abbreviations_list())) {
|
||||
$timezone = $userDefinedTimezone;
|
||||
}
|
||||
}
|
||||
$utcTimezone = new DateTimeZone("UTC");
|
||||
|
||||
$weekStartDateTime->setTimezone($utcTimezone);
|
||||
$utcDayStart = $weekStartDateTime->format(DEFAULT_TIMESTAMP_FORMAT);
|
||||
for ($i = 0; $i < 14; $i++) {
|
||||
//have to be in station timezone when adding 1 day for daylight savings.
|
||||
$weekStartDateTime->setTimezone(new DateTimeZone($timezone));
|
||||
$weekStartDateTime->add(new DateInterval('P1D'));
|
||||
|
||||
//convert back to UTC to get the actual timestamp used for search.
|
||||
$weekStartDateTime->setTimezone($utcTimezone);
|
||||
|
||||
$utcDayEnd = $weekStartDateTime->format(DEFAULT_TIMESTAMP_FORMAT);
|
||||
$shows = Application_Model_Show::getNextShows($utcDayStart, "ALL", $utcDayEnd);
|
||||
$utcDayStart = $utcDayEnd;
|
||||
|
||||
// convert to user-defined timezone, or default to station
|
||||
Application_Common_DateHelper::convertTimestampsToTimezone(
|
||||
$shows,
|
||||
array("starts", "ends", "start_timestamp","end_timestamp"),
|
||||
$timezone
|
||||
);
|
||||
|
||||
$result[$dow[$i]] = $shows;
|
||||
}
|
||||
|
||||
// XSS exploit prevention
|
||||
SecurityHelper::htmlescape_recursive($result);
|
||||
|
||||
// convert image paths to point to api endpoints
|
||||
self::findAndConvertPaths($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a weeks worth of shows in UTC, and an info array of the current week's days.
|
||||
* Returns an array of two arrays:
|
||||
*
|
||||
* The first array is 7 consecutive week days, starting with the current day.
|
||||
*
|
||||
* The second array contains shows scheduled during the 7 week days in the first array.
|
||||
* The shows returned in this array are not in any order and are in UTC.
|
||||
*
|
||||
* We don't do any timezone conversion in this function on purpose. All timezone conversion
|
||||
* and show time ordering should be done on the frontend.
|
||||
*
|
||||
* *** This function does no HTML encoding. It is up to the caller to escape or encode the data appropriately.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getWeekInfoV2()
|
||||
{
|
||||
$weekStartDateTime = new DateTime("now", new DateTimeZone(Application_Model_Preference::GetTimezone()));
|
||||
|
||||
$result = array();
|
||||
|
||||
$utcTimezone = new DateTimeZone("UTC");
|
||||
|
||||
$weekStartDateTime->setTimezone($utcTimezone);
|
||||
|
||||
// Use this variable as the start date/time range when querying
|
||||
// for shows. We set it to 1 day prior to the beginning of the
|
||||
// schedule widget data to account for show date changes when
|
||||
// converting their start day/time to the client's local timezone.
|
||||
$showQueryDateRangeStart = clone $weekStartDateTime;
|
||||
$showQueryDateRangeStart->sub(new DateInterval("P1D"));
|
||||
$showQueryDateRangeStart->setTime(0, 0, 0);
|
||||
|
||||
for ($dayOfWeekCounter = 0; $dayOfWeekCounter < DAYS_PER_WEEK; $dayOfWeekCounter++) {
|
||||
$dateParse = date_parse($weekStartDateTime->format("Y-m-d H:i:s"));
|
||||
|
||||
// Associate data to its date so that when we convert this array
|
||||
// to json the order remains the same - in chronological order.
|
||||
// We also format the key to be for example: "2015-6-1" to match
|
||||
// javascript date formats so it's easier to sort the shows by day.
|
||||
$result["weekDays"][$weekStartDateTime->format("Y-n-j")] = array();
|
||||
$result["weekDays"][$weekStartDateTime->format("Y-n-j")]["dayOfMonth"] = $dateParse["day"];
|
||||
$result["weekDays"][$weekStartDateTime->format("Y-n-j")]["dayOfWeek"] = strtoupper(_(date("D", $weekStartDateTime->getTimestamp())));
|
||||
|
||||
// Shows scheduled for this day will get added to this array when
|
||||
// we convert the show times to the client's local timezone in weekly-program.phtml
|
||||
$result["weekDays"][$weekStartDateTime->format("Y-n-j")]["shows"] = array();
|
||||
|
||||
// $weekStartDateTime has to be in station timezone when adding 1 day for daylight savings.
|
||||
// TODO: is this necessary since we set the time to "00:00" ?
|
||||
$stationTimezone = Application_Model_Preference::GetDefaultTimezone();
|
||||
$weekStartDateTime->setTimezone(new DateTimeZone($stationTimezone));
|
||||
|
||||
$weekStartDateTime->add(new DateInterval('P1D'));
|
||||
|
||||
//convert back to UTC to get the actual timestamp used for search.
|
||||
$weekStartDateTime->setTimezone($utcTimezone);
|
||||
}
|
||||
|
||||
// Use this variable as the end date/time range when querying
|
||||
// for shows. We set it to 1 day after the end of the schedule
|
||||
// widget data to account for show date changes when converting
|
||||
// their start day/time to the client's local timezone.
|
||||
$showQueryDateRangeEnd = clone $weekStartDateTime;
|
||||
$showQueryDateRangeEnd->setTime(23, 59, 0);
|
||||
|
||||
$shows = Application_Model_Show::getNextShows(
|
||||
$showQueryDateRangeStart->format("Y-m-d H:i:s"),
|
||||
"ALL",
|
||||
$showQueryDateRangeEnd->format("Y-m-d H:i:s"));
|
||||
|
||||
// Convert each start and end time string to DateTime objects
|
||||
// so we can get a real timestamp. The timestamps will be used
|
||||
// to convert into javascript Date objects.
|
||||
foreach($shows as &$show) {
|
||||
$dtStarts = new DateTime($show["starts"], new DateTimeZone("UTC"));
|
||||
$show["starts_timestamp"] = $dtStarts->getTimestamp();
|
||||
|
||||
$dtEnds = new DateTime($show["ends"], new DateTimeZone("UTC"));
|
||||
$show["ends_timestamp"] = $dtEnds->getTimestamp();
|
||||
}
|
||||
$result["shows"] = $shows;
|
||||
|
||||
// convert image paths to point to api endpoints
|
||||
//TODO: do we need this here?
|
||||
self::findAndConvertPaths($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively find image_path keys in the various $result subarrays,
|
||||
* and convert them to point to the show-logo endpoint
|
||||
*
|
||||
* @param unknown $arr the array to search
|
||||
*/
|
||||
public static function findAndConvertPaths(&$arr)
|
||||
{
|
||||
$CC_CONFIG = Config::getConfig();
|
||||
$baseDir = Application_Common_OsPath::formatDirectoryWithDirectorySeparators($CC_CONFIG['baseDir']);
|
||||
|
||||
foreach ($arr as &$a) {
|
||||
if (is_array($a)) {
|
||||
if (array_key_exists("image_path", $a)) {
|
||||
$a["image_path"] = $a["image_path"] && $a["image_path"] !== '' ?
|
||||
Application_Common_HTTPHelper::getStationUrl()."api/show-logo?id=".$a["id"] : '';
|
||||
} else {
|
||||
self::findAndConvertPaths($a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
legacy/application/common/enum/HttpRequestType.php
Normal file
12
legacy/application/common/enum/HttpRequestType.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
final class HttpRequestType {
|
||||
|
||||
const GET = "GET";
|
||||
const POST = "POST";
|
||||
const PUT = "PUT";
|
||||
const DELETE = "DELETE";
|
||||
const PATCH = "PATCH";
|
||||
const OPTIONS = "OPTIONS";
|
||||
|
||||
}
|
17
legacy/application/common/enum/MediaType.php
Normal file
17
legacy/application/common/enum/MediaType.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
final class MediaType {
|
||||
|
||||
const __default = self::FILE;
|
||||
|
||||
const FILE = 1;
|
||||
const PLAYLIST = 2;
|
||||
const BLOCK = 3;
|
||||
const WEBSTREAM = 4;
|
||||
const PODCAST = 5;
|
||||
|
||||
public static function getDefault() {
|
||||
return static::__default;
|
||||
}
|
||||
|
||||
}
|
31
legacy/application/common/interface/OAuth2.php
Normal file
31
legacy/application/common/interface/OAuth2.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
interface OAuth2 {
|
||||
|
||||
/**
|
||||
* Check whether an OAuth access token exists
|
||||
*
|
||||
* @return bool true if an access token exists, otherwise false
|
||||
*/
|
||||
public function hasAccessToken();
|
||||
|
||||
/**
|
||||
* Get the OAuth authorization URL
|
||||
*
|
||||
* @return string the authorization URL
|
||||
*/
|
||||
public function getAuthorizeUrl();
|
||||
|
||||
/**
|
||||
* Request a new OAuth access token and store it in CcPref
|
||||
*
|
||||
* @param $code string exchange authorization code for access token
|
||||
*/
|
||||
public function requestNewAccessToken($code);
|
||||
|
||||
/**
|
||||
* Regenerate the OAuth access token
|
||||
*/
|
||||
public function accessTokenRefresh();
|
||||
|
||||
}
|
27
legacy/application/common/interface/OAuth2Controller.php
Normal file
27
legacy/application/common/interface/OAuth2Controller.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
interface OAuth2Controller {
|
||||
|
||||
/**
|
||||
* Send user to a third-party service to authorize before being redirected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function authorizeAction();
|
||||
|
||||
/**
|
||||
* Clear the previously saved request token from the preferences
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deauthorizeAction();
|
||||
|
||||
/**
|
||||
* Called when user successfully completes third-party authorization
|
||||
* Store the returned request token for future requests
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function redirectAction();
|
||||
|
||||
}
|
36
legacy/application/common/interface/Publish.php
Normal file
36
legacy/application/common/interface/Publish.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
interface Publish {
|
||||
|
||||
/**
|
||||
* Publish the file with the given file ID
|
||||
*
|
||||
* @param int $fileId ID of the file to be published
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function publish($fileId);
|
||||
|
||||
/**
|
||||
* Unpublish the file with the given file ID
|
||||
*
|
||||
* @param int $fileId ID of the file to be unpublished
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unpublish($fileId);
|
||||
|
||||
|
||||
/**
|
||||
* Fetch the publication status for the file with the given ID
|
||||
*
|
||||
* @param int $fileId the ID of the file to check
|
||||
*
|
||||
* @return int 1 if the file has been published,
|
||||
* 0 if the file has yet to be published,
|
||||
* -1 if the file is in a pending state,
|
||||
* 2 if the source is unreachable (disconnected)
|
||||
*/
|
||||
public function getPublishStatus($fileId);
|
||||
|
||||
}
|
30
legacy/application/common/widgets/Table.php
Normal file
30
legacy/application/common/widgets/Table.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: asantoni
|
||||
* Date: 11/09/15
|
||||
* Time: 2:47 PM
|
||||
*/
|
||||
|
||||
class AirtimeTableView {
|
||||
|
||||
private static function _getTableJavaScriptDependencies() {
|
||||
return ['js/airtime/widgets/table.js',
|
||||
'js/datatables/js/jquery.dataTables.js',
|
||||
'js/datatables/plugin/dataTables.pluginAPI.js',
|
||||
'js/datatables/plugin/dataTables.fnSetFilteringDelay.js',
|
||||
'js/datatables/plugin/dataTables.ColVis.js',
|
||||
'js/datatables/plugin/dataTables.colReorder.min.js?',
|
||||
'js/datatables/plugin/dataTables.FixedColumns.js',
|
||||
'js/datatables/plugin/dataTables.FixedHeader.js',
|
||||
'js/datatables/plugin/dataTables.columnFilter.js?'];
|
||||
}
|
||||
|
||||
public static function injectTableJavaScriptDependencies(&$headScript, $baseUrl, $airtimeVersion)
|
||||
{
|
||||
$deps = self::_getTableJavaScriptDependencies();
|
||||
for ($i = 0; $i < count($deps); $i++) {
|
||||
$headScript->appendFile($baseUrl . $deps[$i] .'?'. $airtimeVersion, 'text/javascript');
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue