diff --git a/airtime_mvc/application/configs/conf.php b/airtime_mvc/application/configs/conf.php index 00af3e337..503880256 100644 --- a/airtime_mvc/application/configs/conf.php +++ b/airtime_mvc/application/configs/conf.php @@ -96,6 +96,12 @@ class Config { $CC_CONFIG['soundcloud-client-id'] = $globalAirtimeConfigValues['soundcloud']['soundcloud_client_id']; $CC_CONFIG['soundcloud-client-secret'] = $globalAirtimeConfigValues['soundcloud']['soundcloud_client_secret']; $CC_CONFIG['soundcloud-redirect-uri'] = $globalAirtimeConfigValues['soundcloud']['soundcloud_redirect_uri']; + if (isset($globalAirtimeConfigValues['facebook']['facebook_app_id'])) { + $CC_CONFIG['facebook-app-id'] = $globalAirtimeConfigValues['facebook']['facebook_app_id']; + $CC_CONFIG['facebook-app-url'] = $globalAirtimeConfigValues['facebook']['facebook_app_url']; + $CC_CONFIG['facebook-app-api-key'] = $globalAirtimeConfigValues['facebook']['facebook_app_api_key']; + } + if(isset($values['demo']['demo'])){ $CC_CONFIG['demo'] = $values['demo']['demo']; diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php index 7ab7b5fb7..2a12003f7 100644 --- a/airtime_mvc/application/configs/navigation.php +++ b/airtime_mvc/application/configs/navigation.php @@ -48,6 +48,12 @@ $pages = array( 'module' => 'default', 'controller' => 'embeddablewidgets', 'action' => 'schedule', + ), + array( + 'label' => _('Facebook'), + 'module' => 'default', + 'controller' => 'embeddablewidgets', + 'action' => 'facebook', ) ) ), diff --git a/airtime_mvc/application/controllers/EmbeddablewidgetsController.php b/airtime_mvc/application/controllers/EmbeddablewidgetsController.php index 210edb969..98158fa7f 100644 --- a/airtime_mvc/application/controllers/EmbeddablewidgetsController.php +++ b/airtime_mvc/application/controllers/EmbeddablewidgetsController.php @@ -26,8 +26,8 @@ class EmbeddableWidgetsController extends Zend_Controller_Action $this->view->player_form = $form; } else { $this->view->player_error_msg = _("To configure and use the embeddable player you must:

- 1. Enable at least one MP3, AAC, or OGG stream under System -> Streams
- 2. Enable the Public Airtime API under System -> Preferences"); + 1. Enable at least one MP3, AAC, or OGG stream under Settings -> Streams
+ 2. Enable the Public Airtime API under Settings -> Preferences"); } } @@ -39,8 +39,83 @@ class EmbeddableWidgetsController extends Zend_Controller_Action if (!$apiEnabled) { $this->view->weekly_schedule_error_msg = _("To use the embeddable weekly schedule widget you must:

- Enable the Public Airtime API under System -> Preferences"); + Enable the Public Airtime API under Settings -> Preferences"); } } + + public function facebookAction() + { + Zend_Layout::getMvcInstance()->assign('parent_page', 'Widgets'); + + $apiEnabled = Application_Model_Preference::GetAllow3rdPartyApi(); + + if (!$apiEnabled) { + $this->view->facebook_error_msg = _("To add the Radio Tab to your Facebook Page, you must first:

+ Enable the Public Airtime API under Settings -> Preferences"); + } + + $CC_CONFIG = Config::getConfig(); + $baseUrl = Application_Common_OsPath::getBaseDir(); + + $facebookAppId = $CC_CONFIG['facebook-app-id']; + $this->view->headScript()->appendScript("var FACEBOOK_APP_ID = " . json_encode($facebookAppId) . ";"); + $this->view->headScript()->appendFile($baseUrl . 'js/airtime/common/facebook.js?' . $CC_CONFIG['airtime_version'], 'text/javascript'); + } + + /** Airtime makes an AJAX POST here after it successfully adds a tab to your Facebook page. */ + public function facebookTabSuccessAction() + { + // disable the view and the layout + $this->view->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + + //TODO: Get list of page IDs (deserialize) + + $request = $this->getRequest(); + if (!$request->isPost()) { + return; + } + + $values = $request->getPost(); + $facebookPageIds = json_decode($values["pages"]); + + $CC_CONFIG = Config::getConfig(); + $facebookMicroserviceUrl = $CC_CONFIG['facebook-app-url']; + $facebookMicroserviceApiKey = $CC_CONFIG['facebook-app-api-key']; + + //Post the page tab ID and station subdomain to the social microservice so that mapping can be saved + //in a database. + foreach ($facebookPageIds as $facebookPageId) + { + $postfields = array(); + $postfields["facebookPageId"] = $facebookPageId; + $postfields["stationId"] = $CC_CONFIG['stationId']; + + $query_string = ""; + foreach ($postfields as $k => $v) $query_string .= "$k=".urlencode($v)."&"; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $facebookMicroserviceUrl); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_FAILONERROR, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $query_string); + curl_setopt($ch, CURLOPT_USERPWD, ":$facebookMicroserviceApiKey"); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + + $jsondata = curl_exec($ch); + if (curl_error($ch)) { + throw new Exception("Failed to reach server in " . __FUNCTION__ . ": " + . curl_errno($ch) . ' - ' . curl_error($ch) . ' - ' . curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + } + curl_close($ch); + + } + + //$arr = json_decode($jsondata, true); # Decode JSON String + + } } diff --git a/airtime_mvc/application/layouts/scripts/layout.phtml b/airtime_mvc/application/layouts/scripts/layout.phtml index 8076b6bd8..fbf24bee6 100644 --- a/airtime_mvc/application/layouts/scripts/layout.phtml +++ b/airtime_mvc/application/layouts/scripts/layout.phtml @@ -227,6 +227,9 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= } ?> +
+ + diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index c3f86606f..8a93bc81a 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -1572,6 +1572,9 @@ class Application_Model_Preference self::setValue("station_podcast_download_counter", empty($c) ? 0 : --$c); } + /** + * @return int either 0 (public) or 1 (private) + */ public static function getStationPodcastPrivacy() { if (!Billing::isStationPodcastAllowed()) { // return private setting diff --git a/airtime_mvc/application/models/Scheduler.php b/airtime_mvc/application/models/Scheduler.php index de3b73ca1..f3f0c508c 100644 --- a/airtime_mvc/application/models/Scheduler.php +++ b/airtime_mvc/application/models/Scheduler.php @@ -482,10 +482,15 @@ class Application_Model_Scheduler ->orderByDbStarts() ->find($this->con); + $now = new DateTime("now", new DateTimeZone("UTC")); $itemStartDT = $instance->getDbStarts(null); foreach ($schedule as $item) { $itemEndDT = $this->findEndTime($itemStartDT, $item->getDbClipLength()); - Logging::info($itemEndDT); + // If the track has already ended, don't change it. + if ($itemEndDT < $now) { + $itemStartDT = $itemEndDT; + continue; + } $item->setDbStarts($itemStartDT) ->setDbEnds($itemEndDT) ->save($this->con); @@ -515,10 +520,15 @@ class Application_Model_Scheduler ->orderByDbStarts() ->find($this->con); + $now = new DateTime("now", new DateTimeZone("UTC")); $itemStartDT = $instance->getDbStarts(null); foreach ($schedule as $item) { - $itemEndDT = $this->findEndTime($itemStartDT, $item->getDbClipLength()); + // If the track has already ended, don't change it. + if ($itemEndDT < $now) { + $itemStartDT = $itemEndDT; + continue; + } $item->setDbStarts($itemStartDT) ->setDbEnds($itemEndDT); @@ -1191,7 +1201,7 @@ class Application_Model_Scheduler foreach ($removedItems as $removedItem) { $instance = $removedItem->getCcShowInstances($this->con); - $effectedInstanceIds[] = $instance->getDbId(); + $effectedInstanceIds[$instance->getDbId()] = $instance->getDbId(); //check if instance is linked and if so get the schedule items //for all linked instances so we can delete them too diff --git a/airtime_mvc/application/views/scripts/billing/upgrade.phtml b/airtime_mvc/application/views/scripts/billing/upgrade.phtml index 83077cb9e..b3b6b09f0 100644 --- a/airtime_mvc/application/views/scripts/billing/upgrade.phtml +++ b/airtime_mvc/application/views/scripts/billing/upgrade.phtml @@ -193,13 +193,13 @@ $(document).ready(function() { - 64kbps Stream Quality + Up to 64kbps Stream Quality - 64kbps and 128kbps Stream Quality + Up to 128kbps Stream Quality - 64kbps and 196kbps Stream Quality + Up to 196kbps Stream Quality - 64kbps, 128kbps, and 196kbps Stream Quality + Up to 196kbps Stream Quality @@ -222,7 +222,18 @@ $(document).ready(function() { 150GB Storage - + + + No Built-in Podcast + + 2,000 Podcast Episode Downloads + + 5,000 Podcast Episode Downloads + + + 10,000 Podcast Episode Downloads + + Ticket, Email, Forum Support diff --git a/airtime_mvc/application/views/scripts/embeddablewidgets/facebook.phtml b/airtime_mvc/application/views/scripts/embeddablewidgets/facebook.phtml new file mode 100644 index 000000000..172c7b73a --- /dev/null +++ b/airtime_mvc/application/views/scripts/embeddablewidgets/facebook.phtml @@ -0,0 +1,37 @@ +
+ + + +

+
+ +

+ +
+ facebook_error_msg; ?> +
+ + +
+

Preview:

+ +
+ +
+ +
+
+ + +
+

Tips:

+

+ To make the tab more visible on your Facebook page, click "More", and "Manage Tabs":
+ ">

+ + Then, drag the Radio Player item higher in the list, and click Save. It will now appear as one of the default tabs instead of being buried under "More":
+ "> + +

+
+
\ No newline at end of file diff --git a/airtime_mvc/public/images/doc/facebook_widget1.png b/airtime_mvc/public/images/doc/facebook_widget1.png new file mode 100644 index 000000000..6b4a8cbb8 Binary files /dev/null and b/airtime_mvc/public/images/doc/facebook_widget1.png differ diff --git a/airtime_mvc/public/images/doc/facebook_widget2.png b/airtime_mvc/public/images/doc/facebook_widget2.png new file mode 100644 index 000000000..a8e4b945d Binary files /dev/null and b/airtime_mvc/public/images/doc/facebook_widget2.png differ diff --git a/airtime_mvc/public/js/airtime/common/facebook.js b/airtime_mvc/public/js/airtime/common/facebook.js new file mode 100644 index 000000000..ecd803ca8 --- /dev/null +++ b/airtime_mvc/public/js/airtime/common/facebook.js @@ -0,0 +1,89 @@ + +$(document).ready(function() { + $("#facebook-login").click(function() { + AIRTIME.facebook.promptForFacebookPage(); + }); +}); + + +window.fbAsyncInit = function() { + FB.init({ + appId : FACEBOOK_APP_ID, + xfbml : true, + version : 'v2.4' + }); +}; + +var AIRTIME = (function(AIRTIME) { + + //Module initialization + if (AIRTIME.facebook === undefined) { + AIRTIME.facebook = {}; + } + + var mod = AIRTIME.facebook; + + (function (d, s, id) { + var js, fjs = d.getElementsByTagName(s)[0]; + if (d.getElementById(id)) { + return; + } + js = d.createElement(s); + js.id = id; + js.src = "//connect.facebook.net/en_US/sdk.js"; + fjs.parentNode.insertBefore(js, fjs); + }(document, 'script', 'facebook-jssdk')); + + mod.promptForFacebookPage = function() { + FB.login(function (response) { + if (response.authResponse) { + mod.getPagesOwnedByUser(response.authResponse.userID, response.authResponse.accessToken); + mod.addPageTab(); + + } else { + console.log('Authorization failed.'); + } + }, {scope: 'manage_pages'}); + } + + mod.getPagesOwnedByUser = function(userId, accessToken) { + FB.api('/' + userId + '/accounts', function (response) { + console.log(response); + }, {access_token: accessToken}); + } + + mod.addPageTab = function() { + FB.ui( + { method: 'pagetab' }, + function (resp) { + console.log("response:"); + console.log(resp); + var pageIdList = []; + var tabs = resp["tabs_added"]; + + if ((tabs != undefined) && (Object.keys(tabs).length > 0)) { + for (var pageId in tabs) { + pageIdList.push(pageId); + } + + //POST these back to Airtime, which will then proxy it over to our social app. (multiple requests from Airtime) + $.post('facebook-tab-success', { "pages" : JSON.stringify(pageIdList) }, function() { + alert("Successfully added to your Facebook page!"); + }).done(function() { + + }).fail(function() { + alert("Sorry, an error occurred and we were unable to add the widget to your Facebook page."); + }); + } + }, + { + app_id: FACEBOOK_APP_ID, + //redirect_uri: 'https://localhost' + } + ); + } + + return AIRTIME; + +}(AIRTIME || {})); + diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 3bf80d800..1b9f82a2c 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -542,6 +542,18 @@ var AIRTIME = (function(AIRTIME) { } }; + mod.handleAjaxError = function (r) { + // If the request was denied due to permissioning + if (r.status === 403) { + // Hide the processing div + var wrapper = $("#library_display_wrapper"); + wrapper.find(".dt-process-rel").hide(); + wrapper.find('.empty_placeholder_text').text($.i18n._("You don't have permission to view the library.")); + wrapper.find('.empty_placeholder').show(); + } + }; + + libraryInit = function() { $libContent = $("#library_content"); @@ -706,7 +718,7 @@ var AIRTIME = (function(AIRTIME) { "url": sSource, "data": aoData, "success": fnCallback, - "error": handleAjaxError + "error": mod.handleAjaxError }).done(function (data) { var filterMessage = $libContent.find('.filter-message'); if (data.iTotalRecords > data.iTotalDisplayRecords) { @@ -904,17 +916,6 @@ var AIRTIME = (function(AIRTIME) { } - function handleAjaxError(r) { - // If the request was denied due to permissioning - if (r.status === 403) { - // Hide the processing div - $("#library_display_wrapper").find(".dt-process-rel").hide(); - $('.empty_placeholder_text').text($.i18n._("You don't have permission to view the library.")); - - $('.empty_placeholder').show(); - } - } - var selected = $("a[href$='"+location.hash+"']"), table; if (selected.parent().data("selection-id") == AIRTIME.library.MediaTypeIntegerEnum.PODCAST) { table = mod.DataTableTypeEnum.PODCAST; diff --git a/airtime_mvc/public/js/airtime/widgets/table.js b/airtime_mvc/public/js/airtime/widgets/table.js index aaf3b75b9..dc23f8df8 100644 --- a/airtime_mvc/public/js/airtime/widgets/table.js +++ b/airtime_mvc/public/js/airtime/widgets/table.js @@ -399,15 +399,9 @@ var AIRTIME = (function(AIRTIME) { Table.prototype._handleAjaxError = function(r) { // If the request was denied due to permissioning if (r.status === 403) { - // Hide the processing div - /* - $("#library_display_wrapper").find(".dt-process-rel").hide(); - $.getJSON( "ajax/library_placeholders.json", function( data ) { - $('.empty_placeholder_text').text($.i18n._(data.unauthorized)); - }) ; - - $('.empty_placeholder').show(); - */ + $(".dt-process-rel").hide(); + $('.empty_placeholder_text').text($.i18n._("You don't have permission to view this resource.")); + $('.empty_placeholder').show(); } };