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/application/common/Billing.php b/airtime_mvc/application/common/Billing.php index fa716f6d1..0baa56104 100644 --- a/airtime_mvc/application/common/Billing.php +++ b/airtime_mvc/application/common/Billing.php @@ -1,5 +1,8 @@ $v) $query_string .= "$k=".urlencode($v)."&"; @@ -54,9 +57,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) { - Logging::info($p); - if ($p["paytype"] === "free") + if ($p["paytype"] === "free" || strpos($p["name"], "Awesome August 2015") !== false) { unset($products[$k]); } @@ -326,4 +329,150 @@ 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"] = WHMCS_AIRTIME_GROUP_ID; + + $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) + { + // use this to check if client is upgrading from an old plan + $currentPaidPlanProductIds = self::getCurrentPaidProductIds(); + + $currentPlanProduct = self::getClientCurrentAirtimeProduct(); + $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/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? + 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/free plan because the + // old/free plan ids are higher than the current paid 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; + } + + 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/common/FileDataHelper.php b/airtime_mvc/application/common/FileDataHelper.php index 25318333b..568b25d4d 100644 --- a/airtime_mvc/application/common/FileDataHelper.php +++ b/airtime_mvc/application/common/FileDataHelper.php @@ -13,7 +13,7 @@ class FileDataHelper { "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", @@ -66,4 +66,4 @@ class FileDataHelper { } } -} \ No newline at end of file +} diff --git a/airtime_mvc/application/common/ProvisioningHelper.php b/airtime_mvc/application/common/ProvisioningHelper.php index f5aacd53d..113676e5e 100644 --- a/airtime_mvc/application/common/ProvisioningHelper.php +++ b/airtime_mvc/application/common/ProvisioningHelper.php @@ -25,8 +25,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"; diff --git a/airtime_mvc/application/common/UsabilityHints.php b/airtime_mvc/application/common/UsabilityHints.php index 517517f23..4afa3396b 100644 --- a/airtime_mvc/application/common/UsabilityHints.php +++ b/airtime_mvc/application/common/UsabilityHints.php @@ -55,15 +55,15 @@ 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()) { 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."), - "", + "", ""); } } 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 { 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( diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index 11e147674..2952d70ca 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,33 @@ 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(); + + $current_namespace = new Zend_Session_Namespace('csrf_namespace'); + $observed_csrf_token = $this->_getParam('csrf_token'); + $expected_csrf_token = $current_namespace->authtoken; + + 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() { @@ -31,21 +59,32 @@ class BillingController extends Zend_Controller_Action { $request = $this->getRequest(); $form = new Application_Form_BillingUpgradeDowngrade(); + if ($request->isPost()) { $formData = $request->getPost(); + 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) { + $newProductName = Billing::getProductName($formData["newproductid"]); + $formData["newproductid"] = Billing::getEligibleAwesomeAugustPromoPlanId($newProductName); + } + $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"]); @@ -54,33 +93,33 @@ 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; } - + $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"]); @@ -93,8 +132,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); @@ -103,33 +142,39 @@ 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"]); } - 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; 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/models/Preference.php b/airtime_mvc/application/models/Preference.php index 462025a1b..c6e6fff49 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/application/views/scripts/billing/upgrade.phtml b/airtime_mvc/application/views/scripts/billing/upgrade.phtml index dfdab0707..5d583f245 100644 --- a/airtime_mvc/application/views/scripts/billing/upgrade.phtml +++ b/airtime_mvc/application/views/scripts/billing/upgrade.phtml @@ -120,17 +120,43 @@ function configureByCountry(countryCode) }); } +function promoEligibilityCheck() +{ + 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, "csrf_token": $("#csrf").attr('value')}) + .success(function(data) { + if (data.result == true) { + $("#promo-plan-eligible").show(); + } else if ($("#promo-plan-eligible").is(":visible")) { + $("#promo-plan-eligible").hide(); + } + }); +} + $(document).ready(function() { configureByCountry($("#country").val()); recalculateTotals(); - + $("input[name='newproductid']").change(function() { validatePlan(); recalculateTotals(); + promoEligibilityCheck(); }); $("input[name='newproductbillingcycle']").change(function() { recalculateTotals(); + promoEligibilityCheck(); }); $("#country").change(function() { @@ -170,13 +196,17 @@ $(document).ready(function() {