diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index 0c56fc2bb..f7af625b3 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -101,6 +101,11 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap $csrf_namespace->authtoken = sha1(uniqid(rand(), 1)); $csrf_namespace->setExpirationSeconds(2 * 60 * 60); } + + //Here we are closing the session for writing because otherwise no requests + //in this session will be handled in parallel. This gives a major boost to the perceived performance + //of the application (page load times are more consistent, no lock contention). + session_write_close(); } /** diff --git a/airtime_mvc/application/common/ProvisioningHelper.php b/airtime_mvc/application/common/ProvisioningHelper.php index 580c20e52..605617105 100644 --- a/airtime_mvc/application/common/ProvisioningHelper.php +++ b/airtime_mvc/application/common/ProvisioningHelper.php @@ -10,7 +10,7 @@ class ProvisioningHelper // Parameter values private $dbuser, $dbpass, $dbname, $dbhost, $dbowner, $apikey; private $instanceId; - private $station_name, $description; + private $stationName, $description; public function __construct($apikey) { @@ -40,18 +40,14 @@ class ProvisioningHelper if ($this->dbhost && !empty($this->dbhost)) { $this->setNewDatabaseConnection(); - //if ($this->checkDatabaseExists()) { - // throw new Exception("ERROR: Airtime database already exists"); - //} - if (!$this->checkDatabaseExists()) { - throw new Exception("ERROR: $this->dbname database does not exist."); + throw new DatabaseDoesNotExistException("ERROR: $this->dbname database does not exist."); } //We really want to do this check because all the Propel-generated SQL starts with "DROP TABLE IF EXISTS". //If we don't check, then a second call to this API endpoint would wipe all the tables! if ($this->checkTablesExist()) { - throw new Exception("ERROR: airtime tables already exists"); + throw new DatabaseAlreadyExistsException(); } $this->createDatabaseTables(); @@ -63,11 +59,19 @@ class ProvisioningHelper //All we need to do is create the database tables. $this->initializePrefs(); - } catch (Exception $e) { + } catch (DatabaseDoesNotExistException $e) { http_response_code(400); Logging::error($e->getMessage()); echo $e->getMessage() . PHP_EOL; return; + } catch (DatabaseAlreadyExistsException $e) { + // When we recreate a terminated instance, the process will fail + // if we return a 40x response here. In order to circumvent this, + // just return a 200; we still avoid dropping the existing tables + http_response_code(200); + Logging::info($e->getMessage()); + echo $e->getMessage() . PHP_EOL; + return; } http_response_code(201); @@ -108,7 +112,7 @@ class ProvisioningHelper $this->dbowner = $_POST['dbowner']; $this->instanceId = $_POST['instanceid']; - $this->station_name = $_POST['station_name']; + $this->stationName = $_POST['station_name']; $this->description = $_POST['description']; } @@ -194,8 +198,8 @@ class ProvisioningHelper * Initialize preference values passed from the dashboard (if any exist) */ private function initializePrefs() { - if ($this->station_name) { - Application_Model_Preference::SetStationName($this->station_name); + if ($this->stationName) { + Application_Model_Preference::SetStationName($this->stationName); } if ($this->description) { Application_Model_Preference::SetStationDescription($this->description); @@ -203,3 +207,14 @@ class ProvisioningHelper } } + +class DatabaseAlreadyExistsException extends Exception { + private static $_defaultMessage = "ERROR: airtime tables already exists"; + public function __construct($message = null, $code = 0, Exception $previous = null) { + $message = _((is_null($message) ? self::$_defaultMessage : $message)); + parent::__construct($message, $code, $previous); + } +} + +class DatabaseDoesNotExistException extends Exception {} + diff --git a/airtime_mvc/application/common/TuneIn.php b/airtime_mvc/application/common/TuneIn.php new file mode 100644 index 000000000..6040fb184 --- /dev/null +++ b/airtime_mvc/application/common/TuneIn.php @@ -0,0 +1,48 @@ + _('User Manual'), - 'uri' => "http://sourcefabric.booktype.pro/airtime-25-for-broadcasters/", + 'uri' => "http://sourcefabric.booktype.pro/airtime-pro-for-broadcasters", 'target' => "_blank" ), array( diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 11f6f7889..38c453d9e 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -1,4 +1,5 @@ setLastPlayedTime($now); + + // Push metadata to TuneIn + if (Application_Model_Preference::getTuneinEnabled()) { + $filePropelOrm = $file->getPropelOrm(); + $title = urlencode($filePropelOrm->getDbTrackTitle()); + $artist = urlencode($filePropelOrm->getDbArtistName()); + Application_Common_TuneIn::sendMetadataToTunein($title, $artist); + } } } else { // webstream diff --git a/airtime_mvc/application/controllers/LoginController.php b/airtime_mvc/application/controllers/LoginController.php index 9d2ef2f9d..920a258cc 100644 --- a/airtime_mvc/application/controllers/LoginController.php +++ b/airtime_mvc/application/controllers/LoginController.php @@ -8,6 +8,8 @@ class LoginController extends Zend_Controller_Action public function init() { + //Open the session for writing, because we close it for writing by default in Bootstrap.php as an optimization. + session_start(); } public function indexAction() diff --git a/airtime_mvc/application/controllers/PlaylistController.php b/airtime_mvc/application/controllers/PlaylistController.php index 4f511f4b5..2bbe691e1 100644 --- a/airtime_mvc/application/controllers/PlaylistController.php +++ b/airtime_mvc/application/controllers/PlaylistController.php @@ -31,6 +31,8 @@ class PlaylistController extends Zend_Controller_Action ->addActionContext('empty-content', 'json') ->initContext(); + //This controller writes to the session all over the place, so we're going to reopen it for writing here. + session_start(); //Reopen the session for writing } private function getPlaylist($p_type) diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index b06a9e1ed..741125422 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -32,6 +32,7 @@ class PreferenceController extends Zend_Controller_Action $form = new Application_Form_Preferences(); $values = array(); + session_start(); //Open session for writing. if ($request->isPost()) { $values = $request->getPost(); @@ -56,14 +57,19 @@ class PreferenceController extends Zend_Controller_Action Application_Model_Preference::SetStationLogo($imagePath); } - Application_Model_Preference::SetUploadToSoundcloudOption($values["UploadToSoundcloudOption"]); + Application_Model_Preference::setTuneinEnabled($values["enable_tunein"]); + Application_Model_Preference::setTuneinStationId($values["tunein_station_id"]); + Application_Model_Preference::setTuneinPartnerKey($values["tunein_partner_key"]); + Application_Model_Preference::setTuneinPartnerId($values["tunein_partner_id"]); + + /*Application_Model_Preference::SetUploadToSoundcloudOption($values["UploadToSoundcloudOption"]); Application_Model_Preference::SetSoundCloudDownloadbleOption($values["SoundCloudDownloadbleOption"]); Application_Model_Preference::SetSoundCloudUser($values["SoundCloudUser"]); Application_Model_Preference::SetSoundCloudPassword($values["SoundCloudPassword"]); Application_Model_Preference::SetSoundCloudTags($values["SoundCloudTags"]); Application_Model_Preference::SetSoundCloudGenre($values["SoundCloudGenre"]); Application_Model_Preference::SetSoundCloudTrackType($values["SoundCloudTrackType"]); - Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]); + Application_Model_Preference::SetSoundCloudLicense($values["SoundCloudLicense"]);*/ $this->view->statusMsg = "
". _("Preferences updated.")."
"; $this->view->form = $form; @@ -89,6 +95,8 @@ class PreferenceController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/airtime/preferences/support-setting.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->statusMsg = ""; + session_start(); //Open session for writing. + $form = new Application_Form_SupportSettings(); if ($request->isPost()) { $values = $request->getPost(); @@ -123,6 +131,8 @@ class PreferenceController extends Zend_Controller_Action public function removeLogoAction() { + session_start(); //Open session for writing. + $this->view->layout()->disableLayout(); // Remove reliance on .phtml files to render requests $this->_helper->viewRenderer->setNoRender(true); @@ -140,6 +150,8 @@ class PreferenceController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/airtime/preferences/streamsetting.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + session_start(); //Open session for writing. + // get current settings $setting = Application_Model_StreamSetting::getStreamSetting(); @@ -176,9 +188,14 @@ class PreferenceController extends Zend_Controller_Action $num_of_stream = intval(Application_Model_Preference::GetNumOfStreams()); $form = new Application_Form_StreamSetting(); - $form->addElement('hash', 'csrf', array( - 'salt' => 'unique' - )); + // $form->addElement('hash', 'csrf', array( + // 'salt' => 'unique' + // )); + + $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'); + $form->addElement($csrf_element); $form->setSetting($setting); $form->startFrom(); @@ -425,6 +442,8 @@ class PreferenceController extends Zend_Controller_Action public function setSourceConnectionUrlAction() { + session_start(); //Open session for writing. + $request = $this->getRequest(); $type = $request->getParam("type", null); $url = urldecode($request->getParam("url", null)); @@ -443,6 +462,8 @@ class PreferenceController extends Zend_Controller_Action public function getAdminPasswordStatusAction() { + session_start(); //Open session for writing. + $out = array(); $num_of_stream = intval(Application_Model_Preference::GetNumOfStreams()); for ($i=1; $i<=$num_of_stream; $i++) { @@ -454,4 +475,69 @@ class PreferenceController extends Zend_Controller_Action } $this->_helper->json->sendJson($out); } + + public function deleteAllFilesAction() + { + $this->view->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + + // Only admin users should get here through ACL permissioning + // Only allow POST requests + $method = $_SERVER['REQUEST_METHOD']; + if (!($method == 'POST')) { + $this->getResponse() + ->setHttpResponseCode(405) + ->appendBody(_("Request method not accepted") . ": $method"); + return; + } + + $user = Application_Model_User::getCurrentUser(); + $playlists = $blocks = $streams = []; + + $allPlaylists = CcPlaylistQuery::create()->find(); + foreach ($allPlaylists as $p) { + $playlists[] = $p->getDbId(); + } + + $allBlocks = CcBlockQuery::create()->find(); + foreach ($allBlocks as $b) { + $blocks[] = $b->getDbId(); + } + + $allStreams = CcWebstreamQuery::create()->find(); + foreach ($allStreams as $s) { + $streams[] = $s->getDbId(); + } + + // Delete all playlists, blocks, and streams + Application_Model_Playlist::deletePlaylists($playlists, $user->getId()); + Application_Model_Block::deleteBlocks($blocks, $user->getId()); + Application_Model_Webstream::deleteStreams($streams, $user->getId()); + + try { + // Delete all the cloud files + $CC_CONFIG = Config::getConfig(); + + foreach ($CC_CONFIG["supportedStorageBackends"] as $storageBackend) { + $proxyStorageBackend = new ProxyStorageBackend($storageBackend); + $proxyStorageBackend->deleteAllCloudFileObjects(); + } + } catch(Exception $e) { + Logging::info($e->getMessage()); + } + + // Delete all files from the database + $files = CcFilesQuery::create()->find(); + foreach ($files as $file) { + $storedFile = new Application_Model_StoredFile($file, null); + // Delete the files quietly to avoid getting Sentry errors for + // every S3 file we delete. + $storedFile->delete(true); + } + + $this->getResponse() + ->setHttpResponseCode(200) + ->appendBody("OK"); + } + } diff --git a/airtime_mvc/application/controllers/ProvisioningController.php b/airtime_mvc/application/controllers/ProvisioningController.php index cda59e0db..06ee7a2ab 100644 --- a/airtime_mvc/application/controllers/ProvisioningController.php +++ b/airtime_mvc/application/controllers/ProvisioningController.php @@ -57,13 +57,16 @@ class ProvisioningController extends Zend_Controller_Action /** * Delete the Airtime Pro station's files from Amazon S3 + * + * FIXME: When we deploy this next time, we should ensure that + * this function can only be accessed with POST requests! */ public function terminateAction() { $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); - if (!RestAuth::verifyAuth(true, true, $this)) { + if (!RestAuth::verifyAuth(true, false, $this)) { return; } diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index 452788b23..8d7aa0587 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -1,4 +1,5 @@ getRequest(), $this->getResponse()); + $scheduleController->eventFeedPreloadAction(); + $events = json_encode($scheduleController->view->events); + $this->view->headScript()->appendScript( "var calendarPref = {};\n". "calendarPref.weekStart = ".Application_Model_Preference::GetWeekStartDay().";\n". @@ -58,7 +64,7 @@ class ScheduleController extends Zend_Controller_Action "calendarPref.timeScale = '".Application_Model_Preference::GetCalendarTimeScale()."';\n". "calendarPref.timeInterval = ".Application_Model_Preference::GetCalendarTimeInterval().";\n". "calendarPref.weekStartDay = ".Application_Model_Preference::GetWeekStartDay().";\n". - "var calendarEvents = null;" + "var calendarEvents = $events;" ); $this->view->headScript()->appendFile($baseUrl.'js/contextmenu/jquery.contextMenu.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); @@ -144,7 +150,7 @@ class ScheduleController extends Zend_Controller_Action } else if ($calendar_interval == "agendaWeek") { list($start, $end) = Application_Model_Show::getStartEndCurrentWeekView(); } else if ($calendar_interval == "month") { - list($start, $end) = Application_Model_Show::getStartEndCurrentMonthView(); + list($start, $end) = Application_Model_Show::getStartEndCurrentMonthPlusView(); } else { Logging::error("Invalid Calendar Interval '$calendar_interval'"); } @@ -294,9 +300,28 @@ class ScheduleController extends Zend_Controller_Action } } + /** This is a nasty hack to let us embed the the data the dashboard needs into the HTML response for each page. + * This was originally loaded AFTER page load by AJAX, which is needlessly slow. This should have been templated in. + */ + public static function printCurrentPlaylistForEmbedding() + { + $front = Zend_Controller_Front::getInstance(); + $scheduleController = new ScheduleController($front->getRequest(), $front->getResponse()); + $scheduleController->getCurrentPlaylistAction(); + echo(json_encode($scheduleController->view)); + } + public function getCurrentPlaylistAction() { $range = Application_Model_Schedule::GetPlayOrderRangeOld(); + + // If there is no current track playing update TuneIn so it doesn't + // display outdated metadata + //TODO: find a better solution for this so we don't spam the station on TuneIn + /*if (is_null($range["current"]) && Application_Model_Preference::getTuneinEnabled()) { + Application_Common_TuneIn::updateOfflineMetadata(); + }*/ + $show = Application_Model_Show::getCurrentShow(); /* Convert all UTC times to localtime before sending back to user. */ diff --git a/airtime_mvc/application/controllers/ShowbuilderController.php b/airtime_mvc/application/controllers/ShowbuilderController.php index 3bb30abf4..712bc0348 100644 --- a/airtime_mvc/application/controllers/ShowbuilderController.php +++ b/airtime_mvc/application/controllers/ShowbuilderController.php @@ -42,7 +42,7 @@ class ShowbuilderController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.pluginAPI.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.fnSetFilteringDelay.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.ColVis.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); - $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + //$this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.columnFilter.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); @@ -67,7 +67,8 @@ class ShowbuilderController extends Zend_Controller_Action if (isset($values["Privacy"])) { Application_Model_Preference::SetPrivacyPolicyCheck($values["Privacy"]); } - // unset session + session_start(); //open session for writing again + // unset referrer Zend_Session::namespaceUnset('referrer'); } elseif ($values["Publicise"] == '1' && $form->isValid($values)) { Application_Model_Preference::SetHeadTitle($values["stnName"], $this->view); @@ -88,7 +89,8 @@ class ShowbuilderController extends Zend_Controller_Action if (isset($values["Privacy"])) { Application_Model_Preference::SetPrivacyPolicyCheck($values["Privacy"]); } - // unset session + session_start(); //open session for writing again + // unset referrer Zend_Session::namespaceUnset('referrer'); } else { $logo = Application_Model_Preference::GetStationLogo(); @@ -156,7 +158,7 @@ class ShowbuilderController extends Zend_Controller_Action //populate date range form for show builder. $now = time(); $from = $request->getParam("from", $now); - $to = $request->getParam("to", $now + (24*60*60)); + $to = $request->getParam("to", $now + (3*60*60)); $utcTimezone = new DateTimeZone("UTC"); $displayTimeZone = new DateTimeZone(Application_Model_Preference::GetTimezone()); @@ -341,7 +343,8 @@ class ShowbuilderController extends Zend_Controller_Action $request = $this->getRequest(); $selectedItems = $request->getParam("selectedItem"); $afterItem = $request->getParam("afterItem"); - + + /* $log_vars = array(); $log_vars["url"] = $_SERVER['HTTP_HOST']; $log_vars["action"] = "showbuilder/schedule-move"; @@ -349,6 +352,7 @@ class ShowbuilderController extends Zend_Controller_Action $log_vars["params"]["selected_items"] = $selectedItems; $log_vars["params"]["destination_after_item"] = $afterItem; Logging::info($log_vars); + */ try { $scheduler = new Application_Model_Scheduler(); diff --git a/airtime_mvc/application/controllers/SystemstatusController.php b/airtime_mvc/application/controllers/SystemstatusController.php index dfc2d019b..543fbef70 100644 --- a/airtime_mvc/application/controllers/SystemstatusController.php +++ b/airtime_mvc/application/controllers/SystemstatusController.php @@ -13,16 +13,18 @@ class SystemstatusController extends Zend_Controller_Action public function indexAction() { + /* $services = array( "pypo"=>Application_Model_Systemstatus::GetPypoStatus(), "liquidsoap"=>Application_Model_Systemstatus::GetLiquidsoapStatus(), //"media-monitor"=>Application_Model_Systemstatus::GetMediaMonitorStatus(), ); + */ $partitions = Application_Model_Systemstatus::GetDiskInfo(); $this->view->status = new StdClass; - $this->view->status->services = $services; + //$this->view->status->services = $services; $this->view->status->partitions = $partitions; } } diff --git a/airtime_mvc/application/controllers/UserController.php b/airtime_mvc/application/controllers/UserController.php index 51ac869e7..4c3168f80 100644 --- a/airtime_mvc/application/controllers/UserController.php +++ b/airtime_mvc/application/controllers/UserController.php @@ -16,6 +16,9 @@ class UserController extends Zend_Controller_Action public function addUserAction() { + // Start the session to re-open write permission to the session so we can + // create the namespace for our csrf token verification + session_start(); $CC_CONFIG = Config::getConfig(); $request = $this->getRequest(); @@ -118,7 +121,8 @@ class UserController extends Zend_Controller_Action } public function editUserAction() - { + { + session_start(); //Reopen session for writing. $request = $this->getRequest(); $form = new Application_Form_EditUser(); if ($request->isPost()) { diff --git a/airtime_mvc/application/controllers/UsersettingsController.php b/airtime_mvc/application/controllers/UsersettingsController.php index 9d8154578..35fbb19e1 100644 --- a/airtime_mvc/application/controllers/UsersettingsController.php +++ b/airtime_mvc/application/controllers/UsersettingsController.php @@ -61,28 +61,23 @@ class UsersettingsController extends Zend_Controller_Action public function getTimelineDatatableAction() { - $start = microtime(true); - $data = Application_Model_Preference::getTimelineDatatableSetting(); if (!is_null($data)) { $this->view->settings = $data; } - - $end = microtime(true); - - Logging::debug("getting timeline datatables info took:"); - Logging::debug(floatval($end) - floatval($start)); } public function remindmeAction() { // unset session + session_start(); //open session for writing again Zend_Session::namespaceUnset('referrer'); Application_Model_Preference::SetRemindMeDate(); } public function remindmeNeverAction() { + session_start(); //open session for writing again Zend_Session::namespaceUnset('referrer'); //pass in true to indicate 'Remind me never' was clicked Application_Model_Preference::SetRemindMeDate(true); @@ -91,6 +86,7 @@ class UsersettingsController extends Zend_Controller_Action public function donotshowregistrationpopupAction() { // unset session + session_start(); //open session for writing again Zend_Session::namespaceUnset('referrer'); } diff --git a/airtime_mvc/application/forms/DangerousPreferences.php b/airtime_mvc/application/forms/DangerousPreferences.php new file mode 100644 index 000000000..28d203405 --- /dev/null +++ b/airtime_mvc/application/forms/DangerousPreferences.php @@ -0,0 +1,21 @@ +setDecorators(array( + array('ViewScript', array('viewScript' => 'form/preferences_danger.phtml')) + )); + + $clearLibrary = new Zend_Form_Element_Button('clear_library'); + $clearLibrary->setLabel(_('Delete All Tracks in Library')); + //$submit->removeDecorator('Label'); + $clearLibrary->setAttribs(array('class'=>'btn centered')); + $clearLibrary->setAttrib('onclick', 'deleteAllFiles();'); + $clearLibrary->removeDecorator('DtDdWrapper'); + + $this->addElement($clearLibrary); + } + +} diff --git a/airtime_mvc/application/forms/Preferences.php b/airtime_mvc/application/forms/Preferences.php index 3e5158413..d410ed3c6 100644 --- a/airtime_mvc/application/forms/Preferences.php +++ b/airtime_mvc/application/forms/Preferences.php @@ -12,18 +12,30 @@ class Application_Form_Preferences extends Zend_Form $general_pref = new Application_Form_GeneralPreferences(); - $this->addElement('hash', 'csrf', array( - 'salt' => 'unique', - 'decorators' => array( - 'ViewHelper' - ) - )); + // $this->addElement('hash', 'csrf', array( + // 'salt' => 'unique', + // 'decorators' => array( + // 'ViewHelper' + // ) + // )); + + $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->addSubForm($general_pref, 'preferences_general'); + //tunein form + $tuneinPreferences = new Application_Form_TuneInPreferences(); + $this->addSubForm($tuneinPreferences, 'preferences_tunein'); + $soundcloud_pref = new Application_Form_SoundcloudPreferences(); $this->addSubForm($soundcloud_pref, 'preferences_soundcloud'); + $danger_pref = new Application_Form_DangerousPreferences(); + $this->addSubForm($danger_pref, 'preferences_danger'); + $submit = new Zend_Form_Element_Submit('submit'); $submit->setLabel(_('Save')); //$submit->removeDecorator('Label'); diff --git a/airtime_mvc/application/forms/TuneInPreferences.php b/airtime_mvc/application/forms/TuneInPreferences.php new file mode 100644 index 000000000..7eee0d56f --- /dev/null +++ b/airtime_mvc/application/forms/TuneInPreferences.php @@ -0,0 +1,144 @@ +setDecorators(array( + array('ViewScript', array('viewScript' => 'form/preferences_tunein.phtml')) + )); + + $enableTunein = new Zend_Form_Element_Checkbox("enable_tunein"); + $enableTunein->setDecorators(array( + 'ViewHelper', + 'Errors', + 'Label' + )); + $enableTunein->addDecorator('Label', array('class' => 'enable-tunein')); + $enableTunein->setLabel(_("Push metadata to your station on TuneIn?")); + $enableTunein->setValue(Application_Model_Preference::getTuneinEnabled()); + $enableTunein->setAttrib("class", "block-display"); + $this->addElement($enableTunein); + + $tuneinStationId = new Zend_Form_Element_Text("tunein_station_id"); + $tuneinStationId->setLabel(_("Station ID:")); + $tuneinStationId->setValue(Application_Model_Preference::getTuneinStationId()); + $tuneinStationId->setAttrib("class", "input_text"); + $this->addElement($tuneinStationId); + + $tuneinPartnerKey = new Zend_Form_Element_Text("tunein_partner_key"); + $tuneinPartnerKey->setLabel(_("Partner Key:")); + $tuneinPartnerKey->setValue(Application_Model_Preference::getTuneinPartnerKey()); + $tuneinPartnerKey->setAttrib("class", "input_text"); + $this->addElement($tuneinPartnerKey); + + $tuneinPartnerId = new Zend_Form_Element_Text("tunein_partner_id"); + $tuneinPartnerId->setLabel(_("Partner Id:")); + $tuneinPartnerId->setValue(Application_Model_Preference::getTuneinPartnerId()); + $tuneinPartnerId->setAttrib("class", "input_text"); + $this->addElement($tuneinPartnerId); + } + + public function isValid($data) + { + $valid = true; + // Make request to TuneIn API to test the settings are valid. + // TuneIn does not have an API to make test requests to check if + // the credentials are correct. Therefore we will make a request + // with the commercial flag set to true, which removes the metadata + // from the station on TuneIn. After that, and if the test request + // succeeds, we will make another request with the real metadata. + if ($data["enable_tunein"]) { + $credentialsQryStr = "?partnerId=".$data["tunein_partner_id"]."&partnerKey=".$data["tunein_partner_key"]."&id=".$data["tunein_station_id"]; + $commercialFlagQryStr = "&commercial=true"; + + $metadata = Application_Model_Schedule::getCurrentPlayingTrack(); + + if (is_null($metadata)) { + $qryStr = $credentialsQryStr . $commercialFlagQryStr; + } else { + $metadata["artist"] = empty($metadata["artist"]) ? "n/a" : $metadata["artist"]; + $metadata["title"] = empty($metadata["title"]) ? "n/a" : $metadata["title"]; + $metadataQryStr = "&artist=" . $metadata["artist"] . "&title=" . $metadata["title"]; + + $qryStr = $credentialsQryStr . $metadataQryStr; + } + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, TUNEIN_API_URL . $qryStr); + curl_setopt($ch, CURLOPT_FAILONERROR, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + + $xmlData = curl_exec($ch); + if (curl_error($ch)) { + Logging::error("Failed to reach TuneIn: ". curl_errno($ch)." - ". curl_error($ch) . " - " . curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + if (curl_error($ch) == "The requested URL returned error: 403 Forbidden") { + $this->getElement("enable_tunein")->setErrors(array(_("Invalid TuneIn Settings. Please ensure your TuneIn settings are correct and try again."))); + + $valid = false; + } + } + curl_close($ch); + + if ($valid) { + $xmlObj = new SimpleXMLElement($xmlData); + if (!$xmlObj || $xmlObj->head->status != "200") { + $valid = false; + } else if ($xmlObj->head->status == "200") { + $valid = true; + + // Make another request to TuneIn to update the metadata right away + // and to turn off the commercial flag. + + /*$metadata = Application_Model_Schedule::getCurrentPlayingTrack(); + + if (!is_null($metadata)) { + + Logging::info($metadata); + // Replace empty strings with "n/a" since the TuneIn API will complain + // and return an error that title and/or artist is not set. + $metadata["artist"] = empty($metadata["artist"]) ? "n/a" : $metadata["artist"]; + $metadata["title"] = empty($metadata["title"]) ? "n/a" : $metadata["title"]; + Logging::info($metadata); + + $metadataQryStr = "&artist=" . $metadata["artist"] . "&title=" . $metadata["title"]; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, TUNEIN_API_URL . $qry_str . "&commercial=false" . $metadataQryStr); + curl_setopt($ch, CURLOPT_FAILONERROR, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + + $xmlData = curl_exec($ch); + Logging::info($xmlData); + if (curl_error($ch)) { + Logging::error("Failed to reach TuneIn: " . curl_errno($ch) . " - " . curl_error($ch) . " - " . curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + } + + curl_close($ch); + $xmlObj = new SimpleXMLElement($xmlData); + if (!$xmlObj || $xmlObj->head->status != "200") { + Logging::error("Failed updating metadata on TuneIn"); + } + }*/ + + } + } + } else { + $valid = true; + } + + if (!$valid) { + // Set values to what the user entered since the form is invalid so they + // don't have to enter in the values again and can see what they entered. + $this->getElement("enable_tunein")->setValue($data["enable_tunein"]); + $this->getElement("tunein_partner_key")->setValue($data["tunein_partner_key"]); + $this->getElement("tunein_partner_id")->setValue($data["tunein_partner_id"]); + $this->getElement("tunein_station_id")->setValue($data["tunein_station_id"]); + } + + return $valid; + } +} diff --git a/airtime_mvc/application/layouts/scripts/layout.phtml b/airtime_mvc/application/layouts/scripts/layout.phtml index d7cbad25e..c3f5f5e2d 100644 --- a/airtime_mvc/application/layouts/scripts/layout.phtml +++ b/airtime_mvc/application/layouts/scripts/layout.phtml @@ -34,6 +34,23 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= "scheduled_play_switch"=>$sss['scheduled_play'])) ?> navigation()->menu()->setPartial($partial); ?> + + + + +