Merge branch 'cc-5709-airtime-analyzer' into cc-5709-airtime-analyzer-cloud-storage

This commit is contained in:
Albert Santoni 2014-07-16 15:03:39 -04:00
commit c09457ce7c
30 changed files with 11878 additions and 105 deletions

22
INSTALL
View File

@ -1,22 +0,0 @@
Airtime is the open radio software for scheduling and remote station management.
Home page: http://airtime.sourcefabric.org/
Installation instructions are here:
http://wiki.sourcefabric.org/x/BQBF
Here is the manual:
http://en.flossmanuals.net/airtime/
To report bugs, visit our bug tracker at:
http://dev.sourcefabric.org/browse/CC
Visit our community support forum here:
http://forum.sourcefabric.org/index.php/f/14/
For commercial support, see:
http://sourcefabric.org/en/services/about/347/Support.htm
or send an e-mail to contact@sourcefabric.org
If you are a developer and want to hack on Airtime, go here:
http://wiki.sourcefabric.org/display/CC

View File

@ -14,6 +14,7 @@ require_once "DateHelper.php";
require_once "OsPath.php";
require_once "Database.php";
require_once "Timezone.php";
require_once "Auth.php";
require_once __DIR__.'/forms/helpers/ValidationTypes.php';
require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php';
require_once __DIR__.'/controllers/plugins/Maintenance.php';
@ -26,6 +27,8 @@ require_once __DIR__."/configs/navigation.php";
Zend_Validate::setDefaultNamespaces("Zend");
Application_Model_Auth::pinSessionToClient(Zend_Auth::getInstance());
$front = Zend_Controller_Front::getInstance();
$front->registerPlugin(new RabbitMqPlugin());

View File

@ -14,9 +14,10 @@ class LoginController extends Zend_Controller_Action
$request = $this->getRequest();
Application_Model_Locale::configureLocalization($request->getcookie('airtime_locale', 'en_CA'));
if (Zend_Auth::getInstance()->hasIdentity())
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity())
{
$this->_redirect('Showbuilder');
}
@ -52,8 +53,7 @@ class LoginController extends Zend_Controller_Action
//pass to the adapter the submitted username and password
$authAdapter->setIdentity($username)
->setCredential($password);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($authAdapter);
if ($result->isValid()) {
//all info about this user from the login table omit only the password
@ -65,15 +65,13 @@ class LoginController extends Zend_Controller_Action
Application_Model_LoginAttempts::resetAttempts($_SERVER['REMOTE_ADDR']);
Application_Model_Subjects::resetLoginAttempts($username);
$tempSess = new Zend_Session_Namespace("referrer");
$tempSess->referrer = 'login';
//set the user locale in case user changed it in when logging in
Application_Model_Preference::SetUserLocale($locale);
$this->_redirect('Showbuilder');
} else {
$message = _("Wrong username or password provided. Please try again.");
Application_Model_Subjects::increaseLoginAttempts($username);
Application_Model_LoginAttempts::increaseAttempts($_SERVER['REMOTE_ADDR']);
@ -96,7 +94,8 @@ class LoginController extends Zend_Controller_Action
public function logoutAction()
{
Zend_Auth::getInstance()->clearIdentity();
$auth = Zend_Auth::getInstance();
$auth->clearIdentity();
$this->_redirect('showbuilder/index');
}

View File

@ -109,6 +109,7 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$controller = strtolower($request->getControllerName());
Application_Model_Auth::pinSessionToClient(Zend_Auth::getInstance());
//Ignore authentication for all access to the rest API. We do auth via API keys for this
//and/or by OAuth.

View File

@ -13,8 +13,8 @@
<?php echo $this->layout()->content ?>
</div>
<div class="footer">
<?php echo sprintf(_("Airtime Copyright &copy;Sourcefabric o.p.s. All rights reserved.%s"
."Maintained and distributed under GNU GPL v.3 by %sSourcefabric o.p.s%s"),
<?php echo sprintf(_("Airtime copyright &copy; Sourcefabric z.ú. All rights reserved.%s"
."Maintained and distributed under the GNU GPL v.3 by %sSourcefabric z.ú.%s"),
"<br>", "<a href='http://www.sourcefabric.org'>" ,"</a>");?>
</div>

View File

@ -110,4 +110,18 @@ class Application_Model_Auth
return $string;
}
/** It is essential to do this before interacting with Zend_Auth otherwise sessions could be shared between
* different copies of Airtime on the same webserver. This essentially pins this session to:
* - The server hostname - including subdomain so we segment multiple Airtime installs on different subdomains
* - The remote IP of the browser - to help prevent session hijacking
* - The client ID - same reason as server hostname
* @param Zend_Auth $auth Get this with Zend_Auth::getInstance().
*/
public static function pinSessionToClient($auth)
{
$serverName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : "";
$remoteAddr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "";
$auth->setStorage(new Zend_Auth_Storage_Session('Airtime' . $serverName . $remoteAddr . Application_Model_Preference::GetClientId()));
}
}

