Merge branch 'saas-dev' into saas-dev-publishing

Conflicts:
	airtime_mvc/application/controllers/plugins/PageLayoutInitPlugin.php
	airtime_mvc/public/css/dashboard.css
	airtime_mvc/public/js/airtime/library/spl.js
This commit is contained in:
Duncan Sommerville 2015-10-29 11:21:24 -04:00
commit 95aae317c6
26 changed files with 189 additions and 63 deletions

View File

@ -329,4 +329,49 @@ class Billing
$result = Billing::makeRequest($credentials["url"], $query_string);
}
public static function getInvoices()
{
Billing::ensureClientIdIsValid();
$credentials = Billing::getAPICredentials();
$postfields = array();
$postfields["username"] = $credentials["username"];
$postfields["password"] = md5($credentials["password"]);
$postfields["action"] = "getinvoices";
$postfields["responsetype"] = "json";
$postfields["userid"] = Application_Model_Preference::GetClientId();
$query_string = "";
foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&";
$result = Billing::makeRequest($credentials["url"], $query_string);
$invoices = array();
if ($result["invoices"]) {
$invoices = $result["invoices"]["invoice"];
}
return $invoices;
}
/**
* Checks if the customer has any unpaid invoices and if so, returns
* the ID of one of them. Returns 0 otherwise.
*/
public static function checkForUnpaidInvoice() {
$invoices = self::getInvoices();
$unpaidInvoice = 0;
$unpaidInvoices = 0;
foreach ($invoices as $invoice)
{
if ($invoice['status'] == 'Unpaid') {
$unpaidInvoices += 1;
$unpaidInvoice = $invoice;
}
}
if ($unpaidInvoices > 0) {
return $unpaidInvoice;
} else {
return 0;
}
}
}

View File

@ -21,6 +21,8 @@ class Application_Common_UsabilityHints
$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.
@ -36,6 +38,11 @@ class Application_Common_UsabilityHints
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.
@ -48,6 +55,10 @@ class Application_Common_UsabilityHints
if ($currentController == "plupload") {
$userIsOnAddMediaPage = true;
}
if ($currentController == 'showbuilder') {
$userIsOnShowbuilderPage = true;
}
}
if (self::zeroFilesUploaded()) {
@ -92,9 +103,15 @@ class Application_Common_UsabilityHints
"<a href=\"/schedule\">",
"</a>");
}
} else {
return "";
} else if ($userIsOnShowbuilderPage && $userIsSuperAdmin) {
$unpaidInvoice = Billing::checkForUnpaidInvoice();
if ($unpaidInvoice != null) {
$invoiceUrl = "/billing/invoice?invoiceid=" . $unpaidInvoice['id'];
$amount = $unpaidInvoice['currencyprefix'] . $unpaidInvoice['total'];
return _pro(sprintf("You have an unpaid invoice for %s due soon. <a href='%s'>Please pay it to keep your station on the air.</a>", $amount, $invoiceUrl));;
}
}
return "";
}
/**

View File

@ -110,7 +110,7 @@ $pages = array(
)
),
array(
'label' => "<i class='icon-briefcase icon-white'></i>"._('Billing'),
'label' => (Application_Model_Preference::GetPlanLevel()=="trial") ? "<i class='icon-star icon-orange'></i><span style='color: #ff5d1a'>"._('Upgrade')."</span>" : "<i class='icon-briefcase icon-white'></i>"._('Billing'),
'controller' => 'billing',
'action' => 'upgrade',
'resource' => 'billing',

View File

@ -24,7 +24,8 @@ class ApiController extends Zend_Controller_Action
"show-tracks",
"show-schedules",
"station-logo",
"show-logo"
"show-logo",
"stream-m3u"
);
if (Zend_Session::isStarted()) {
@ -1501,5 +1502,23 @@ class ApiController extends Zend_Controller_Action
$hint = Application_Common_UsabilityHints::getUsabilityHint($userPath);
$this->_helper->json->sendJson($hint);
}
public function streamM3uAction()
{
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
header('Content-Type: application/x-mpegurl');
header('Content-Disposition: attachment; filename=stream.m3u');
$m3uFile = "#EXTM3U\r\n\r\n"; //Windows linebreaks eh
$stationName = Application_Model_Preference::GetStationName();
$streamData = Application_Model_StreamSetting::getEnabledStreamData();
foreach ($streamData as $stream) {
$m3uFile .= "#EXTINF,".$stationName." - " . strtoupper($stream['codec']) . "\r\n";
$m3uFile .= $stream['url'] . "\r\n\r\n";
}
echo $m3uFile;
}
}

View File

@ -283,26 +283,7 @@ class BillingController extends Zend_Controller_Action {
$baseUrl = Application_Common_OsPath::getBaseDir();
$this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']);
Billing::ensureClientIdIsValid();
$credentials = Billing::getAPICredentials();
$postfields = array();
$postfields["username"] = $credentials["username"];
$postfields["password"] = md5($credentials["password"]);
$postfields["action"] = "getinvoices";
$postfields["responsetype"] = "json";
$postfields["userid"] = Application_Model_Preference::GetClientId();
$query_string = "";
foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&";
$result = Billing::makeRequest($credentials["url"], $query_string);
if ($result["invoices"]) {
$this->view->invoices = $result["invoices"]["invoice"];;
} else {
$this->view->invoices = array();
}
$this->view->invoices = Billing::getInvoices();
}
public function invoiceAction()
@ -312,6 +293,4 @@ class BillingController extends Zend_Controller_Action {
$invoice_id = $request->getParam('invoiceid');
self::viewInvoice($invoice_id);
}
}

View File

@ -29,6 +29,7 @@ class EmbedController extends Zend_Controller_Action
$this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/muses.swf";
$this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info";
$this->view->player_title = json_encode($this->view->escape($request->getParam('title')));
$this->view->jquery_i18n = Application_Common_HTTPHelper::getStationUrl() . "js/i18n/jquery.i18n.js?";
$styleParam = $request->getParam('style');
$player_style = isset($styleParam) ? $styleParam : "basic";

View File

@ -375,6 +375,9 @@ class LibraryController extends Zend_Controller_Action
if ($form->isValid($serialized)) {
$file->setDbColMetadata($serialized);
$this->view->status = true;
} else {
$this->view->status = false;
}
}

View File

@ -428,7 +428,12 @@ class LocaleController extends Zend_Controller_Action
": activate to sort column ascending",
": activate to sort column descending",
//End of datatables
"Welcome to the new Airtime Pro!" => _("Welcome to the new Airtime Pro!")
"Welcome to the new Airtime Pro!" => _("Welcome to the new Airtime Pro!"),
//embed player
"On Air" => _("On Air"),
"Off Air" => _("Off Air"),
"Offline" => _("Offline"),
"Nothing scheduled" => _("Nothing scheduled")
);
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);

View File

@ -188,7 +188,7 @@ class Application_Form_BillingClient extends Zend_Form
$passwordVerify->addValidator($notEmptyValidator);
$this->addElement($passwordVerify);
$this->addElement('hash', 'csrf', array(
$this->addElement('hash', 'csrf_client', array(
'salt' => 'unique'
));

View File

@ -3,12 +3,7 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form
{
public function init()
{
$csrf_namespace = new Zend_Session_Namespace('csrf_namespace');
$csrf_element = new Zend_Form_Element_Hidden('csrf');
$csrf_element->setValue($csrf_namespace->authtoken)->setRequired('true')->removeDecorator('HtmlTag')->removeDecorator('Label');
$this->addElement($csrf_element);
$this->addElement('hash', 'csrf', array(
$this->addElement('hash', 'csrf_upgrade', array( //Needs a unique ID (csrf_upgrade) so it doesn't conflict with other tokens in subforms
'salt' => 'unique'
));

View File

@ -179,6 +179,26 @@ class Application_Form_EditAudioMD extends Zend_Form
));
$this->addElement($language);
$validCuePattern = '/^(?:[0-9]{1,2}:)?(?:[0-9]{1,2}:)?[0-9]{1,6}(\.\d{1,6})?$/';
$cueIn = new Zend_Form_Element_Text('cuein');
$cueIn->class = 'input_text';
$cueIn->setLabel("Cue In:");
$cueInValidator = Application_Form_Helper_ValidationTypes::overrideRegexValidator(
$validCuePattern, _(sprintf("Specify cue in time in the format %s", "(hh:mm:)ss(.dddddd)"))
);
$cueIn->setValidators(array($cueInValidator));
$this->addElement($cueIn);
$cueOut = new Zend_Form_Element_Text('cueout');
$cueOut->class = 'input_text';
$cueOut->setLabel('Cue Out:');
$cueOutValidator = Application_Form_Helper_ValidationTypes::overrideRegexValidator(
$validCuePattern, _(sprintf("Specify cue out time in the format %s", "(hh:mm:)ss(.dddddd)"))
);
$cueOut->setValidators(array($cueOutValidator));
$this->addElement($cueOut);
// Add the submit button
$this->addElement('button', 'editmdsave', array(
'ignore' => true,

View File

@ -97,7 +97,7 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
<!--<div style="padding-bottom: 2px;">Disk Usage</div>-->
<div class="disk_usage_progress_bar"></div>
<div class="disk_usage_percent_in_use"><?php echo sprintf("%01.1fGB of %01.1fGB", $used/pow(2, 30), $total/pow(2, 30)); ?></div>
<div class="disk_usage_used" style="width:<?php echo sprintf("%01.1f%%", $used/$total*100) ?>;"></div>
<div class="disk_usage_used" style="width:<?php echo sprintf("%01.1f%%", min(100, $used/$total*100)) ?>;"></div>
<!--<div style="margin-top: 15px; font-size: 12px;">
<?php //echo sprintf("%01.1fGB of %01.1fGB", $used/pow(2, 30), $total/pow(2, 30)); ?>

View File

@ -12,10 +12,15 @@ class Application_Model_Datatables
if (strstr($term, '~')) {
$info = explode('~', $term);
if ($dbname == 'utime' || $dbname == 'mtime' || $dbname == 'lptime') {
$input1 = ($info[0] != "") ? Application_Common_DateHelper::UserTimezoneStringToUTCString($info[0]) : null;
$input2 = ($info[1] != "") ? Application_Common_DateHelper::UserTimezoneStringToUTCString($info[1]) : null;
try {
$input1 = ($info[0] != "") ? Application_Common_DateHelper::UserTimezoneStringToUTCString($info[0]) : null;
$input2 = ($info[1] != "") ? Application_Common_DateHelper::UserTimezoneStringToUTCString($info[1]) : null;
} catch (Exception $e) {
$input1 = null;
$input2 = null;
}
} else if($dbname == 'bit_rate' || $dbname == 'sample_rate') {
$input1 = isset($info[0])?doubleval($info[0]) * 1000:null;
$input2 = isset($info[1])?doubleval($info[1]) * 1000:null;

View File

@ -956,6 +956,10 @@ class Application_Model_Scheduler
$this->con->beginTransaction();
try {
//Increase the transaction isolation level to prevent two concurrent requests from potentially resulting
//in tracks scheduled at the same time.
$this->con->exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
$this->validateMediaItems($mediaItems); //Check for missing files, etc.
$this->validateRequest($scheduleItems, true);
@ -1006,6 +1010,9 @@ class Application_Model_Scheduler
//$this->con->useDebug(true);
try {
//Increase the transaction isolation level to prevent two concurrent requests from potentially resulting
//in tracks scheduled at the same time.
$this->con->exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
$this->validateItemMove($selectedItems, $afterItems[0]);
$this->validateRequest($selectedItems);

View File

@ -5,7 +5,7 @@ $topTextClass = "";
if (array_key_exists("planupdated", $_GET))
{
$topText = _pro("<b>Thank you!</b> Your plan has been updated and you will be invoiced during your next billing cycle.");
$topTextClass = "status-good";
$topTextClass = "invoice-status-good";
}
else {
$topText = _pro("Tip: To pay an invoice, click \"View Invoice\" and look for the \"Checkout\" button.");

View File

@ -274,7 +274,7 @@ echo($currentProduct["name"]);
<form id="<?php echo $form->getId(); ?>" method="<?php echo $form->getMethod() ?>" action="<?php echo
$form->getAction()?>" enctype="<?php echo $form->getEncType();?>">
<?php echo $form->csrf ?>
<?php echo $form->csrf_upgrade ?>
<div id="plantype">
<?php echo $form->newproductid ?>
@ -353,8 +353,9 @@ echo($currentProduct["name"]);
<div id="vaterror"></div>
</div>
<div class="clearfix"></div>
<div>
<?php echo $billingForm->csrf_client ?>
<div>
<div class="billing_checkbox">
<?=$billingForm->getElement("71")->renderViewHelper(); ?>
</div>
@ -379,7 +380,7 @@ echo($currentProduct["name"]);
<b>Total:</b> <span id="total"></span>
</div>
<input type="submit" class="btn right-floated" value="Submit Order" id="atpro_submitorder"></input>
<input type="submit" class="btn right-floated" value="Submit Order" id="atpro_submitorder">
<div class="clearfix"></div>
</form>
</div>

View File

@ -5,8 +5,12 @@
<link rel="stylesheet" href="<?php echo $this->css?>" type="text/css">
<script src="<?php echo $this->mrp_js?>" type="text/javascript"></script>
<script src="<?php echo $this->jquery?>" type="text/javascript"></script>
<script src="<?php echo $this->jquery_i18n?>" type="text/javascript"></script>
<script src="/locale/general-translation-table" type="text/javascript"></script>
<link href='https://fonts.googleapis.com/css?family=Roboto:400,100,300,700' rel='stylesheet' type='text/css'>
<script type="text/javascript">
$.i18n.setDictionary(general_dict);
var RETRY_DELAY_MSECS = 2000; //Delay before trying to reconnect to stream after an error.
var MAX_MOBILE_SCREEN_WIDTH = 760;
@ -309,9 +313,9 @@
if (data.current === null) {
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("On Air:" + "<span>" + data.currentShow[0].name + "</span>");
$("p.now_playing").html($.i18n._("On Air") + "<span>" + data.currentShow[0].name + "</span>");
} else {
$("p.now_playing").html("Off Air" + "<span>Offline</span>");
$("p.now_playing").html($.i18n._("Off Air") + "<span>"+ $.i18n._("Offline") + "</span>");
}
time_to_next_track_starts = 20000;
} else {
@ -340,7 +344,7 @@
}
if (data.next === null) {
$("ul.schedule_list").find("li").html("Nothing scheduled");
$("ul.schedule_list").find("li").html($.i18n._("Nothing scheduled"));
} else {
$("ul.schedule_list").find("li").html(data.next.name);
}
@ -404,7 +408,7 @@
<div style="clear:both"></div>
<div class="airtime_schedule">
<p class="airtime_next">Next</p>
<p class="airtime_next"><?php echo _("Next") ?></p>
<ul class="schedule_list">
<li></li>
</ul>

View File

@ -9,8 +9,8 @@
</div>
<div id="player_instructions">
Customize the player by configuring the options below. When you are done,
copy the embeddable code below and paste it into your website's HTML.
<?php echo _("Customize the player by configuring the options below. When you are done,
copy the embeddable code below and paste it into your website's HTML.") ?>
</div>
<?php echo $this->element->getElement('player_title')->render(); ?>

View File

@ -47,7 +47,7 @@
<div style="padding-bottom: 2px;">Storage</div>
<div class="disk_usage_progress_bar"></div>
<div class="disk_usage_percent_in_use"><?php echo sprintf("%01.1f%% ", $used/$total*100) . _("in use") ?></div>
<div class="disk_usage_used" style="width:<?php echo sprintf("%01.1f%%", $used/$total*100) ?>;"></div>
<div class="disk_usage_used" style="width:<?php echo sprintf("%01.1f%%", min(100, $used/$total*100)) ?>;"></div>
<div style="margin-top: 17px; font-size: 12px;"><?php echo sprintf("%01.1fGB of %01.1fGB", $used/pow(2, 30), $total/pow(2, 30)); ?></div>
</div>

View File

@ -30,6 +30,10 @@
overflow-x: hidden;
}
#recent_uploads_table_wrapper > .dataTables_scrolling {
border: 0;
}
#recent_uploads_table_wrapper > .fg-toolbar
{
position: absolute;
@ -56,10 +60,6 @@ table#recent_uploads_table td
margin-top: 5px;
margin-right: 3px;
}
#recent_uploads_table_length
{
margin: 5px;
}
/** Vertically align radio buttons with the labels */
#recent_uploads_filter input[type='radio'] {

View File

@ -225,6 +225,14 @@
color: #ff0000;
}
.invoice-status-good {
background: #e3ffc9 url(images/stream_status.png) no-repeat 10px 10px;
border-color: #54b300;
padding: 2px 12px 4px 32px;
margin: 2px 1px 10px 0px;
color: #330;
}
/** This form is the separate one on the Billing Account Details page (BillingClient.php) */
#clientdetails_form {
width: 500px;

View File

@ -94,7 +94,7 @@
}
.dataTables_length {
padding: 6px;
padding: 5px 6px;
}
.dataTables_length label {
@ -426,6 +426,7 @@ textarea {
font-size: 14px;
margin: 0 0 10px;
max-width: 280px;
}
/* Playlist/Block/Webstream Editors */

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -8,6 +8,15 @@
float: left;
}
#edit-md-dialog ul.errors {
clear: both;
float: none;
}
#edit-md-dialog dd input.input_text {
width: auto;
}
.spl_sortable,.spl_sortable>li,.side_playlist>div,#spl_editor,.spl_artist,.spl_cue_in,.spl_fade_in,.spl_cue_out,.spl_fade_out
{
clear: left;

View File

@ -3781,6 +3781,11 @@ hr {
margin-right: 4px;
}
.icon-orange {
margin-right: 4px;
background-image: url("img/glyphicons-halflings-orange.png");
}
.nav-ui-icon-formatter {
width:16px !important;
display:inline;
@ -3815,10 +3820,8 @@ hr {
}
.disk_usage_progress_bar {
position: absolute;
left: 4px;
right: 4px;
/* width: 118px; */
float: left;
width: 100%;
height: 13px;
background: #444444;
background: -moz-linear-gradient(top, #464646 0, #3e3e3e 100%);
@ -3844,7 +3847,7 @@ hr {
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ff6f01), color-stop(100%, #bc5200));
height:13px;
z-index: 2;
position:absolute;
position: relative;
}
.dropdown-menu li > a:hover, .dropdown-menu li > a:focus, .dropdown-submenu:hover > a
@ -4002,4 +4005,4 @@ li .ui-state-hover {
.published-sources > .checked-icon {
height: 26px;
padding-right: 8px;
}
}

View File

@ -2,6 +2,10 @@
function isAudioSupported(mime){
var audio = new Audio();
if ((typeof mime) !== "string" || (mime === null)) {
return false;
}
var bMime = null;
if (mime.indexOf("ogg") != -1 || mime.indexOf("vorbis") != -1) {
bMime = 'audio/ogg; codecs="vorbis"';