Merge branch 'saas-dev' of https://github.com/sourcefabric/Airtime into saas-dev

This commit is contained in:
drigato 2015-10-21 11:01:20 -04:00
commit b98f573531
14 changed files with 111 additions and 53 deletions

View File

@ -329,4 +329,49 @@ class Billing
$result = Billing::makeRequest($credentials["url"], $query_string); $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; $userIsOnCalendarPage = false;
$userIsOnAddMediaPage = false; $userIsOnAddMediaPage = false;
$userIsOnShowbuilderPage = false;
$userIsSuperAdmin = Application_Model_User::getCurrentUser()->isSuperAdmin();
// If $userPath is set the request came from AJAX so the user's // If $userPath is set the request came from AJAX so the user's
// current location inside Airtime gets passed in to this function. // current location inside Airtime gets passed in to this function.
@ -36,6 +38,11 @@ class Application_Common_UsabilityHints
if (strpos(strtolower($userPath), 'schedule') !== false) { if (strpos(strtolower($userPath), 'schedule') !== false) {
$userIsOnCalendarPage = true; $userIsOnCalendarPage = true;
} }
if (strpos(strtolower($userPath), 'showbuilder') !== false) {
$userIsOnShowbuilderPage = true;
}
} else { } else {
// If $userPath is not set the request came from inside Airtime so // 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. // 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") { if ($currentController == "plupload") {
$userIsOnAddMediaPage = true; $userIsOnAddMediaPage = true;
} }
if ($currentController == 'showbuilder') {
$userIsOnShowbuilderPage = true;
}
} }
if (self::zeroFilesUploaded()) { if (self::zeroFilesUploaded()) {
@ -92,9 +103,15 @@ class Application_Common_UsabilityHints
"<a href=\"/schedule\">", "<a href=\"/schedule\">",
"</a>"); "</a>");
} }
} else { } else if ($userIsOnShowbuilderPage && $userIsSuperAdmin) {
return ""; $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

@ -283,26 +283,7 @@ class BillingController extends Zend_Controller_Action {
$baseUrl = Application_Common_OsPath::getBaseDir(); $baseUrl = Application_Common_OsPath::getBaseDir();
$this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']);
Billing::ensureClientIdIsValid(); $this->view->invoices = Billing::getInvoices();
$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();
}
} }
public function invoiceAction() public function invoiceAction()
@ -312,6 +293,4 @@ class BillingController extends Zend_Controller_Action {
$invoice_id = $request->getParam('invoiceid'); $invoice_id = $request->getParam('invoiceid');
self::viewInvoice($invoice_id); self::viewInvoice($invoice_id);
} }
} }

View File

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

View File