View File

@ -545,7 +545,11 @@ class Application_Model_Preference
// Returns station default timezone (from preferences)
public static function GetDefaultTimezone()
{
return self::getValue("timezone");
$stationTimezone = self::getValue("timezone");
if (is_null($stationTimezone) || $stationTimezone == "") {
$stationTimezone = "UTC";
}
return $stationTimezone;
}
public static function SetUserTimezone($timezone = null)
@ -1313,7 +1317,12 @@ class Application_Model_Preference
}
$ds = unserialize($v);
if (is_null($ds) || !is_array($ds)) {
return $id;
}
if (!array_key_exists('ColReorder', $ds)) {
return $id;
}

View File

@ -303,10 +303,10 @@ SQL;
$p_start_str = $p_start->format("Y-m-d H:i:s");
$p_end_str = $p_end->format("Y-m-d H:i:s");
//We need to search 24 hours before and after the show times so that that we
//We need to search 48 hours before and after the show times so that that we
//capture all of the show's contents.
$p_track_start= $p_start->sub(new DateInterval("PT24H"))->format("Y-m-d H:i:s");
$p_track_end = $p_end->add(new DateInterval("PT24H"))->format("Y-m-d H:i:s");
$p_track_start= $p_start->sub(new DateInterval("PT48H"))->format("Y-m-d H:i:s");
$p_track_end = $p_end->add(new DateInterval("PT48H"))->format("Y-m-d H:i:s");
$templateSql = <<<SQL
SELECT DISTINCT sched.starts AS sched_starts,
@ -738,13 +738,16 @@ SQL;
$replay_gain = is_null($item["replay_gain"]) ? "0": $item["replay_gain"];
$replay_gain += Application_Model_Preference::getReplayGainModifier();
if ( !Application_Model_Preference::GetEnableReplayGain() ) {
if (!Application_Model_Preference::GetEnableReplayGain() ) {
$replay_gain = 0;
}
$fileMetadata = CcFiles::sanitizeResponse(CcFilesQuery::create()->findPk($media_id));
$schedule_item = array(
'id' => $media_id,
'type' => 'file',
'metadata' => $fileMetadata,
'row_id' => $item["id"],
'uri' => $uri,
'fade_in' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_in"]),

View File

@ -13,6 +13,14 @@
*/
class CcFiles extends BaseCcFiles {
//fields we should never expose through our RESTful API
private static $privateFields = array(
'file_exists',
'silan_check',
'is_scheduled',
'is_playlist'
);
public function getCueLength()
{
$cuein = $this->getDbCuein();
@ -46,4 +54,20 @@ class CcFiles extends BaseCcFiles {
$this->save();
}
/**
*
* Strips out the private fields we do not want to send back in API responses
* @param $file a CcFiles object
*/
//TODO: rename this function?
public static function sanitizeResponse($file)
{
$response = $file->toArray(BasePeer::TYPE_FIELDNAME);
foreach (self::$privateFields as $key) {
unset($response[$key]);
}
return $response;
}
} // CcFiles

View File

@ -4,7 +4,7 @@
class Rest_MediaController extends Zend_Rest_Controller
{
//fields that are not modifiable via our RESTful API
private $blackList = array(
private static $blackList = array(
'id',
'directory',
'filepath',
@ -18,14 +18,6 @@ class Rest_MediaController extends Zend_Rest_Controller
'is_playlist'
);
//fields we should never expose through our RESTful API
private $privateFields = array(
'file_exists',
'silan_check',
'is_scheduled',
'is_playlist'
);
public function init()
{
$this->view->layout()->disableLayout();
@ -44,7 +36,7 @@ class Rest_MediaController extends Zend_Rest_Controller
$files_array = array();
foreach (CcFilesQuery::create()->find() as $file)
{
array_push($files_array, $this->sanitizeResponse($file));
array_push($files_array, CcFiles::sanitizeResponse($file));
}
$this->getResponse()
@ -137,7 +129,7 @@ class Rest_MediaController extends Zend_Rest_Controller
$this->getResponse()
->setHttpResponseCode(200)
->appendBody(json_encode($this->sanitizeResponse($file)));
->appendBody(json_encode(CcFiles::sanitizeResponse($file)));
} else {
$this->fileNotFoundResponse();
}
@ -204,7 +196,7 @@ class Rest_MediaController extends Zend_Rest_Controller
$this->getResponse()
->setHttpResponseCode(201)
->appendBody(json_encode($this->sanitizeResponse($file)));
->appendBody(json_encode(CcFiles::sanitizeResponse($file)));
}
}
@ -254,7 +246,7 @@ class Rest_MediaController extends Zend_Rest_Controller
$this->getResponse()
->setHttpResponseCode(200)
->appendBody(json_encode($this->sanitizeResponse($file)));
->appendBody(json_encode(CcFiles::sanitizeResponse($file)));
} else {
$file->setDbImportStatus(2)->save();
$this->fileNotFoundResponse();
@ -428,7 +420,7 @@ class Rest_MediaController extends Zend_Rest_Controller
}
if (!$fileForm->isValidPartial($whiteList)) {
throw Exception("Data validation failed");
throw new Exception("Data validation failed");
}
} catch (Exception $e) {
$errors = $fileForm->getErrors();
@ -509,30 +501,15 @@ class Rest_MediaController extends Zend_Rest_Controller
* from outside of Airtime
* @param array $data
*/
private function removeBlacklistedFieldsFromRequestData($data)
private static function removeBlacklistedFieldsFromRequestData($data)
{
foreach ($this->blackList as $key) {
foreach (self::$blackList as $key) {
unset($data[$key]);
}
return $data;
}
/**
*
* Strips out the private fields we do not want to send back in API responses
*/
//TODO: rename this function?
public function sanitizeResponse($file)
{
$response = $file->toArray(BasePeer::TYPE_FIELDNAME);
foreach ($this->privateFields as $key) {
unset($response[$key]);
}
return $response;
}
private function removeEmptySubFolders($path)
{

View File

@ -85,32 +85,31 @@ class Application_Service_CalendarService
$currentShow = Application_Model_Show::getCurrentShow();
$currentShowId = count($currentShow) == 1 ? $currentShow[0]["id"] : null;
$showIsLinked = $this->ccShow->isLinked();
if ($now < $end && ($isAdminOrPM || $isHostOfShow) && !$this->ccShowInstance->isRecorded()) {
//if the show is not linked the user can add/remove content
if (!$showIsLinked) {
$menu["schedule"] = array(
"name"=> _("Add / Remove Content"),
"icon" => "add-remove-content",
"url" => $baseUrl."showbuilder/builder-dialog/");
//if the show is linked and it's not currently playing the user can add/remove content
} elseif ($showIsLinked && $currentShowId != $this->ccShow->getDbId()) {
//user can add/remove content if the show has not ended
if ($now < $end && ($isAdminOrPM || $isHostOfShow) && !$this->ccShowInstance->isRecorded()) {
//if the show is not linked OR if the show is linked AND not the current playing show
//the user can add/remove content
if (!$showIsLinked || ($showIsLinked && $currentShowId != $this->ccShow->getDbId())) {
$menu["schedule"] = array(
"name"=> _("Add / Remove Content"),
"icon" => "add-remove-content",
"url" => $baseUrl."showbuilder/builder-dialog/");
}
}
if ($now < $start && ($isAdminOrPM || $isHostOfShow) &&
!$this->ccShowInstance->isRecorded() ) {
$menu["clear"] = array(
"name"=> _("Remove All Content"),
"icon" => "remove-all-content",
"url" => $baseUrl."schedule/clear-show");
//user can remove all content if the show has not started
if ($now < $start && ($isAdminOrPM || $isHostOfShow) && !$this->ccShowInstance->isRecorded() ) {
//if the show is not linked OR if the show is linked AND not the current playing show
//the user can remove all content
if (!$showIsLinked || ($showIsLinked && $currentShowId != $this->ccShow->getDbId())) {
$menu["clear"] = array(
"name"=> _("Remove All Content"),
"icon" => "remove-all-content",
"url" => $baseUrl."schedule/clear-show");
}
}
//"Show Content" should be a menu item at all times except when

View File

@ -289,7 +289,15 @@ class Application_Service_ShowService
if ($this->ccShow->isRepeating()) {
$ccShowDays = $this->ccShow->getRepeatingCcShowDays();
} else {
$ccShowDays = $this->ccShow->getCcShowDayss();
//$ccShowDays = $this->ccShow->getCcShowDayss();
/* Cannot use the above statement to get the cc_show_days
* object because it's getting the old object before the
* show was edited. clearInstancePool() didn't work.
*/
$ccShowDays = CcShowDaysQuery::create()
->filterByDbShowId($this->ccShow->getDbId())
->find();
}
}
@ -1039,6 +1047,9 @@ SQL;
);
$origStartDateTime->setTimezone(new DateTimeZone("UTC"));
$ccShowInstance = $this->getInstance($origStartDateTime);
if (!$ccShowInstance) {
throw new Exception("Could not find show instance with start time: ".$origStartDateTime->format("Y-m-d H:i:s"));
}
}
$ccShowInstance->setDbShowId($this->ccShow->getDbId());

View File

@ -10,7 +10,7 @@ echo sprintf(_("%sAirtime%s %s, the open radio software for scheduling and remot
?>
<br>© 2013
<?php
echo sprintf(_("%sSourcefabric%s o.p.s. Airtime is distributed under the %sGNU GPL v.3%s"),
echo sprintf(_("%sSourcefabric%s z.ú Airtime is distributed under the %sGNU GPL v.3%s"),
"<a href='http://www.sourcefabric.org' target='_blank'>",
"</a>",
"<a href='http://www.gnu.org/licenses/gpl-3.0-standalone.html' target='_blank'>",

View File

@ -4,7 +4,7 @@
<div id="login" class="login-content clearfix">
<?php if(isset($this->demo) && $this->demo == 1){?>
<p style="font-weight:bold">
<?php echo _("Welcome to the online Airtime demo! You can log in using the username 'admin' and the password 'admin'.")?>
<?php echo _("Welcome to the Airtime demo! You can log in using the username 'admin' and the password 'admin'.")?>
</p>
<?php }?>
<p class="light" style='<?php echo $this->error?"color:#902d2d;font-size:11px;padding:2px 4px;background:#c6b4b4;margin-bottom:2px;border:1px solid #c83f3f;":""?>'><?php echo $this->message; ?></p>

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -109,7 +109,6 @@ class TestHelper
//Create all the database tables
AirtimeInstall::createDatabaseTables($dbuser, $dbpasswd, $dbname, $dbhost);
}
AirtimeInstall::SetDefaultTimezone();
}
public static function setupZendBootstrap()

View File

@ -178,7 +178,7 @@ class AirtimeInstall
echo "* Giving Apache permission to access $rp".PHP_EOL;
$success = chgrp($rp, $CC_CONFIG["webServerUser"]);
$success = chmod($rp, 02775);
$success = chmod($rp, 0775);
}
}

View File

@ -104,7 +104,7 @@ def test_double_duplicate_files():
@raises(OSError)
def test_bad_permissions_destination_dir():
filename = os.path.basename(DEFAULT_AUDIO_FILE)
dest_dir = u'/var/foobar'
dest_dir = u'/sys/foobar' # /sys is using sysfs on Linux, which is unwritable
FileMoverAnalyzer.move(DEFAULT_AUDIO_FILE, dest_dir, filename, dict())
#Move the file back
shutil.move(os.path.join(dest_dir, filename), DEFAULT_AUDIO_FILE)

View File

@ -283,11 +283,11 @@ def organized_path(old_path, root_path, orig_md):
title_re = re.match(r, normal_md['MDATA_KEY_TITLE'])
show_name = title_re.group('show')
#date = title_re.group('date')
yyyy, mm, _ = normal_md['MDATA_KEY_YEAR'].split('-',2)
yyyy, mm, dd = normal_md['MDATA_KEY_YEAR'].split('-',2)
fname_base = '%s-%s-%s.%s' % \
(title_re.group('time'), show_name,
normal_md['MDATA_KEY_BITRATE'], ext)
filepath = os.path.join(root_path, yyyy, mm, fname_base)
filepath = os.path.join(root_path, yyyy, mm, dd, fname_base)
elif len(normal_md['MDATA_KEY_TRACKNUMBER']) == 0:
fname = u'%s-%s.%s' % (normal_md['MDATA_KEY_TITLE'],
normal_md['MDATA_KEY_BITRATE'], ext)

View File

@ -44,5 +44,8 @@ while not successful:
logging.error("traceback: %s", traceback.format_exc())
sys.exit(1)
else:
logging.error(str(e))
logging.error("traceback: %s", traceback.format_exc())
logging.info("Retrying in 3 seconds...")
time.sleep(3)
attempts += 1

View File

@ -3,17 +3,31 @@ from timeout import ls_timeout
def create_liquidsoap_annotation(media):
# We need liq_start_next value in the annotate. That is the value that controls overlap duration of crossfade.
return ('annotate:media_id="%s",liq_start_next="0",liq_fade_in="%s",' + \
filename = media['dst']
annotation = ('annotate:media_id="%s",liq_start_next="0",liq_fade_in="%s",' + \
'liq_fade_out="%s",liq_cue_in="%s",liq_cue_out="%s",' + \
'schedule_table_id="%s",replay_gain="%s dB":%s') % \
(media['id'],
float(media['fade_in']) / 1000,
float(media['fade_out']) / 1000,
float(media['cue_in']),
float(media['cue_out']),
media['row_id'],
media['replay_gain'],
media['dst'])
'schedule_table_id="%s",replay_gain="%s dB"') % \
(media['id'],
float(media['fade_in']) / 1000,
float(media['fade_out']) / 1000,
float(media['cue_in']),
float(media['cue_out']),
media['row_id'],
media['replay_gain'])
# Override the the artist/title that Liquidsoap extracts from a file's metadata
# with the metadata we get from Airtime. (You can modify metadata in Airtime's library,
# which doesn't get saved back to the file.)
if 'metadata' in media:
if 'artist_name' in media['metadata']:
annotation += ',artist="%s"' % (media['metadata']['artist_name'])
if 'track_title' in media['metadata']:
annotation += ',title="%s"' % (media['metadata']['track_title'])
annotation += ":" + filename
return annotation
class TelnetLiquidsoap: