From 4d9fb27554864ebecd3537335f1854864c20427e Mon Sep 17 00:00:00 2001 From: drigato Date: Wed, 29 Jul 2015 08:10:05 -0400 Subject: [PATCH 01/27] SAAS-973: Airtime Billing page - Add support for August promotion plans created promo-eligibility-check endpoint --- .../controllers/BillingController.php | 23 ++++++++++++++++++- .../views/scripts/billing/upgrade.phtml | 15 ++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index fda5c881d..a6be46e82 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -10,6 +10,7 @@ class BillingController extends Zend_Controller_Action { //Two of the actions in this controller return JSON because they're used for AJAX: $ajaxContext = $this->_helper->getHelper('AjaxContext'); $ajaxContext->addActionContext('vat-validator', 'json') + ->addActionContext('promo-eligibility-check', 'json') ->addActionContext('is-country-in-eu', 'json') ->initContext(); } @@ -19,6 +20,22 @@ class BillingController extends Zend_Controller_Action { $this->_redirect('billing/upgrade'); } + public function promoEligibilityCheckAction() + { + $this->view->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + + $request = $this->getRequest(); + if (!$request->isPost()) { + throw new Exception("Must POST data to promoEligibilityCheckAction."); + } + $data = $request->getPost(); + Logging::info($data); + + //Set the return JSON value + $this->_helper->json(array("result"=>"ok")); + } + public function upgradeAction() { $CC_CONFIG = Config::getConfig(); @@ -29,9 +46,13 @@ class BillingController extends Zend_Controller_Action { $request = $this->getRequest(); $form = new Application_Form_BillingUpgradeDowngrade(); if ($request->isPost()) { + $doUpgrade = false; $formData = $request->getPost(); - if ($form->isValid($formData)) { + Logging::info($formData); + if ($doUpgrade && $form->isValid($formData)) { + Logging::info("---upgrading----"); + $credentials = Billing::getAPICredentials(); //Check if VAT should be applied or not to this invoice. diff --git a/airtime_mvc/application/views/scripts/billing/upgrade.phtml b/airtime_mvc/application/views/scripts/billing/upgrade.phtml index dfdab0707..c74441179 100644 --- a/airtime_mvc/application/views/scripts/billing/upgrade.phtml +++ b/airtime_mvc/application/views/scripts/billing/upgrade.phtml @@ -120,6 +120,19 @@ function configureByCountry(countryCode) }); } +function promoEligibilityCheck() +{ + var newproductid = $("input[type='radio'][name='newproductid']:checked"); + var newproductbillingcycle = $("input[type='radio'][name='newproductbillingcycle']:checked"); + console.log(newproductid.val()); + console.log(newproductbillingcycle.val()); + $.post("/billing/promo-eligibility-check", {"newproductid": newproductid, + "newproductbillingcycle": newproductbillingcycle}) + .success(function(data) { + console.log(data); + }); +} + $(document).ready(function() { configureByCountry($("#country").val()); @@ -128,9 +141,11 @@ $(document).ready(function() { $("input[name='newproductid']").change(function() { validatePlan(); recalculateTotals(); + promoEligibilityCheck(); }); $("input[name='newproductbillingcycle']").change(function() { recalculateTotals(); + promoEligibilityCheck(); }); $("#country").change(function() { From 4ed87de183176e71550f0dfce4f7584cf1221d02 Mon Sep 17 00:00:00 2001 From: drigato Date: Wed, 29 Jul 2015 22:24:17 -0400 Subject: [PATCH 02/27] SAAS-973: Airtime Billing page - Add support for August promotion plans Backend side pretty much done --- airtime_mvc/application/common/Billing.php | 45 ++++++++++++- .../controllers/BillingController.php | 63 ++++++++++++------- .../views/scripts/billing/upgrade.phtml | 24 +++++-- 3 files changed, 103 insertions(+), 29 deletions(-) diff --git a/airtime_mvc/application/common/Billing.php b/airtime_mvc/application/common/Billing.php index fa716f6d1..b41ea36ee 100644 --- a/airtime_mvc/application/common/Billing.php +++ b/airtime_mvc/application/common/Billing.php @@ -1,5 +1,7 @@ $p) { - Logging::info($p); if ($p["paytype"] === "free") { unset($products[$k]); @@ -326,4 +327,46 @@ class Billing $result = Billing::makeRequest($credentials["url"], $query_string); } + public static function isClientEligibleForPromo($newProductId, $newProductBillingCycle) + { + $currentPaidPlanProductIds = array( + "25", + "26", + "27", + "28" + ); + + $currentPlanProduct = self::getClientCurrentAirtimeProduct(); + $currentPlanProductId = $currentPlanProduct["pid"]; + $currentPlanBillingCycle = strtolower($currentPlanProduct["billingcycle"]); + + // if client is on trial plan, YES + if ($currentPlanProductId == AIRTIME_PRO_FREE_TRIAL_PLAN_ID) { + return true; + } + + // if client is currently on monthly or old plan AND (upgrading OR upgrading/downgrading to annual plan), YES + if ($currentPlanBillingCycle == "monthly" || $currentPlanBillingCycle == "free account") { + // is the client changing billing cycle to annual? + if ($newProductBillingCycle == "annually") { + return true; + } + + // Is the client staying on monthly and upgrading? + // This won't hold true if the client is on an old plan because the old + // plan ids are higher than the current plan ids. + if ($newProductBillingCycle == "monthly" && $newProductId > $currentPlanProductId) { + return true; + } + + // Is the client staying on monthly and upgrading from an old plan? + if ($newProductBillingCycle == "monthly" && !in_array($currentPlanProductId, $currentPaidPlanProductIds) + && in_array($newProductId, $currentPaidPlanProductIds)) { + return true; + } + } + + return false; + } + } diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index a6be46e82..420b13644 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -30,10 +30,12 @@ class BillingController extends Zend_Controller_Action { throw new Exception("Must POST data to promoEligibilityCheckAction."); } $data = $request->getPost(); - Logging::info($data); + + $eligible = Billing::isClientEligibleForPromo( + $data["newproductid"], $data["newproductbillingcycle"]); //Set the return JSON value - $this->_helper->json(array("result"=>"ok")); + $this->_helper->json(array("result"=>$eligible)); } public function upgradeAction() @@ -46,24 +48,42 @@ class BillingController extends Zend_Controller_Action { $request = $this->getRequest(); $form = new Application_Form_BillingUpgradeDowngrade(); if ($request->isPost()) { + // for testing $doUpgrade = false; $formData = $request->getPost(); - Logging::info($formData); + if ($doUpgrade && $form->isValid($formData)) { + // for testing Logging::info("---upgrading----"); + // Maps current paid plans to the promo plan ids + // TODO: update these to the correct values + $promoPlanIdMap = array( + "25" => "50", //hobbyist + "26" => "51", //starter + "27" => "52", //plus + "28" => "53" //premium + ); + + // Check if client is eligible for promo and update the new product id if so + $eligibleForPromo = Billing::isClientEligibleForPromo( + $formData["newproductid"], $formData["newproductbillingcycle"]); + if ($eligibleForPromo) { + $formData["newproductid"] = $promoPlanIdMap[$formData["newproductid"]]; + } + $credentials = Billing::getAPICredentials(); - + //Check if VAT should be applied or not to this invoice. if (in_array("7", $formData["customfields"])) { $apply_vat = Billing::checkIfVatShouldBeApplied($formData["customfields"]["7"], $formData["country"]); } else { $apply_vat = false; } - + $placeAnUpgradeOrder = true; - + $currentPlanProduct = Billing::getClientCurrentAirtimeProduct(); $currentPlanProductId = $currentPlanProduct["pid"]; $currentPlanProductBillingCycle = strtolower($currentPlanProduct["billingcycle"]); @@ -76,29 +96,29 @@ class BillingController extends Zend_Controller_Action { { $placeAnUpgradeOrder = false; } - + $postfields = array(); $postfields["username"] = $credentials["username"]; $postfields["password"] = md5($credentials["password"]); $postfields["action"] = "upgradeproduct"; $postfields["clientid"] = Application_Model_Preference::GetClientId(); - + $postfields["serviceid"] = Billing::getClientInstanceId(); $postfields["type"] = "product"; $postfields["newproductid"] = $formData["newproductid"]; $postfields["newproductbillingcycle"] = $formData["newproductbillingcycle"]; $postfields["paymentmethod"] = $formData["paymentmethod"]; $postfields["responsetype"] = "json"; - + $upgrade_query_string = ""; - foreach ($postfields AS $k=>$v) $upgrade_query_string .= "$k=".urlencode($v)."&"; - + foreach ($postfields AS $k => $v) $upgrade_query_string .= "$k=" . urlencode($v) . "&"; + //update client info $clientfields = array(); $clientfields["username"] = $credentials["username"]; $clientfields["password"] = md5($credentials["password"]); - $clientfields["action"] = "updateclient"; + $clientfields["action"] = "updateclient"; $clientfields["clientid"] = Application_Model_Preference::GetClientId(); $clientfields["customfields"] = base64_encode(serialize($formData["customfields"])); unset($formData["customfields"]); @@ -111,8 +131,8 @@ class BillingController extends Zend_Controller_Action { unset($clientfields["password2verify"]); unset($clientfields["submit"]); $client_query_string = ""; - foreach ($clientfields AS $k=>$v) $client_query_string .= "$k=".urlencode($v)."&"; - + foreach ($clientfields AS $k => $v) $client_query_string .= "$k=" . urlencode($v) . "&"; + //Update the client details in WHMCS first $result = Billing::makeRequest($credentials["url"], $client_query_string); Logging::info($result); @@ -121,29 +141,28 @@ class BillingController extends Zend_Controller_Action { $this->view->form = $form; return; } - + //If there were no changes to the plan or billing cycle, we just redirect you to the //invoices screen and show a message. - if (!$placeAnUpgradeOrder) - { + if (!$placeAnUpgradeOrder) { $this->_redirect('billing/invoices?planupdated'); return; } - + //Then place an upgrade order in WHMCS $result = Billing::makeRequest($credentials["url"], $upgrade_query_string); if ($result["result"] == "error") { - Logging::info($_SERVER['HTTP_HOST']." - Account upgrade failed. - ".$result["message"]); + Logging::info($_SERVER['HTTP_HOST'] . " - Account upgrade failed. - " . $result["message"]); $this->setErrorMessage(); $this->view->form = $form; } else { - Logging::info($_SERVER['HTTP_HOST']. "Account plan upgrade request:"); + Logging::info($_SERVER['HTTP_HOST'] . "Account plan upgrade request:"); Logging::info($result); - + // Disable the view and the layout here, squashes an error. $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); - + if ($apply_vat) { Billing::addVatToInvoice($result["invoiceid"]); } diff --git a/airtime_mvc/application/views/scripts/billing/upgrade.phtml b/airtime_mvc/application/views/scripts/billing/upgrade.phtml index c74441179..17274c286 100644 --- a/airtime_mvc/application/views/scripts/billing/upgrade.phtml +++ b/airtime_mvc/application/views/scripts/billing/upgrade.phtml @@ -122,14 +122,23 @@ function configureByCountry(countryCode) function promoEligibilityCheck() { - var newproductid = $("input[type='radio'][name='newproductid']:checked"); - var newproductbillingcycle = $("input[type='radio'][name='newproductbillingcycle']:checked"); - console.log(newproductid.val()); - console.log(newproductbillingcycle.val()); + var newproductid = $("input[type='radio'][name='newproductid']:checked").val(); + + // newproductid can be undefined if the client is currently on an old plan + // and they just change the billing cycle value without selecting a new plan type. + // In this case, let's not check if they are eligible for the promo because + // they won't be able to upgrade without selecting a new plan first. + if (newproductid === undefined) { + return; + } + var newproductbillingcycle = $("input[type='radio'][name='newproductbillingcycle']:checked").val(); + $.post("/billing/promo-eligibility-check", {"newproductid": newproductid, "newproductbillingcycle": newproductbillingcycle}) .success(function(data) { - console.log(data); + if (data.result == true) { + $("#promo-plan-eligible").show(); + } }); } @@ -137,7 +146,7 @@ $(document).ready(function() { configureByCountry($("#country").val()); recalculateTotals(); - + $("input[name='newproductid']").change(function() { validatePlan(); recalculateTotals(); @@ -289,6 +298,9 @@ echo($currentProduct["name"]);
Save 15% on annual plans (Hobbyist plan excluded).
+
Subtotal:
From d9e2ba0ed31e14f9f47f21e22fcc96069ba1dbdd Mon Sep 17 00:00:00 2001 From: drigato Date: Thu, 30 Jul 2015 09:39:50 -0400 Subject: [PATCH 03/27] SAAS-973: Airtime Billing page - Add support for August promotion plans Un-hardcode product ids --- airtime_mvc/application/common/Billing.php | 98 +++++++++++++++++-- .../controllers/BillingController.php | 18 +--- 2 files changed, 91 insertions(+), 25 deletions(-) diff --git a/airtime_mvc/application/common/Billing.php b/airtime_mvc/application/common/Billing.php index b41ea36ee..6958bcad9 100644 --- a/airtime_mvc/application/common/Billing.php +++ b/airtime_mvc/application/common/Billing.php @@ -32,10 +32,10 @@ class Billing { //Making this static to cache the products during a single HTTP request. //This saves us roundtrips to WHMCS if getProducts() is called multiple times. - static $result = array(); - if (!empty($result)) + static $products = array(); + if (!empty($products)) { - return $result["products"]["product"]; + return $products; } $credentials = self::getAPICredentials(); @@ -56,8 +56,9 @@ class Billing $products = $result["products"]["product"]; //Blacklist all free plans + //Hide the promo plans - we will tell the user if they are eligible for a promo plan foreach ($products as $k => $p) { - if ($p["paytype"] === "free") + if ($p["paytype"] === "free" || strpos($p["name"], "Awesome August 2015") !== false) { unset($products[$k]); } @@ -327,14 +328,91 @@ class Billing $result = Billing::makeRequest($credentials["url"], $query_string); } + /** + * Returns an array of the current Airtime Pro plan IDs. + * This excludes any old, free, promotional, or custom plans. + */ + public static function getCurrentPaidProductIds() + { + $products = self::getProducts(); + $productIds = array(); + foreach ($products as $k => $p) { + array_push($productIds, $p["pid"]); + } + + return $productIds; + } + + /** + * Returns an array of the Awesome August 2015 Promotional plans + */ + public static function getAwesomeAugustPromoProducts() + { + $credentials = self::getAPICredentials(); + + $postfields = array(); + $postfields["username"] = $credentials["username"]; + $postfields["password"] = md5($credentials["password"]); + $postfields["action"] = "getproducts"; + $postfields["responsetype"] = "json"; + //gid is the Airtime product group id on whmcs + $postfields["gid"] = "15"; + + $query_string = ""; + foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&"; + + $result = self::makeRequest($credentials["url"], $query_string); + $promoProducts = $result["products"]["product"]; + + foreach ($promoProducts as $k => $p) { + if (strpos($p["name"], "Awesome August 2015") === false) { + unset($promoProducts[$k]); + } + } + + return $promoProducts; + } + + /** + * Returns the eligible promo plan ID that corresponds to the regular paid plan. + * + * i.e. if the client wants to upgrade to the Plus plan this function returns the + * plan id of the Awesome August 2015 Plus plan. + */ + public static function getEligibleAwesomeAugustPromoPlanId($productName) + { + $promoPlans = self::getAwesomeAugustPromoProducts(); + $promoPlanId = ""; + + foreach($promoPlans as $k => $p) { + if (strpos($p["name"], $productName) !== false) { + $promoPlanId = $p["pid"]; + break; + } + } + + return $promoPlanId; + } + + public static function getProductName($productId) + { + $products = self::getProducts(); + $productName = ""; + + foreach($products as $k => $p) { + if ($p["pid"] == $productId) { + $productName = $p["name"]; + break; + } + } + + return $productName; + } + public static function isClientEligibleForPromo($newProductId, $newProductBillingCycle) { - $currentPaidPlanProductIds = array( - "25", - "26", - "27", - "28" - ); + // use this to check if client is upgrading from an old plan + $currentPaidPlanProductIds = self::getCurrentPaidProductIds(); $currentPlanProduct = self::getClientCurrentAirtimeProduct(); $currentPlanProductId = $currentPlanProduct["pid"]; diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index 420b13644..dee22bfcd 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -48,29 +48,17 @@ class BillingController extends Zend_Controller_Action { $request = $this->getRequest(); $form = new Application_Form_BillingUpgradeDowngrade(); if ($request->isPost()) { - // for testing - $doUpgrade = false; $formData = $request->getPost(); - if ($doUpgrade && $form->isValid($formData)) { - // for testing - Logging::info("---upgrading----"); - - // Maps current paid plans to the promo plan ids - // TODO: update these to the correct values - $promoPlanIdMap = array( - "25" => "50", //hobbyist - "26" => "51", //starter - "27" => "52", //plus - "28" => "53" //premium - ); + if ($form->isValid($formData)) { // Check if client is eligible for promo and update the new product id if so $eligibleForPromo = Billing::isClientEligibleForPromo( $formData["newproductid"], $formData["newproductbillingcycle"]); if ($eligibleForPromo) { - $formData["newproductid"] = $promoPlanIdMap[$formData["newproductid"]]; + $newProductName = Billing::getProductName($formData["newproductid"]); + $formData["newproductid"] = Billing::getEligibleAwesomeAugustPromoPlanId($newProductName); } $credentials = Billing::getAPICredentials(); From bccba2f9d5583bf42378138f85bb134adf82cc3d Mon Sep 17 00:00:00 2001 From: drigato Date: Thu, 30 Jul 2015 12:53:37 -0400 Subject: [PATCH 04/27] SAAS-973: Airtime Billing page - Add support for August promotion plans Front-end changes to price grid, and plan title --- airtime_mvc/application/common/Billing.php | 5 +- .../views/scripts/billing/upgrade.phtml | 49 +++++++++++++------ airtime_mvc/public/css/billing.css | 28 +++++++++-- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/airtime_mvc/application/common/Billing.php b/airtime_mvc/application/common/Billing.php index 6958bcad9..eaddf254e 100644 --- a/airtime_mvc/application/common/Billing.php +++ b/airtime_mvc/application/common/Billing.php @@ -423,8 +423,9 @@ class Billing return true; } - // if client is currently on monthly or old plan AND (upgrading OR upgrading/downgrading to annual plan), YES - if ($currentPlanBillingCycle == "monthly" || $currentPlanBillingCycle == "free account") { + // if client is currently on monthly or annually or old plan AND (upgrading OR upgrading/downgrading to annual plan), YES + if ($currentPlanBillingCycle == "monthly" || $currentPlanBillingCycle == "free account" + || $currentPlanBillingCycle == "annually") { // is the client changing billing cycle to annual? if ($newProductBillingCycle == "annually") { return true; diff --git a/airtime_mvc/application/views/scripts/billing/upgrade.phtml b/airtime_mvc/application/views/scripts/billing/upgrade.phtml index 17274c286..bd7dccfb7 100644 --- a/airtime_mvc/application/views/scripts/billing/upgrade.phtml +++ b/airtime_mvc/application/views/scripts/billing/upgrade.phtml @@ -138,6 +138,8 @@ function promoEligibilityCheck() .success(function(data) { if (data.result == true) { $("#promo-plan-eligible").show(); + } else if ($("#promo-plan-eligible").is(":visible")) { + $("#promo-plan-eligible").hide(); } }); } @@ -197,10 +199,10 @@ $(document).ready(function() {
- - - - + + + + - - + - - - - - + - - - @@ -298,8 +316,9 @@ echo($currentProduct["name"]);
Save 15% on annual plans (Hobbyist plan excluded).
+
diff --git a/airtime_mvc/public/css/billing.css b/airtime_mvc/public/css/billing.css index 06edbec0b..834b43a24 100644 --- a/airtime_mvc/public/css/billing.css +++ b/airtime_mvc/public/css/billing.css @@ -56,8 +56,8 @@ border-spacing: 0px; border-collapse: separate; border: 1px solid #777; - width: 600px; - margin-left: -100px; + width: 680px; + margin-left: -140px; /*background-color: #555;*/ table-layout: fixed; margin-top: 20px; @@ -65,6 +65,19 @@ box-shadow: 0px 5px 5px rgba(0,0,0,0.5); } +.pricing-grid .august-promo div { + display: inline-block; + background-color: #ff611f; + padding: 3px 5px; + border-radius: 5px; + margin-left: -5px; + color: #ffffff; +} + +.pricing-grid .august-promo span { + text-decoration: line-through; +} + .pricing-grid td, .pricing-grid th { border-bottom: 1px solid #999; @@ -223,4 +236,13 @@ { /*text-align: right;*/ width: 100%; -} \ No newline at end of file +} + +#promo-plan-eligible { + margin-top: 10px; + font-size: 13px; + background-color: #ff611f; + padding: 3px 5px; + border-radius: 5px; + color: #ffffff; +} From b2fbb27801c1c1cfff61cbae57a82203bcdabfe5 Mon Sep 17 00:00:00 2001 From: drigato Date: Thu, 30 Jul 2015 13:27:32 -0400 Subject: [PATCH 05/27] SAAS-973: Airtime Billing page - Add support for August promotion plans Made WHMCS Airtime group id a constant Check for CSRF token on promo eligibilty ajax check --- airtime_mvc/application/common/Billing.php | 5 +++-- .../controllers/BillingController.php | 22 ++++++++++++++----- .../forms/BillingUpgradeDowngrade.php | 5 +++++ .../views/scripts/billing/upgrade.phtml | 4 +++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/airtime_mvc/application/common/Billing.php b/airtime_mvc/application/common/Billing.php index eaddf254e..30be5ff3b 100644 --- a/airtime_mvc/application/common/Billing.php +++ b/airtime_mvc/application/common/Billing.php @@ -1,6 +1,7 @@ $v) $query_string .= "$k=".urlencode($v)."&"; @@ -356,7 +357,7 @@ class Billing $postfields["action"] = "getproducts"; $postfields["responsetype"] = "json"; //gid is the Airtime product group id on whmcs - $postfields["gid"] = "15"; + $postfields["gid"] = WHMCS_AIRTIME_GROUP_ID; $query_string = ""; foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&"; diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index dee22bfcd..b8d2b8d1a 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -31,11 +31,20 @@ class BillingController extends Zend_Controller_Action { } $data = $request->getPost(); - $eligible = Billing::isClientEligibleForPromo( - $data["newproductid"], $data["newproductbillingcycle"]); + $current_namespace = new Zend_Session_Namespace('csrf_namespace'); + $observed_csrf_token = $this->_getParam('csrf_token'); + $expected_csrf_token = $current_namespace->authtoken; - //Set the return JSON value - $this->_helper->json(array("result"=>$eligible)); + if($observed_csrf_token == $expected_csrf_token) { + $eligible = Billing::isClientEligibleForPromo( + $data["newproductid"], $data["newproductbillingcycle"]); + + //Set the return JSON value + $this->_helper->json(array("result"=>$eligible)); + } else { + $this->getResponse()->setHttpResponseCode(403); + $this->_helper->json(array("result"=>false, "error"=>"CSRF token did not match.")); + } } public function upgradeAction() @@ -47,6 +56,7 @@ class BillingController extends Zend_Controller_Action { $request = $this->getRequest(); $form = new Application_Form_BillingUpgradeDowngrade(); + if ($request->isPost()) { $formData = $request->getPost(); @@ -80,8 +90,8 @@ class BillingController extends Zend_Controller_Action { //and it freaks out and does the wrong thing if we do it via the API //so we have to do avoid that. if (($currentPlanProductId == $formData["newproductid"]) && - ($currentPlanProductBillingCycle == $formData["newproductbillingcycle"])) - { + ($currentPlanProductBillingCycle == $formData["newproductbillingcycle"]) + ) { $placeAnUpgradeOrder = false; } diff --git a/airtime_mvc/application/forms/BillingUpgradeDowngrade.php b/airtime_mvc/application/forms/BillingUpgradeDowngrade.php index 6726c5da8..ecf9b4f50 100644 --- a/airtime_mvc/application/forms/BillingUpgradeDowngrade.php +++ b/airtime_mvc/application/forms/BillingUpgradeDowngrade.php @@ -3,6 +3,11 @@ 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); + $productPrices = array(); $productTypes = array(); list($productPrices, $productTypes) = Billing::getProductPricesAndTypes(); diff --git a/airtime_mvc/application/views/scripts/billing/upgrade.phtml b/airtime_mvc/application/views/scripts/billing/upgrade.phtml index bd7dccfb7..d956ed363 100644 --- a/airtime_mvc/application/views/scripts/billing/upgrade.phtml +++ b/airtime_mvc/application/views/scripts/billing/upgrade.phtml @@ -134,7 +134,7 @@ function promoEligibilityCheck() var newproductbillingcycle = $("input[type='radio'][name='newproductbillingcycle']:checked").val(); $.post("/billing/promo-eligibility-check", {"newproductid": newproductid, - "newproductbillingcycle": newproductbillingcycle}) + "newproductbillingcycle": newproductbillingcycle, "csrf_token": $("#csrf").attr('value')}) .success(function(data) { if (data.result == true) { $("#promo-plan-eligible").show(); @@ -306,6 +306,8 @@ echo($currentProduct["name"]);

Choose a plan:

+ + csrf ?>
newproductid ?> From 48d745bf77a6fb39ec45d3b7c09a863a6b0ffcbf Mon Sep 17 00:00:00 2001 From: drigato Date: Thu, 30 Jul 2015 13:37:15 -0400 Subject: [PATCH 06/27] Hide Awesome August 2015 promo plans from billing page --- airtime_mvc/application/common/Billing.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/airtime_mvc/application/common/Billing.php b/airtime_mvc/application/common/Billing.php index fa716f6d1..12ea8c6e2 100644 --- a/airtime_mvc/application/common/Billing.php +++ b/airtime_mvc/application/common/Billing.php @@ -55,8 +55,7 @@ class Billing //Blacklist all free plans foreach ($products as $k => $p) { - Logging::info($p); - if ($p["paytype"] === "free") + if ($p["paytype"] === "free" || strpos($p["name"], "Awesome August 2015") !== false) { unset($products[$k]); } From ff61fab62ac7440c5dafcaf3067246f76139beea Mon Sep 17 00:00:00 2001 From: drigato Date: Fri, 31 Jul 2015 10:46:02 -0400 Subject: [PATCH 07/27] SAAS-973: Airtime Billing page - Add support for August promotion plans Fix to not allow current promo plans to downgrade to another promo plan Added promo banner to billing page --- airtime_mvc/application/common/Billing.php | 32 +++++++++++++++++-- .../views/scripts/billing/upgrade.phtml | 4 +++ airtime_mvc/public/css/billing.css | 5 +++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/airtime_mvc/application/common/Billing.php b/airtime_mvc/application/common/Billing.php index 30be5ff3b..0baa56104 100644 --- a/airtime_mvc/application/common/Billing.php +++ b/airtime_mvc/application/common/Billing.php @@ -419,12 +419,25 @@ class Billing $currentPlanProductId = $currentPlanProduct["pid"]; $currentPlanBillingCycle = strtolower($currentPlanProduct["billingcycle"]); + if (self::isClientOnAwesomeAugustPromoPlan($currentPlanProductId)) { + + $newEligiblePromoId = self::getEligibleAwesomeAugustPromoPlanId( + self::getProductName($newProductId) + ); + + if ($newProductBillingCycle == "annually" || $newEligiblePromoId > $currentPlanProductId) { + return true; + } else { + return false; + } + } + // if client is on trial plan, YES if ($currentPlanProductId == AIRTIME_PRO_FREE_TRIAL_PLAN_ID) { return true; } - // if client is currently on monthly or annually or old plan AND (upgrading OR upgrading/downgrading to annual plan), YES + // if client is currently on monthly or annually or old/free plan AND (upgrading OR upgrading/downgrading to annual plan), YES if ($currentPlanBillingCycle == "monthly" || $currentPlanBillingCycle == "free account" || $currentPlanBillingCycle == "annually") { // is the client changing billing cycle to annual? @@ -433,8 +446,8 @@ class Billing } // Is the client staying on monthly and upgrading? - // This won't hold true if the client is on an old plan because the old - // plan ids are higher than the current plan ids. + // This won't hold true if the client is on an old/free plan because the + // old/free plan ids are higher than the current paid plan ids. if ($newProductBillingCycle == "monthly" && $newProductId > $currentPlanProductId) { return true; } @@ -449,4 +462,17 @@ class Billing return false; } + public static function isClientOnAwesomeAugustPromoPlan($currentPlanId) + { + $promoPlans = self::getAwesomeAugustPromoProducts(); + + foreach ($promoPlans as $k => $p) { + if ($p["pid"] == $currentPlanId) { + return true; + } + } + + return false; + } + } diff --git a/airtime_mvc/application/views/scripts/billing/upgrade.phtml b/airtime_mvc/application/views/scripts/billing/upgrade.phtml index d956ed363..5d583f245 100644 --- a/airtime_mvc/application/views/scripts/billing/upgrade.phtml +++ b/airtime_mvc/application/views/scripts/billing/upgrade.phtml @@ -196,6 +196,10 @@ $(document).ready(function() {

+
+ + +
HobbyistStarterPlusPremiumAwesome HobbyistAwesome StarterAwesome PlusAwesome Premium
1 Stream @@ -222,24 +224,40 @@ $(document).ready(function() { 64kbps, 128kbps, and 196kbps Stream Quality
5 Listeners +
+ 5 Listeners
+
10 Listeners
40 Listeners per stream + + 40 Listeners per stream
+
80 Listeners per stream
100 Listeners per stream + + 100 Listeners per stream
+
200 Listeners per stream
500 Listeners per stream + + 500 Listeners per stream
+
1000 Listeners per stream
2GB Storage +
+ 2GB Storage
+
4GB Storage
5GB Storage + + 5GB Storage
+
10GB Storage
30GB Storage + + 30GB Storage
+
60GB Storage
150GB Storage + + 150GB Storage
+
300GB Storage
diff --git a/airtime_mvc/public/css/billing.css b/airtime_mvc/public/css/billing.css index 834b43a24..33cbe006c 100644 --- a/airtime_mvc/public/css/billing.css +++ b/airtime_mvc/public/css/billing.css @@ -246,3 +246,8 @@ border-radius: 5px; color: #ffffff; } + +.promo-banner { + margin-top:10px; + background: url('images/august_aweomse_promo_banner.png') no-repeat; +} From 7de507c99a363deade5a1690dbec7a34dfbc9ad6 Mon Sep 17 00:00:00 2001 From: drigato Date: Fri, 31 Jul 2015 10:49:24 -0400 Subject: [PATCH 08/27] Forgot to commit promo banner --- .../css/images/august_aweomse_promo_banner.png | Bin 0 -> 49859 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 airtime_mvc/public/css/images/august_aweomse_promo_banner.png diff --git a/airtime_mvc/public/css/images/august_aweomse_promo_banner.png b/airtime_mvc/public/css/images/august_aweomse_promo_banner.png new file mode 100644 index 0000000000000000000000000000000000000000..22d10be9e370a59da035a683822a7902ba27eb8d GIT binary patch literal 49859 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

|QV~PSi#JzfHkP46ji9#bUH7b_r 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 literal 0 HcmV?d00001 From 84c21de5786da3edab4d465c9e0852b96db1eaa3 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Fri, 31 Jul 2015 11:43:58 -0400 Subject: [PATCH 09/27] Fix error logic if no API key passed to provisioning/create API --- airtime_mvc/application/common/ProvisioningHelper.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/common/ProvisioningHelper.php b/airtime_mvc/application/common/ProvisioningHelper.php index cdd9818c0..c5852e94c 100644 --- a/airtime_mvc/application/common/ProvisioningHelper.php +++ b/airtime_mvc/application/common/ProvisioningHelper.php @@ -24,8 +24,12 @@ class ProvisioningHelper */ public function createAction() { - $apikey = $_SERVER['PHP_AUTH_USER']; - if (!isset($apikey) || $apikey != $this->apikey) { + $apikey = ""; + if (isset($_SERVER['PHP_AUTH_USER'])) + { + $apikey = $_SERVER['PHP_AUTH_USER']; + } + if ($apikey != $this->apikey) { Logging::info("Invalid API Key: $apikey"); http_response_code(403); echo "ERROR: Incorrect API key"; From 278ca264eb3e4434c121e0bd5b4c0fbea9618973 Mon Sep 17 00:00:00 2001 From: drigato Date: Fri, 31 Jul 2015 11:48:20 -0400 Subject: [PATCH 10/27] Fix for bringing customer to view invoice that doesn't exist after downgrade --- airtime_mvc/application/controllers/BillingController.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index b8d2b8d1a..a20f92dd6 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -164,7 +164,12 @@ class BillingController extends Zend_Controller_Action { if ($apply_vat) { Billing::addVatToInvoice($result["invoiceid"]); } - self::viewInvoice($result["invoiceid"]); + + // invoice id can be 0 if client is downgrading and they don't + // owe any money + if (!empty($result["invoiceid"])) { + self::viewInvoice($result["invoiceid"]); + } } } else { $this->view->form = $form; From aab4b634bfb7252bc64afebf932a7a1eb620f959 Mon Sep 17 00:00:00 2001 From: drigato Date: Fri, 31 Jul 2015 11:56:00 -0400 Subject: [PATCH 11/27] Show form on billing page if no invoice was generated after downgrade --- airtime_mvc/application/controllers/BillingController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index a20f92dd6..0d5ab097f 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -169,6 +169,8 @@ class BillingController extends Zend_Controller_Action { // owe any money if (!empty($result["invoiceid"])) { self::viewInvoice($result["invoiceid"]); + } else { + $this->view->form = $form; } } } else { From 5530142174053005b3366f4d9d7872503aab4f73 Mon Sep 17 00:00:00 2001 From: drigato Date: Fri, 31 Jul 2015 12:01:26 -0400 Subject: [PATCH 12/27] Un-doing last 2 committs about invoices --- airtime_mvc/application/controllers/BillingController.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index 0d5ab097f..ca5f784a6 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -165,13 +165,7 @@ class BillingController extends Zend_Controller_Action { Billing::addVatToInvoice($result["invoiceid"]); } - // invoice id can be 0 if client is downgrading and they don't - // owe any money - if (!empty($result["invoiceid"])) { - self::viewInvoice($result["invoiceid"]); - } else { - $this->view->form = $form; - } + self::viewInvoice($result["invoiceid"]); } } else { $this->view->form = $form; From 8a52178765a729c98bf68c04e28e6a8687107c6b Mon Sep 17 00:00:00 2001 From: drigato Date: Fri, 31 Jul 2015 13:42:47 -0400 Subject: [PATCH 13/27] Fix for billing downgrade with no new invoice created --- airtime_mvc/application/controllers/BillingController.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index ca5f784a6..92f499199 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -165,7 +165,13 @@ class BillingController extends Zend_Controller_Action { Billing::addVatToInvoice($result["invoiceid"]); } - self::viewInvoice($result["invoiceid"]); + // there may not be an invoice created if the client is downgrading + if (!empty($result["invoiceid"])) { + self::viewInvoice($result["invoiceid"]); + } else { + $this->_redirect('billing/invoices?planupdated'); + return; + } } } else { $this->view->form = $form; From d0d6efd5082f46fb87738628c0c5aecf724fac86 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Fri, 31 Jul 2015 15:02:53 -0400 Subject: [PATCH 14/27] Fix m4a uploads in Airtime --- airtime_mvc/application/common/FileDataHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/common/FileDataHelper.php b/airtime_mvc/application/common/FileDataHelper.php index 2c5648f9f..70a77eca8 100644 --- a/airtime_mvc/application/common/FileDataHelper.php +++ b/airtime_mvc/application/common/FileDataHelper.php @@ -12,7 +12,7 @@ class FileDataHelper { "audio/mpeg3" => "mp3", "audio/aac" => "aac", "audio/aacp" => "aac", - "audio/mp4" => "mp4", + "audio/mp4" => "m4a", "audio/x-flac" => "flac", "audio/wav" => "wav", "audio/x-wav" => "wav", @@ -65,4 +65,4 @@ class FileDataHelper { } } -} \ No newline at end of file +} From c934866063f1d3f0cb70d894ecc8ccd2a55094b6 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 4 Aug 2015 17:42:53 -0400 Subject: [PATCH 15/27] Two small code improvements --- airtime_mvc/application/models/Preference.php | 2 +- airtime_mvc/public/js/airtime/common/common.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index 6143058ba..2ccf68d05 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -203,7 +203,7 @@ class Application_Model_Preference if (strlen($title) > 0) $title .= " - "; - return $title."Airtime"; + return $title.PRODUCT_NAME; } public static function SetHeadTitle($title, $view=null) diff --git a/airtime_mvc/public/js/airtime/common/common.js b/airtime_mvc/public/js/airtime/common/common.js index 46c016bf3..587c1f163 100644 --- a/airtime_mvc/public/js/airtime/common/common.js +++ b/airtime_mvc/public/js/airtime/common/common.js @@ -164,7 +164,7 @@ function removeSuccessMsg() { function getUsabilityHint() { var pathname = window.location.pathname; - $.getJSON("/api/get-usability-hint", {"format": "json", "userPath": pathname}, function(json) { + $.getJSON(baseUrl + "api/get-usability-hint", {"format": "json", "userPath": pathname}, function(json) { var $hint_div = $('.usability_hint'); var current_hint = $hint_div.html(); if (json === "") { From f833ab969058b4c075cff384cf518fe99f7994ac Mon Sep 17 00:00:00 2001 From: drigato Date: Wed, 5 Aug 2015 09:48:31 -0400 Subject: [PATCH 16/27] SAAS-838: Undefined variable previousMediaName --- airtime_mvc/application/models/Schedule.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 811b90bd7..bec73fc7d 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -215,6 +215,7 @@ SQL; $currentMedia["ends"] = $currentMedia["show_ends"]; } + $currentMediaName = ""; $currentMediaFileId = $currentMedia["file_id"]; $currentMediaStreamId = $currentMedia["stream_id"]; if (isset($currentMediaFileId)) { @@ -256,6 +257,7 @@ SQL; ->orderByDbStarts(Criteria::DESC) ->findOne(); if (isset($previousMedia)) { + $previousMediaName = ""; $previousMediaFileId = $previousMedia->getDbFileId(); $previousMediaStreamId = $previousMedia->getDbStreamId(); if (isset($previousMediaFileId)) { From dab0f00736e49364bc9581c426adf85b92f33214 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 11 Aug 2015 08:59:44 -0400 Subject: [PATCH 17/27] Fixed m4a file extension mapping in pypo --- python_apps/pypo/pypo/mime.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/pypo/pypo/mime.types b/python_apps/pypo/pypo/mime.types index 9f05f6b44..4cea7b21c 100644 --- a/python_apps/pypo/pypo/mime.types +++ b/python_apps/pypo/pypo/mime.types @@ -7,7 +7,7 @@ audio/mpeg3 mp3 audio/x-aac aac audio/aac aac audio/aacp aac -audio/mp4 mp4 +audio/mp4 m4a audio/x-flac flac audio/wav wav audio/x-wav wav From 60a02cfdeec6ca54aca25242089b11ba31546fbc Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 11 Aug 2015 16:34:28 -0400 Subject: [PATCH 18/27] Temporarily disable the task manager to test lock contention --- airtime_mvc/application/Bootstrap.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index abc9ad99b..8711540c6 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -134,8 +134,9 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap if (getenv("AIRTIME_UNIT_TEST") != 1) { $taskManager = TaskManager::getInstance(); $taskManager->runTask(AirtimeTask::UPGRADE); // Run the upgrade on each request (if it needs to be run) + //XXX: Testing if this is causing lock contention //This will do the upgrade too if it's needed... - $taskManager->runTasks(); + //$taskManager->runTasks(); } } From e5f70e7ec3fafe2f12782dd75c1529c27325bb88 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 11 Aug 2015 17:32:40 -0400 Subject: [PATCH 19/27] Re-enable the task manager, it made no difference --- airtime_mvc/application/Bootstrap.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index 8711540c6..abc9ad99b 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -134,9 +134,8 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap if (getenv("AIRTIME_UNIT_TEST") != 1) { $taskManager = TaskManager::getInstance(); $taskManager->runTask(AirtimeTask::UPGRADE); // Run the upgrade on each request (if it needs to be run) - //XXX: Testing if this is causing lock contention //This will do the upgrade too if it's needed... - //$taskManager->runTasks(); + $taskManager->runTasks(); } } From 2da431895fab4b168a90d9b9721d9bbf909164b1 Mon Sep 17 00:00:00 2001 From: drigato Date: Tue, 25 Aug 2015 12:43:08 -0400 Subject: [PATCH 20/27] SAAS-1022: Re-implement disk quota status on side nav bar Changed "upload" page layout - panels are side by side and storage status is at bottom of left panel --- .../views/scripts/plupload/index.phtml | 28 +++++++++++++--- airtime_mvc/public/css/_showbuilder.css | 27 --------------- airtime_mvc/public/css/addmedia.css | 12 +++---- airtime_mvc/public/css/styles.css | 33 +++++++++++++++++-- 4 files changed, 61 insertions(+), 39 deletions(-) diff --git a/airtime_mvc/application/views/scripts/plupload/index.phtml b/airtime_mvc/application/views/scripts/plupload/index.phtml index f9363e701..7fe85172e 100644 --- a/airtime_mvc/application/views/scripts/plupload/index.phtml +++ b/airtime_mvc/application/views/scripts/plupload/index.phtml @@ -18,8 +18,16 @@

--> - +
+
-
-
+
@@ -54,8 +72,10 @@

+
+
-
+
\ No newline at end of file diff --git a/airtime_mvc/public/css/_showbuilder.css b/airtime_mvc/public/css/_showbuilder.css index ddff8d84d..f8e3b7038 100644 --- a/airtime_mvc/public/css/_showbuilder.css +++ b/airtime_mvc/public/css/_showbuilder.css @@ -20,25 +20,6 @@ background-color: #242424; } -.wide-panel { - display: -webkit-box; - display: -moz-box; - display: -ms-flexbox; - display: -webkit-flex; - display: flex; - -webkit-flex-flow: column; - flex-flow: column; - - /* Account for the left pane */ - flex: 6 auto; - min-width: 555px; -} - -.outer-datatable-wrapper { - position: relative; - flex: 1; -} - @media screen and (max-width: 1280px) { .wrapper { -webkit-flex-flow: column !important; @@ -146,14 +127,6 @@ div.btn > span { left: 4px; } -.dataTables_scrolling { - position: absolute; - bottom: 37px; /* 36 px is the size of the header/footer + 1px because there's no internal border */ - top: 37px; - left: 0; - right: 0; -} - #library_display, #show_builder_table { border: none; } diff --git a/airtime_mvc/public/css/addmedia.css b/airtime_mvc/public/css/addmedia.css index 80595b8c4..f7c48bf7c 100644 --- a/airtime_mvc/public/css/addmedia.css +++ b/airtime_mvc/public/css/addmedia.css @@ -2,18 +2,19 @@ #recent_uploads_wrapper { - width: 100%; - margin-bottom: 40px; + position: relative; + } #recent_uploads_table { width: 100%; + table-layout: fixed; } #recent_uploads_table_wrapper { - width: 100%; + position: absolute; } table#recent_uploads_table td @@ -267,7 +268,7 @@ table#recent_uploads_table td border: 2px solid rgba(0, 0, 0, 0.3); background: #333; padding: 20px 20px; - max-height: 300px; + max-height: 1000px; overflow-y: auto; } @@ -303,7 +304,7 @@ table#recent_uploads_table td border: 2px dashed rgba(255, 255, 255, 0.2); border-radius: 15px; display: block; - width: 50%; + position: relative; background-color: rgba(0, 0, 0, 0.1); -webkit-box-shadow: inset 0px -7px 27px -4px rgba(0,0,0,0.55); @@ -319,7 +320,6 @@ table#recent_uploads_table td display: inline-block; vertical-align: top; margin: 16px; - /*min-height: 100px*/ } .dropzone .dz-preview:hover { diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index 9f626521c..a2b5007d0 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -3605,9 +3605,12 @@ button.btn-icon-text > i.icon-white { /* Uploads/Dropzone */ #upload_form { - width: 100%; - min-width: 555px; + /* width: 100%; + min-width: 555px;*/ margin-bottom: 10px; + margin-right: 10px; + height: 100%; + position:relative; } #sub-menu-wrapper { @@ -3756,3 +3759,29 @@ li .ui-state-hover { border: 0px; } +.wide-panel { + display: -webkit-box; + display: -moz-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -webkit-flex-flow: column; + flex-flow: column; + + /* Account for the left pane */ + flex: 6 auto; + min-width: 500px; +} + +.outer-datatable-wrapper { + position: relative; + flex: 1; +} + +.dataTables_scrolling { + position: absolute; + bottom: 37px; /* 36 px is the size of the header/footer + 1px because there's no internal border */ + top: 37px; + left: 0; + right: 0; +} From bb2757127d6bd5401b381fcd50bd5cdb15824794 Mon Sep 17 00:00:00 2001 From: drigato Date: Tue, 25 Aug 2015 12:56:02 -0400 Subject: [PATCH 21/27] SAAS-1022: Re-implement disk quota status on side nav bar Small css tweak --- airtime_mvc/public/css/addmedia.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/public/css/addmedia.css b/airtime_mvc/public/css/addmedia.css index f7c48bf7c..9306a8abf 100644 --- a/airtime_mvc/public/css/addmedia.css +++ b/airtime_mvc/public/css/addmedia.css @@ -3,6 +3,7 @@ #recent_uploads_wrapper { position: relative; + min-height: 500px; } @@ -268,12 +269,12 @@ table#recent_uploads_table td border: 2px solid rgba(0, 0, 0, 0.3); background: #333; padding: 20px 20px; - max-height: 1000px; + max-height: 500px; overflow-y: auto; } .dropzone.dz-clickable { - cursor: pointer + cursor: pointer; } .dropzone.dz-clickable * { From 0c502479091d0ea393f64a9515bb9b47fca67bb7 Mon Sep 17 00:00:00 2001 From: drigato Date: Tue, 25 Aug 2015 13:09:07 -0400 Subject: [PATCH 22/27] SAAS-1022: Re-implement disk quota status on side nav bar Small css fix --- airtime_mvc/application/views/scripts/plupload/index.phtml | 2 +- airtime_mvc/public/css/addmedia.css | 4 ++-- airtime_mvc/public/css/styles.css | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/airtime_mvc/application/views/scripts/plupload/index.phtml b/airtime_mvc/application/views/scripts/plupload/index.phtml index 7fe85172e..0f0e016f3 100644 --- a/airtime_mvc/application/views/scripts/plupload/index.phtml +++ b/airtime_mvc/application/views/scripts/plupload/index.phtml @@ -28,7 +28,7 @@ $used = $disk->totalSpace-$disk->totalFreeSpace; $total = $disk->totalSpace; ?> -
+
form->getElement('csrf') ?>
diff --git a/airtime_mvc/public/css/addmedia.css b/airtime_mvc/public/css/addmedia.css index 9306a8abf..26e50e88b 100644 --- a/airtime_mvc/public/css/addmedia.css +++ b/airtime_mvc/public/css/addmedia.css @@ -265,11 +265,11 @@ table#recent_uploads_table td } .dropzone { - min-height: 150px; + min-height: 30%; border: 2px solid rgba(0, 0, 0, 0.3); background: #333; padding: 20px 20px; - max-height: 500px; + max-height: 75%; overflow-y: auto; } diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index a2b5007d0..2e60ae1f1 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -3611,6 +3611,7 @@ button.btn-icon-text > i.icon-white { margin-right: 10px; height: 100%; position:relative; + max-width: 50%; } #sub-menu-wrapper { From d4527a04606f39a9b06864706c5ca927d9752192 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 25 Aug 2015 13:15:57 -0400 Subject: [PATCH 23/27] CC-6091: Settings->General doesn't highlight when you click Settings --- airtime_mvc/application/configs/navigation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php index 00a84fb18..9ae03686a 100644 --- a/airtime_mvc/application/configs/navigation.php +++ b/airtime_mvc/application/configs/navigation.php @@ -48,7 +48,7 @@ $pages = array( 'resource' => 'preference', 'action' => 'index', 'module' => 'default', - 'controller' => 'preference', + 'controller' => 'Preference', 'title' => 'Settings', 'pages' => array( array( From c921aabfd460b5e222e72f84b794535fb1627745 Mon Sep 17 00:00:00 2001 From: drigato Date: Tue, 25 Aug 2015 13:19:18 -0400 Subject: [PATCH 24/27] Small css fix on recent uploads table --- airtime_mvc/public/css/styles.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index 2e60ae1f1..e30135b4b 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -862,6 +862,11 @@ dl.inline-list dd { color: #ccc; } +#recent_uploads_table_wrapper .datatable tr td { + overflow: hidden; + text-overflow: ellipsis; +} + .datatable tr, .datatable td, .dataTable tr, .dataTable td { From b99ec518630fcc488aae5e70c6503aadb12c6ca5 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 25 Aug 2015 15:15:03 -0400 Subject: [PATCH 25/27] CC-6102: Can't upload M4A and FLAC anymore - rejected by dropzone --- airtime_mvc/application/Bootstrap.php | 8 +++++++- airtime_mvc/public/js/airtime/library/plupload.js | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index b6c3440ed..26a6843c7 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -95,8 +95,14 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap } $view->headScript()->appendScript("var userType = '$userType';"); + // Dropzone also accept file extensions and doesn't correctly extract certain mimetypes (eg. FLAC - try it), + // so we append the file extensions to the list of mimetypes and that makes it work. $mimeTypes = FileDataHelper::getAudioMimeTypeArray(); - $view->headScript()->appendScript("var acceptedMimeTypes = ['".implode("','", array_keys($mimeTypes))."'];"); + $fileExtensions = array_values($mimeTypes); + foreach($fileExtensions as &$extension) { + $extension = '.' . $extension; + } + $view->headScript()->appendScript("var acceptedMimeTypes = " . json_encode(array_merge(array_keys($mimeTypes), $fileExtensions)) . ";"); } /** diff --git a/airtime_mvc/public/js/airtime/library/plupload.js b/airtime_mvc/public/js/airtime/library/plupload.js index 2287860fb..ea15d7e19 100644 --- a/airtime_mvc/public/js/airtime/library/plupload.js +++ b/airtime_mvc/public/js/airtime/library/plupload.js @@ -14,10 +14,11 @@ $(document).ready(function() { Object.freeze(self.IMPORT_STATUS_CODES); } + console.log(acceptedMimeTypes.join()); Dropzone.options.addMediaDropzone = { url:'/rest/media', //clickable: false, - acceptedFiles: acceptedMimeTypes.join(), + acceptedFiles: acceptedMimeTypes.join() + ",.flac", init: function () { this.on("sending", function (file, xhr, data) { data.append("csrf_token", $("#csrf").val()); From 498b3243e08fbcbc7a5164f076a0311c816e7f8b Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 25 Aug 2015 16:43:10 -0400 Subject: [PATCH 26/27] Fix navbar highlights when navigating via usability hints --- airtime_mvc/application/common/UsabilityHints.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/airtime_mvc/application/common/UsabilityHints.php b/airtime_mvc/application/common/UsabilityHints.php index 517517f23..e644b25ab 100644 --- a/airtime_mvc/application/common/UsabilityHints.php +++ b/airtime_mvc/application/common/UsabilityHints.php @@ -55,7 +55,7 @@ class Application_Common_UsabilityHints return _("Click the 'Add files' button and select files from your computer to upload."); } else { return sprintf(_("It looks like you have not uploaded any audio files yet. %sUpload a file now%s."), - "", + "", ""); } } else if (!self::isFutureOrCurrentShowScheduled()) { @@ -63,7 +63,7 @@ class Application_Common_UsabilityHints return _("Click the 'Create 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."), - "", + "", ""); } } else if (self::isCurrentShowEmpty()) { @@ -73,14 +73,14 @@ class Application_Common_UsabilityHints return _("To start broadcasting, first you need to cancel the current linked show by clicking on it and selecting 'Cancel Current 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."), "", ""); + %sCreate an unlinked show now%s."), "", ""); } } else { if ($userIsOnCalendarPage) { return _("To start broadcasting, click on the current show and select 'Add / Remove Content'"); } else { return sprintf(_("It looks like the current show needs more tracks. %sAdd tracks to your show now%s."), - "", + "", ""); } } @@ -89,7 +89,7 @@ class Application_Common_UsabilityHints return _("Click on the show starting next and select 'Add / Remove Content'"); } else { return sprintf(_("It looks like the next show is empty. %sAdd tracks to your show now%s."), - "", + "", ""); } } else { From b6cf517d1087f2ff34e4db12f8b492f1a422196e Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Tue, 25 Aug 2015 16:48:31 -0400 Subject: [PATCH 27/27] CC-6093: Review and fix the usability hints text --- airtime_mvc/application/common/UsabilityHints.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/common/UsabilityHints.php b/airtime_mvc/application/common/UsabilityHints.php index e644b25ab..4afa3396b 100644 --- a/airtime_mvc/application/common/UsabilityHints.php +++ b/airtime_mvc/application/common/UsabilityHints.php @@ -60,7 +60,7 @@ class Application_Common_UsabilityHints } } else if (!self::isFutureOrCurrentShowScheduled()) { if ($userIsOnCalendarPage) { - return _("Click the 'Create New Show' button and fill out the required fields."); + 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."), "",