@ -3,12 +3,7 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form
{ {
public function init() public function init()
{ {
$csrf_namespace = new Zend_Session_Namespace('csrf_namespace'); $this->addElement('hash', 'csrf_upgrade', array( //Needs a unique ID (csrf_upgrade) so it doesn't conflict with other tokens in subforms
$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(
'salt' => 'unique' 'salt' => 'unique'
)); ));

View File

@ -99,7 +99,7 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
<!--<div style="padding-bottom: 2px;">Disk Usage</div>--> <!--<div style="padding-bottom: 2px;">Disk Usage</div>-->
<div class="disk_usage_progress_bar"></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_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;"> <!--<div style="margin-top: 15px; font-size: 12px;">
<?php //echo sprintf("%01.1fGB of %01.1fGB", $used/pow(2, 30), $total/pow(2, 30)); ?> <?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, '~')) { if (strstr($term, '~')) {
$info = explode('~', $term); $info = explode('~', $term);
if ($dbname == 'utime' || $dbname == 'mtime' || $dbname == 'lptime') { if ($dbname == 'utime' || $dbname == 'mtime' || $dbname == 'lptime') {
$input1 = ($info[0] != "") ? Application_Common_DateHelper::UserTimezoneStringToUTCString($info[0]) : null; try {
$input2 = ($info[1] != "") ? Application_Common_DateHelper::UserTimezoneStringToUTCString($info[1]) : null; $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') { } else if($dbname == 'bit_rate' || $dbname == 'sample_rate') {
$input1 = isset($info[0])?doubleval($info[0]) * 1000:null; $input1 = isset($info[0])?doubleval($info[0]) * 1000:null;
$input2 = isset($info[1])?doubleval($info[1]) * 1000:null; $input2 = isset($info[1])?doubleval($info[1]) * 1000:null;

View File

@ -955,6 +955,10 @@ class Application_Model_Scheduler
$this->con->beginTransaction(); $this->con->beginTransaction();
try { 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->validateMediaItems($mediaItems); //Check for missing files, etc.
$this->validateRequest($scheduleItems, true); $this->validateRequest($scheduleItems, true);
@ -1005,6 +1009,9 @@ class Application_Model_Scheduler
//$this->con->useDebug(true); //$this->con->useDebug(true);
try { 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->validateItemMove($selectedItems, $afterItems[0]);
$this->validateRequest($selectedItems); $this->validateRequest($selectedItems);

View File

@ -850,6 +850,9 @@ SQL;
$con = Propel::getConnection(CcPrefPeer::DATABASE_NAME); $con = Propel::getConnection(CcPrefPeer::DATABASE_NAME);
try { try {
$con->beginTransaction(); $con->beginTransaction();
//It is extremely important that we increase the transaction isolation level, so that if two
//requests cause the show schedule to be generated at the same time, one will be rolled back.
$con->exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
//UTC DateTime object //UTC DateTime object
$showsPopUntil = Application_Model_Preference::GetShowsPopulatedUntil(); $showsPopUntil = Application_Model_Preference::GetShowsPopulatedUntil();
@ -862,7 +865,9 @@ SQL;
$con->commit(); $con->commit();
} catch (Exception $e) { } catch (Exception $e) {
$con->rollBack(); $con->rollBack();
throw $e; //throw $e;
Logging::warn("Did not create show instances due to transaction error. This is usually safe
and caused by two concurrent transactions. " . $e->getMessage());
} }
} }

View File

@ -5,7 +5,7 @@ $topTextClass = "";
if (array_key_exists("planupdated", $_GET)) 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."); $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 { else {
$topText = _pro("Tip: To pay an invoice, click \"View Invoice\" and look for the \"Checkout\" button."); $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 id="<?php echo $form->getId(); ?>" method="<?php echo $form->getMethod() ?>" action="<?php echo
$form->getAction()?>" enctype="<?php echo $form->getEncType();?>"> $form->getAction()?>" enctype="<?php echo $form->getEncType();?>">
<?php echo $form->csrf ?> <?php echo $form->csrf_upgrade ?>
<div id="plantype"> <div id="plantype">
<?php echo $form->newproductid ?> <?php echo $form->newproductid ?>
@ -353,8 +353,9 @@ echo($currentProduct["name"]);
<div id="vaterror"></div> <div id="vaterror"></div>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
<?php echo $billingForm->csrf_client ?>
<div>
<div>
<div class="billing_checkbox"> <div class="billing_checkbox">
<?=$billingForm->getElement("71")->renderViewHelper(); ?> <?=$billingForm->getElement("71")->renderViewHelper(); ?>
</div> </div>
@ -379,7 +380,7 @@ echo($currentProduct["name"]);
<b>Total:</b> <span id="total"></span> <b>Total:</b> <span id="total"></span>
</div> </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> <div class="clearfix"></div>
</form> </form>
</div> </div>

View File

@ -47,7 +47,7 @@
<div style="padding-bottom: 2px;">Storage</div> <div style="padding-bottom: 2px;">Storage</div>
<div class="disk_usage_progress_bar"></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_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 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> </div>

View File

@ -225,6 +225,14 @@
color: #ff0000; 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) */ /** This form is the separate one on the Billing Account Details page (BillingClient.php) */
#clientdetails_form { #clientdetails_form {
width: 500px; width: 500px;

View File

@ -288,15 +288,13 @@ select {
color:#ff5d1a; color:#ff5d1a;
} }
.now-playing-block { .now-playing-block {
/*width:35%;*/ flex: 1 0;
flex: 1 auto;
background: url(images/masterpanel_spacer.png) no-repeat 0 0; background: url(images/masterpanel_spacer.png) no-repeat 0 0;
margin-left: 152px; margin-left: 152px;
padding-left: 14px; padding-left: 14px;
} }
.show-block { .show-block {
/*width:30%;*/ flex: 1 0;
flex: 1 auto;
} }
.text-row { .text-row {
height:30px; height:30px;
@ -3817,10 +3815,8 @@ hr {
} }
.disk_usage_progress_bar { .disk_usage_progress_bar {
position: absolute; float: left;
left: 4px; width: 100%;
right: 4px;
/* width: 118px; */
height: 13px; height: 13px;
background: #444444; background: #444444;
background: -moz-linear-gradient(top, #464646 0, #3e3e3e 100%); background: -moz-linear-gradient(top, #464646 0, #3e3e3e 100%);
@ -3846,7 +3842,7 @@ hr {
background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ff6f01), color-stop(100%, #bc5200)); background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ff6f01), color-stop(100%, #bc5200));
height:13px; height:13px;
z-index: 2; z-index: 2;
position:absolute; position: relative;
} }
.dropdown-menu li > a:hover, .dropdown-menu li > a:focus, .dropdown-submenu:hover > a .dropdown-menu li > a:hover, .dropdown-menu li > a:focus, .dropdown-submenu:hover > a
@ -3962,4 +3958,4 @@ li .ui-state-hover {
} }
/* jQuery dialog */ /* jQuery dialog */
.no-close .ui-dialog-titlebar-close {display: none } .no-close .ui-dialog-titlebar-close {display: none }