Merge branch 'saas-dev' into saas-speedy

This commit is contained in:
Duncan Sommerville 2015-05-04 13:09:51 -04:00
commit 8b380086c3
32 changed files with 4981 additions and 1287 deletions

View file

@ -78,9 +78,9 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
$view->headScript()->appendScript("var baseUrl = '$baseUrl';"); $view->headScript()->appendScript("var baseUrl = '$baseUrl';");
$this->_initTranslationGlobals($view); $this->_initTranslationGlobals($view);
$user = Application_Model_User::GetCurrentUser(); $user = Application_Model_User::GetCurrentUser();
if (!is_null($user)){ if (!is_null($user)) {
$userType = $user->getType(); $userType = $user->getType();
} else { } else {
$userType = ""; $userType = "";
@ -91,14 +91,15 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
/** /**
* Create a global namespace to hold a session token for CSRF prevention * Create a global namespace to hold a session token for CSRF prevention
*/ */
protected function _initCsrfNamespace() { protected function _initCsrfNamespace()
{
$csrf_namespace = new Zend_Session_Namespace('csrf_namespace'); $csrf_namespace = new Zend_Session_Namespace('csrf_namespace');
// Check if the token exists // Check if the token exists
if (!$csrf_namespace->authtoken) { if (!$csrf_namespace->authtoken) {
// If we don't have a token, regenerate it and set a 2 hour timeout // If we don't have a token, regenerate it and set a 2 hour timeout
// Should we log the user out here if the token is expired? // Should we log the user out here if the token is expired?
$csrf_namespace->authtoken = sha1(uniqid(rand(),1)); $csrf_namespace->authtoken = sha1(uniqid(rand(), 1));
$csrf_namespace->setExpirationSeconds(2*60*60); $csrf_namespace->setExpirationSeconds(2 * 60 * 60);
} }
//Here we are closing the session for writing because otherwise no requests //Here we are closing the session for writing because otherwise no requests
@ -106,13 +107,14 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
//of the application (page load times are more consistent, no lock contention). //of the application (page load times are more consistent, no lock contention).
session_write_close(); session_write_close();
} }
/** /**
* Ideally, globals should be written to a single js file once * Ideally, globals should be written to a single js file once
* from a php init function. This will save us from having to * from a php init function. This will save us from having to
* reinitialize them every request * reinitialize them every request
*/ */
private function _initTranslationGlobals() { private function _initTranslationGlobals()
{
$view = $this->getResource('view'); $view = $this->getResource('view');
$view->headScript()->appendScript("var PRODUCT_NAME = '" . PRODUCT_NAME . "';"); $view->headScript()->appendScript("var PRODUCT_NAME = '" . PRODUCT_NAME . "';");
$view->headScript()->appendScript("var USER_MANUAL_URL = '" . USER_MANUAL_URL . "';"); $view->headScript()->appendScript("var USER_MANUAL_URL = '" . USER_MANUAL_URL . "';");
@ -127,13 +129,13 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
$baseUrl = Application_Common_OsPath::getBaseDir(); $baseUrl = Application_Common_OsPath::getBaseDir();
$view->headLink()->appendStylesheet($baseUrl.'css/bootstrap.css?'.$CC_CONFIG['airtime_version']); $view->headLink()->appendStylesheet($baseUrl . 'css/bootstrap.css?' . $CC_CONFIG['airtime_version']);
$view->headLink()->appendStylesheet($baseUrl.'css/redmond/jquery-ui-1.8.8.custom.css?'.$CC_CONFIG['airtime_version']); $view->headLink()->appendStylesheet($baseUrl . 'css/redmond/jquery-ui-1.8.8.custom.css?' . $CC_CONFIG['airtime_version']);
$view->headLink()->appendStylesheet($baseUrl.'css/pro_dropdown_3.css?'.$CC_CONFIG['airtime_version']); $view->headLink()->appendStylesheet($baseUrl . 'css/pro_dropdown_3.css?' . $CC_CONFIG['airtime_version']);
$view->headLink()->appendStylesheet($baseUrl.'css/qtip/jquery.qtip.min.css?'.$CC_CONFIG['airtime_version']); $view->headLink()->appendStylesheet($baseUrl . 'css/qtip/jquery.qtip.min.css?' . $CC_CONFIG['airtime_version']);
$view->headLink()->appendStylesheet($baseUrl.'css/styles.css?'.$CC_CONFIG['airtime_version']); $view->headLink()->appendStylesheet($baseUrl . 'css/styles.css?' . $CC_CONFIG['airtime_version']);
$view->headLink()->appendStylesheet($baseUrl.'css/masterpanel.css?'.$CC_CONFIG['airtime_version']); $view->headLink()->appendStylesheet($baseUrl . 'css/masterpanel.css?' . $CC_CONFIG['airtime_version']);
$view->headLink()->appendStylesheet($baseUrl.'css/tipsy/jquery.tipsy.css?'.$CC_CONFIG['airtime_version']); $view->headLink()->appendStylesheet($baseUrl . 'css/tipsy/jquery.tipsy.css?' . $CC_CONFIG['airtime_version']);
} }
protected function _initHeadScript() protected function _initHeadScript()
@ -144,72 +146,73 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
$baseUrl = Application_Common_OsPath::getBaseDir(); $baseUrl = Application_Common_OsPath::getBaseDir();
$view->headScript()->appendFile($baseUrl.'js/libs/jquery-1.8.3.min.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/libs/jquery-1.8.3.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/libs/jquery-ui-1.8.24.min.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/libs/jquery-ui-1.8.24.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/bootstrap/bootstrap.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/bootstrap/bootstrap.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/libs/underscore-min.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/libs/underscore-min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/libs/jquery.stickyPanel.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); // $view->headScript()->appendFile($baseUrl . 'js/libs/jquery.stickyPanel.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/qtip/jquery.qtip.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/qtip/jquery.qtip.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/jplayer/jquery.jplayer.min.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/jplayer/jquery.jplayer.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/sprintf/sprintf-0.7-beta1.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/sprintf/sprintf-0.7-beta1.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/cookie/jquery.cookie.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/cookie/jquery.cookie.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/i18n/jquery.i18n.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/i18n/jquery.i18n.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'locale/general-translation-table?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'locale/general-translation-table?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl.'locale/datatables-translation-table?'.$CC_CONFIG['airtime_version'],'text/javascript'); $view->headScript()->appendFile($baseUrl . 'locale/datatables-translation-table?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendScript("$.i18n.setDictionary(general_dict)"); $view->headScript()->appendScript("$.i18n.setDictionary(general_dict)");
$view->headScript()->appendScript("var baseUrl='$baseUrl'"); $view->headScript()->appendScript("var baseUrl='$baseUrl'");
//These timezones are needed to adjust javascript Date objects on the client to make sense to the user's set timezone //These timezones are needed to adjust javascript Date objects on the client to make sense to the user's set timezone
//or the server's set timezone. //or the server's set timezone.
$serverTimeZone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone()); $serverTimeZone = new DateTimeZone(Application_Model_Preference::GetDefaultTimezone());
$now = new DateTime("now", $serverTimeZone); $now = new DateTime("now", $serverTimeZone);
$offset = $now->format("Z") * -1; $offset = $now->format("Z") * -1;
$view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds"); $view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds");
if (class_exists("Zend_Auth", false) && Zend_Auth::getInstance()->hasIdentity()) {
$userTimeZone = new DateTimeZone(Application_Model_Preference::GetUserTimezone());
$now = new DateTime("now", $userTimeZone);
$offset = $now->format("Z") * -1;
$view->headScript()->appendScript("var userTimezoneOffset = {$offset}; //in seconds");
}
//scripts for now playing bar
$view->headScript()->appendFile($baseUrl.'js/airtime/airtime_bootstrap.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/airtime/dashboard/helperfunctions.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/airtime/dashboard/dashboard.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/airtime/dashboard/versiontooltip.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/tipsy/jquery.tipsy.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$view->headScript()->appendFile($baseUrl.'js/airtime/common/common.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); if (class_exists("Zend_Auth", false) && Zend_Auth::getInstance()->hasIdentity()) {
$view->headScript()->appendFile($baseUrl.'js/airtime/common/audioplaytest.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $userTimeZone = new DateTimeZone(Application_Model_Preference::GetUserTimezone());
$now = new DateTime("now", $userTimeZone);
$offset = $now->format("Z") * -1;
$view->headScript()->appendScript("var userTimezoneOffset = {$offset}; //in seconds");
}
//scripts for now playing bar
$view->headScript()->appendFile($baseUrl . 'js/airtime/airtime_bootstrap.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl . 'js/airtime/dashboard/helperfunctions.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl . 'js/airtime/dashboard/dashboard.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl . 'js/airtime/dashboard/versiontooltip.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl . 'js/tipsy/jquery.tipsy.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl . 'js/airtime/common/common.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$view->headScript()->appendFile($baseUrl . 'js/airtime/common/audioplaytest.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$user = Application_Model_User::getCurrentUser(); $user = Application_Model_User::getCurrentUser();
if (!is_null($user)){ if (!is_null($user)) {
$userType = $user->getType(); $userType = $user->getType();
} else { } else {
$userType = ""; $userType = "";
} }
$view->headScript()->appendScript("var userType = '$userType';"); $view->headScript()->appendScript("var userType = '$userType';");
if (array_key_exists('REQUEST_URI', $_SERVER)) { //Doesn't exist for unit tests if (array_key_exists('REQUEST_URI', $_SERVER)) { //Doesn't exist for unit tests
if (strpos($_SERVER['REQUEST_URI'], $baseUrl.'Dashboard/stream-player') === false if (strpos($_SERVER['REQUEST_URI'], $baseUrl . 'Dashboard/stream-player') === false
&& strpos($_SERVER['REQUEST_URI'], $baseUrl.'audiopreview/audio-preview') === false && strpos($_SERVER['REQUEST_URI'], $baseUrl . 'audiopreview/audio-preview') === false
&& strpos($_SERVER['REQUEST_URI'], $baseUrl.'audiopreview/playlist-preview') === false && strpos($_SERVER['REQUEST_URI'], $baseUrl . 'audiopreview/playlist-preview') === false
&& strpos($_SERVER['REQUEST_URI'], $baseUrl.'audiopreview/block-preview') === false) { && strpos($_SERVER['REQUEST_URI'], $baseUrl . 'audiopreview/block-preview') === false
) {
$plan_level = strval(Application_Model_Preference::GetPlanLevel()); $plan_level = strval(Application_Model_Preference::GetPlanLevel());
// Since the Hobbyist plan doesn't come with Live Chat support, don't enable it // Since the Hobbyist plan doesn't come with Live Chat support, don't enable it
if (Application_Model_Preference::GetLiveChatEnabled() && $plan_level !== 'hobbyist') { if (Application_Model_Preference::GetLiveChatEnabled() && $plan_level !== 'hobbyist') {
$client_id = strval(Application_Model_Preference::GetClientId()); $client_id = strval(Application_Model_Preference::GetClientId());
$station_url = $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; $station_url = $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
$view->headScript()->appendScript("var livechat_client_id = '$client_id';\n". $view->headScript()->appendScript("var livechat_client_id = '$client_id';\n" .
"var livechat_plan_type = '$plan_level';\n". "var livechat_plan_type = '$plan_level';\n" .
"var livechat_station_url = 'http://$station_url';"); "var livechat_station_url = 'http://$station_url';");
$view->headScript()->appendFile($baseUrl . 'js/airtime/common/livechat.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $view->headScript()->appendFile($baseUrl . 'js/airtime/common/livechat.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
} }
} }
} }
/* /*
@ -222,6 +225,8 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{ {
$view = $this->getResource('view'); $view = $this->getResource('view');
$view->addHelperPath('../application/views/helpers', 'Airtime_View_Helper'); $view->addHelperPath('../application/views/helpers', 'Airtime_View_Helper');
$view->assign('suspended', (Application_Model_Preference::getProvisioningStatus() == PROVISIONING_STATUS_SUSPENDED));
} }
protected function _initTitle() protected function _initTitle()
@ -260,7 +265,7 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
$front = Zend_Controller_Front::getInstance(); $front = Zend_Controller_Front::getInstance();
$router = $front->getRouter(); $router = $front->getRouter();
$front->setBaseUrl(Application_Common_OsPath::getBaseDir()); $front->setBaseUrl(Application_Common_OsPath::getBaseDir());
$router->addRoute( $router->addRoute(
'password-change', 'password-change',
new Zend_Controller_Router_Route('password-change/:user_id/:token', array( new Zend_Controller_Router_Route('password-change/:user_id/:token', array(

View file

@ -11,7 +11,7 @@ define('COMPANY_SITE_URL' , 'http://sourcefabric.org/');
define('WHOS_USING_URL' , 'http://sourcefabric.org/en/airtime/whosusing'); define('WHOS_USING_URL' , 'http://sourcefabric.org/en/airtime/whosusing');
define('TERMS_AND_CONDITIONS_URL' , 'http://www.sourcefabric.org/en/about/policy/'); define('TERMS_AND_CONDITIONS_URL' , 'http://www.sourcefabric.org/en/about/policy/');
define('PRIVACY_POLICY_URL' , 'http://www.sourcefabric.org/en/about/policy/'); define('PRIVACY_POLICY_URL' , 'http://www.sourcefabric.org/en/about/policy/');
define('USER_MANUAL_URL' , 'http://sourcefabric.booktype.pro/airtime-25-for-broadcasters/'); define('USER_MANUAL_URL' , 'http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters');
define('LICENSE_VERSION' , 'GNU AGPL v.3'); define('LICENSE_VERSION' , 'GNU AGPL v.3');
define('LICENSE_URL' , 'http://www.gnu.org/licenses/agpl-3.0-standalone.html'); define('LICENSE_URL' , 'http://www.gnu.org/licenses/agpl-3.0-standalone.html');
@ -87,4 +87,8 @@ define("WHMCS_API_URL", "https://account.sourcefabric.com/includes/api.php");
define("SUBDOMAIN_WHMCS_CUSTOM_FIELD_NAME", "Choose your domain"); define("SUBDOMAIN_WHMCS_CUSTOM_FIELD_NAME", "Choose your domain");
//Sentry error logging //Sentry error logging
define('SENTRY_CONFIG_PATH', '/etc/airtime-saas/sentry.airtime_web.ini'); define('SENTRY_CONFIG_PATH', '/etc/airtime-saas/sentry.airtime_web.ini');
//Provisioning status
define('PROVISIONING_STATUS_SUSPENDED' , 'Suspended');
define('PROVISIONING_STATUS_ACTIVE' , 'Active');

View file

@ -134,7 +134,7 @@ $pages = array(
), ),
array( array(
'label' => _('User Manual'), 'label' => _('User Manual'),
'uri' => "http://sourcefabric.booktype.pro/airtime-25-for-broadcasters/", 'uri' => "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters",
'target' => "_blank" 'target' => "_blank"
), ),
array( array(

View file

@ -13,7 +13,6 @@ class DashboardController extends Zend_Controller_Action
public function indexAction() public function indexAction()
{ {
// action body
} }
public function disconnectSourceAction() public function disconnectSourceAction()

View file

@ -32,12 +32,15 @@ class ProvisioningController extends Zend_Controller_Action
try { try {
// This is hacky and should be genericized // This is hacky and should be genericized
if ($_POST['station_name']) { if (isset($_POST['station_name'])) {
Application_Model_Preference::SetStationName($_POST['station_name']); Application_Model_Preference::SetStationName($_POST['station_name']);
} }
if ($_POST['description']) { if (isset($_POST['description'])) {
Application_Model_Preference::SetStationDescription($_POST['description']); Application_Model_Preference::SetStationDescription($_POST['description']);
} }
if (isset($_POST['provisioning_status'])) {
Application_Model_Preference::setProvisioningStatus($_POST['provisioning_status']);
}
} catch (Exception $e) { } catch (Exception $e) {
$this->getResponse() $this->getResponse()
->setHttpResponseCode(400) ->setHttpResponseCode(400)

View file

@ -20,7 +20,12 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
<!-- End Google Tag Manager --> <!-- End Google Tag Manager -->
<?php echo $this->partial('partialviews/trialBox.phtml', array("is_trial"=>$this->isTrial(), "trial_remain"=> $this->trialRemaining())) ?> <?php echo $this->partial('partialviews/trialBox.phtml', array("is_trial"=>$this->isTrial(), "trial_remain"=> $this->trialRemaining())) ?>
<div id="Panel">
<div id="Panel" class="sticky">
<?php if($this->suspended) : ?>
<?php echo $this->partial('partialviews/suspended.phtml'); ?>
<?php else : ?>
<?php echo $this->versionNotify(); <?php echo $this->versionNotify();
$sss = $this->SourceSwitchStatus(); $sss = $this->SourceSwitchStatus();
$scs = $this->SourceConnectionStatus(); $scs = $this->SourceConnectionStatus();
@ -61,8 +66,10 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
<?php echo $this->navigation()->menu() ?> <?php echo $this->navigation()->menu() ?>
<div style="clear:both;"></div> <div style="clear:both;"></div>
</div> </div>
<?php endif; //suspended ?>
</div> </div>
<div class="wrapper" id="content"><?php echo $this->layout()->content ?></div> <div class="wrapper" id="content"><?php echo $this->layout()->content ?></div>
<script id="tmpl-pl-cues" type="text/template"> <script id="tmpl-pl-cues" type="text/template">

View file

@ -1441,4 +1441,16 @@ class Application_Model_Preference
self::setDiskUsage($currentDiskUsage + $filesize); self::setDiskUsage($currentDiskUsage + $filesize);
} }
public static function setProvisioningStatus($status)
{
//See constants.php for the list of valid values. eg. PROVISIONING_STATUS_ACTIVE
self::setValue("provisioning_status", $status);
}
public static function getProvisioningStatus()
{
return self::getValue("provisioning_status");
}
} }

View file

@ -64,13 +64,6 @@ SQL;
{ {
//Everything in this function must be done in UTC. You will get a swift kick in the pants if you mess that up. //Everything in this function must be done in UTC. You will get a swift kick in the pants if you mess that up.
if (!is_int($p_prev) || !is_int($p_next)) {
//must enter integers to specify ranges
Logging::info("Invalid range parameters: $p_prev or $p_next");
return array();
}
// when timeEnd is unspecified, return to the default behaviour - set a range of 48 hours from current time // when timeEnd is unspecified, return to the default behaviour - set a range of 48 hours from current time
if (!$utcTimeEnd) { if (!$utcTimeEnd) {
$end = new DateTime(); $end = new DateTime();

View file

@ -150,23 +150,27 @@ class Application_Service_CalendarService
$menu["edit"] = array( $menu["edit"] = array(
"name" => _("Edit This Instance"), "name" => _("Edit This Instance"),
"icon" => "edit", "icon" => "edit",
"url" => $baseUrl."Schedule/populate-repeating-show-instance-form"); "url" => $baseUrl . "Schedule/populate-repeating-show-instance-form"
);
} else { } else {
$menu["edit"] = array( $menu["edit"] = array(
"name" => _("Edit"), "name" => _("Edit"),
"icon" => "edit", "icon" => "edit",
"items" => array()); "items" => array()
);
$menu["edit"]["items"]["all"] = array( $menu["edit"]["items"]["all"] = array(
"name" => _("Edit Show"), "name" => _("Edit Show"),
"icon" => "edit", "icon" => "edit",
"url" => $baseUrl."Schedule/populate-show-form"); "url" => $baseUrl . "Schedule/populate-show-form"
);
$menu["edit"]["items"]["instance"] = array( $menu["edit"]["items"]["instance"] = array(
"name" => _("Edit This Instance"), "name" => _("Edit This Instance"),
"icon" => "edit", "icon" => "edit",
"url" => $baseUrl."Schedule/populate-repeating-show-instance-form"); "url" => $baseUrl . "Schedule/populate-repeating-show-instance-form"
} );
}
} else { } else {
$menu["edit"] = array( $menu["edit"] = array(
"name"=> _("Edit Show"), "name"=> _("Edit Show"),

View file

@ -153,14 +153,19 @@ class Application_Service_ShowFormService
if ($ccShowDay->isShowStartInPast()) { if ($ccShowDay->isShowStartInPast()) {
//for a non-repeating show, we should never allow user to change the start time. //for a non-repeating show, we should never allow user to change the start time.
//for a repeating show, we should allow because the form works as repeating template form //for a repeating show, we should allow because the form works as repeating template form
if (!$ccShowDay->isRepeating()) { $form->disableStartDateAndTime();
// Removing this - if there is no future instance, this will throw an error.
// If there is a future instance, then we get a WHEN block representing the next instance
// which may be confusing.
/*if (!$ccShowDay->isRepeating()) {
$form->disableStartDateAndTime(); $form->disableStartDateAndTime();
} else { } else {
list($showStart, $showEnd) = $this->getNextFutureRepeatShowTime(); list($showStart, $showEnd) = $this->getNextFutureRepeatShowTime();
if ($this->hasShowStarted($showStart)) { if ($this->hasShowStarted($showStart)) {
$form->disableStartDateAndTime(); $form->disableStartDateAndTime();
} }
} }*/
} }
$form->populate( $form->populate(
@ -410,9 +415,8 @@ class Application_Service_ShowFormService
//if the show is repeating, set the start date to the next //if the show is repeating, set the start date to the next
//repeating instance in the future //repeating instance in the future
if ($this->ccShow->isRepeating()) { $originalShowStartDateTime = $this->getCurrentOrNextInstanceStartTime();
list($originalShowStartDateTime,) = $this->getNextFutureRepeatShowTime(); if (!$originalShowStartDateTime) {
} else {
$originalShowStartDateTime = $dt; $originalShowStartDateTime = $dt;
} }
@ -421,26 +425,30 @@ class Application_Service_ShowFormService
/** /**
* *
* Returns 2 DateTime objects, in the user's local time, * Returns a DateTime object, in the user's local time,
* of the next future repeat show instance start and end time * of the current or next show instance start time
*
* Returns null if there is no next future repeating show instance
*/ */
public function getNextFutureRepeatShowTime() public function getCurrentOrNextInstanceStartTime()
{ {
$ccShowInstance = CcShowInstancesQuery::create() $ccShowInstance = CcShowInstancesQuery::create()
->filterByDbShowId($this->ccShow->getDbId()) ->filterByDbShowId($this->ccShow->getDbId())
->filterByDbModifiedInstance(false) ->filterByDbModifiedInstance(false)
->filterByDbStarts(gmdate("Y-m-d H:i:s"), Criteria::GREATER_THAN) ->filterByDbStarts(gmdate("Y-m-d"), Criteria::GREATER_EQUAL)
->orderByDbStarts() ->orderByDbStarts()
->findOne(); ->findOne();
if (!$ccShowInstance) {
return null;
}
$starts = new DateTime($ccShowInstance->getDbStarts(), new DateTimeZone("UTC")); $starts = new DateTime($ccShowInstance->getDbStarts(), new DateTimeZone("UTC"));
$ends = new DateTime($ccShowInstance->getDbEnds(), new DateTimeZone("UTC"));
$showTimezone = $this->ccShow->getFirstCcShowDay()->getDbTimezone(); $showTimezone = $this->ccShow->getFirstCcShowDay()->getDbTimezone();
$starts->setTimezone(new DateTimeZone($showTimezone)); $starts->setTimezone(new DateTimeZone($showTimezone));
$ends->setTimezone(new DateTimeZone($showTimezone));
return array($starts, $ends); return $starts;
} }

View file

@ -238,8 +238,8 @@
} }
// variables for updating the player's metadata // variables for updating the player's metadata
var time_to_next_track_starts; var time_to_next_track_starts = 0;
var metadataTimer; var metadataTimer = null;
// Fetches the streams metadata from the Airtime live-info API // Fetches the streams metadata from the Airtime live-info API
// and attaches it to the player UI. // and attaches it to the player UI.
@ -252,7 +252,12 @@
success: function(data) { success: function(data) {
if (data.current === null) { if (data.current === null) {
$("p.now_playing").html("Offline"); if (data.currentShow != null && data.currentShow.length != 0) {
// Master/show source have no current track but they do have a current show.
$("p.now_playing").html(data.currentShow[0].name);
} else {
$("p.now_playing").html("Offline");
}
time_to_next_track_starts = 20000; time_to_next_track_starts = 20000;
} else { } else {
var artist = data.current.name.split(" - ")[0]; var artist = data.current.name.split(" - ")[0];
@ -260,6 +265,12 @@
$("p.now_playing").html(artist + "<span>" + track + "</span>"); $("p.now_playing").html(artist + "<span>" + track + "</span>");
var current_track_end_time = new Date(data.current.ends); var current_track_end_time = new Date(data.current.ends);
if (current_track_end_time == "Invalid Date" || isNaN(current_track_end_time)) {
// If the conversion didn't work (since the String is not in ISO format)
// then change it to be ISO-compliant. This is somewhat hacky and may break
// if the date string format in live-info changes!
current_track_end_time = new Date((data.current.ends).replace(" ", "T"));
}
var current_time = new Date(); var current_time = new Date();
//convert current_time to UTC to match the timezone of time_to_next_track_starts //convert current_time to UTC to match the timezone of time_to_next_track_starts
current_time = new Date(current_time.getTime() + current_time.getTimezoneOffset() * 60 * 1000); current_time = new Date(current_time.getTime() + current_time.getTimezoneOffset() * 60 * 1000);
@ -271,9 +282,14 @@
} else { } else {
$("ul.schedule_list").find("li").html(data.next.name); $("ul.schedule_list").find("li").html(data.next.name);
} }
} }
}); });
//Preventative code if the local and remote clocks are out of sync.
if (isNaN(time_to_next_track_starts) || time_to_next_track_starts < 0) {
time_to_next_track_starts = 0;
}
// Add 3 seconds to the timeout so Airtime has time to update the metadata before we fetch it // Add 3 seconds to the timeout so Airtime has time to update the metadata before we fetch it
metadataTimer = setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts+3000); metadataTimer = setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts+3000);
} }

View file

@ -23,6 +23,30 @@
<?php //echo $this->element->getElement('player_display_track_metadata'); ?> <?php //echo $this->element->getElement('player_display_track_metadata'); ?>
<table id="player_compatibility_chart">
<th colspan="5">Stream Compatibility</th>
<tr>
<td colspan="3">Desktop</td>
<td colspan="2">Mobile</td>
</tr>
<tr>
<td>Firefox</td>
<td>Chrome</td>
<td>Internet Explorer</td>
<td>Android 5 - Chrome</td>
<td>iOS 7 - Safari</td>
</tr>
<tr>
<td>Yes (Flash)</td>
<td>Yes (Flash)</td>
<td>Yes (Flash)</td>
<td>MP3/OGG* only</td>
<td>MP3/AAC only</td>
</tr>
<tr>
<td colspan="5">* Chrome on Android is known to take about 4 seconds to buffer a 128 kbps MP3 stream. Lower bitrates take longer to buffer.</td>
</tr>
</table>
</dl> </dl>

View file

@ -0,0 +1,9 @@
<div class="suspension_notice">
<H2>Station Suspended</H2>
<p>
<?php echo(_pro(sprintf('Your station is suspended due to an <b>unpaid invoice</b>. To restore your station, <a href="%s">please pay any overdue invoices</a>.', '/billing/invoices'))); ?>
</p>
<p>
<?php echo(_pro(sprintf('Suspended stations will be <b>removed</b> if an invoice is unpaid for 30 days. If you believe this suspension was in error, <a href="%s">please contact support</a>.', 'https://sourcefabricberlin.zendesk.com/anonymous_requests/new'))); ?>
</p>
</div>

View file

@ -5,7 +5,10 @@
</style> </style>
<?php if ($this->quotaLimitReached) { ?> <?php if ($this->quotaLimitReached) { ?>
<div class="errors quota-reached"> <div class="errors quota-reached">
Disk quota exceeded. You cannot upload files until you <a href="http://www.sourcefabric.org/en/airtime" target="_blank">upgrade your storage</a>. Disk quota exceeded. You cannot upload files until you
<a target="_parent" href=<?php $baseUrl = Application_Common_OsPath::getBaseDir(); echo $baseUrl . "billing/upgrade"?>>
upgrade your storage
</a>.
</div> </div>
<?php <?php
} }

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

@ -59,4 +59,26 @@
} }
table#player_compatibility_chart{
width: 100%;
border-collapse: collapse;
margin-bottom: 5px;
color: #3b3b3b;
}
#player_compatibility_chart th, tr, td {
border-bottom: 1px solid #999;
border-right: 1px solid #999 !important;
background-color: #ccc;
padding: 10px;
}
#player_compatibility_chart th
{
color: #5b5b5b;
border: 0px;
}

View file

@ -262,6 +262,9 @@ table.datatable tr.sb-header.odd:hover td, table.datatable tr.sb-header.even:hov
margin: 0; margin: 0;
padding: 10px 0 0 0; padding: 10px 0 0 0;
overflow: hidden; overflow: hidden;
top: 0;
left: 0;
right: 0;
} }
.ui-dialog .lib_content { .ui-dialog .lib_content {
@ -281,4 +284,4 @@ table.datatable tr.sb-header.odd:hover td, table.datatable tr.sb-header.even:hov
.ui-dialog .sb_content .padded { .ui-dialog .sb_content .padded {
padding: 5px 10px 5px 16px; padding: 5px 10px 5px 16px;
} }

View file

@ -141,8 +141,22 @@ select {
* html .clearfix, * html li { height: 1%;} * html .clearfix, * html li { height: 1%;}
.clearfix, #side_playlist li { display: block; } .clearfix, #side_playlist li { display: block; }
/* Master Panel */ /* Master Panel */
.sticky {
position: fixed;
width: 100%;
left: 0;
top: 0;
z-index: 1000;
border-top: 0;
}
.push {
width: 100%;
height: 139px;
}
#sticky { #sticky {
position:fixed; position:fixed;
height:130px; height:130px;
@ -150,7 +164,6 @@ select {
left:0; left:0;
} }
#master-panel { #master-panel {
background:#3d3d3d url(images/masterpanel_bg.png) repeat-x 0 0; background:#3d3d3d url(images/masterpanel_bg.png) repeat-x 0 0;
height:100px; height:100px;
@ -427,7 +440,10 @@ select {
.wrapper { .wrapper {
margin: 0 5px 0 5px; position: absolute;
top: 141px;
left: 10px;
right: 10px;
padding:10px 0 0 0; padding:10px 0 0 0;
} }
@ -1182,10 +1198,10 @@ input[type="checkbox"] {
left:0; left:0;
margin-bottom:140px; margin-bottom:140px;
}*/ }*/
.sticky { /*.sticky {*/
padding:0; /*padding:0;*/
width:100%; /*width:100%;*/
} /*}*/
.floated-panel { .floated-panel {
margin-top:0; margin-top:0;
@ -3217,3 +3233,18 @@ dd .stream-status {
{ {
color: #222; color: #222;
} }
.suspension_notice
{
text-align: center;
background-color: #D27A7A;
height: 143px;
font-size: 16px;
font-family: Helvetica, Arial, sans-serif;
}
.suspension_notice h2
{
padding-bottom: 0px;
padding-top: 13px;
}

View file

@ -1,11 +1,11 @@
$(document).ready(function() { $(document).ready(function() {
$("#Panel").stickyPanel({ /* Removed as this is now (hopefully) unnecessary */
topPadding: 1, //$("#Panel").stickyPanel({
afterDetachCSSClass: "floated-panel", // topPadding: 1,
savePanelSpace: true // afterDetachCSSClass: "floated-panel",
}); // savePanelSpace: true
//});
//this statement tells the browser to fade out any success message after 5 seconds //this statement tells the browser to fade out any success message after 5 seconds
setTimeout(function(){$(".success").fadeOut("slow", function(){$(this).empty()});}, 5000); setTimeout(function(){$(".success").fadeOut("slow", function(){$(this).empty()});}, 5000);
@ -52,8 +52,8 @@ var i18n_days_short = [
$.i18n._("We"), $.i18n._("We"),
$.i18n._("Th"), $.i18n._("Th"),
$.i18n._("Fr"), $.i18n._("Fr"),
$.i18n._("Sa"), $.i18n._("Sa")
] ];
function adjustDateToServerDate(date, serverTimezoneOffset){ function adjustDateToServerDate(date, serverTimezoneOffset){
//date object stores time in the browser's localtime. We need to artificially shift //date object stores time in the browser's localtime. We need to artificially shift

View file

@ -1445,7 +1445,7 @@ var AIRTIME = (function(AIRTIME){
initialEvents(); initialEvents();
setUpPlaylist(); setUpPlaylist();
}; };
function setWidgetSize() { function setWidgetSize() {
viewport = AIRTIME.utilities.findViewportDimensions(); viewport = AIRTIME.utilities.findViewportDimensions();
widgetHeight = viewport.height - 185; widgetHeight = viewport.height - 185;
@ -1458,7 +1458,7 @@ var AIRTIME = (function(AIRTIME){
.find(".dataTables_scrolling") .find(".dataTables_scrolling")
.css("max-height", libTableHeight) .css("max-height", libTableHeight)
.end() .end()
.width(Math.floor(width * 0.55)); .width(Math.floor(width * 0.54));
$pl.height(widgetHeight) $pl.height(widgetHeight)
.width(Math.floor(width * 0.45)); .width(Math.floor(width * 0.45));

View file

@ -53,8 +53,8 @@ AIRTIME = (function(AIRTIME) {
function setWidgetSize() { function setWidgetSize() {
viewport = AIRTIME.utilities.findViewportDimensions(); viewport = AIRTIME.utilities.findViewportDimensions();
widgetHeight = viewport.height - 180; widgetHeight = viewport.height - 180;
screenWidth = Math.floor(viewport.width - 40); screenWidth = Math.floor(viewport.width - 50);
var libTableHeight = widgetHeight - 175, var libTableHeight = widgetHeight - 175,
builderTableHeight = widgetHeight - 95, builderTableHeight = widgetHeight - 95,
oTable; oTable;
@ -77,9 +77,9 @@ AIRTIME = (function(AIRTIME) {
if ($lib.filter(':visible').length > 0) { if ($lib.filter(':visible').length > 0) {
$lib.width(Math.floor(screenWidth * 0.48)); $lib.width(Math.floor(screenWidth * 0.47));
$builder.width(Math.floor(screenWidth * 0.48)) $builder.width(Math.floor(screenWidth * 0.47))
.find("#sb_edit") .find("#sb_edit")
.remove() .remove()
.end() .end()

View file

@ -60,6 +60,9 @@ class AirtimeAnalyzerServer:
#Disable most pika/rabbitmq logging: #Disable most pika/rabbitmq logging:
pika_logger = logging.getLogger('pika') pika_logger = logging.getLogger('pika')
pika_logger.setLevel(logging.CRITICAL) pika_logger.setLevel(logging.CRITICAL)
boto_logger = logging.getLogger('auth')
boto_logger.setLevel(logging.CRITICAL)
# Set up logging # Set up logging
logFormatter = logging.Formatter("%(asctime)s [%(module)s] [%(levelname)-5.5s] %(message)s") logFormatter = logging.Formatter("%(asctime)s [%(module)s] [%(levelname)-5.5s] %(message)s")