Merge branch 'saas-dev' into saas-dev-facebook-radio

This commit is contained in:
Albert Santoni 2015-11-19 15:09:29 -05:00
commit 25c54ce974
183 changed files with 52601 additions and 1553 deletions

View file

@ -23,7 +23,6 @@ class ApiController extends Zend_Controller_Action
"shows",
"show-tracks",
"show-schedules",
"station-logo",
"show-logo",
"stream-m3u"
);
@ -73,6 +72,8 @@ class ApiController extends Zend_Controller_Action
->addActionContext('update-replay-gain-value' , 'json')
->addActionContext('update-cue-values-by-silan' , 'json')
->addActionContext('get-usability-hint' , 'json')
->addActionContext('poll-celery' , 'json')
->addActionContext('recalculate-schedule' , 'json') //RKTN-260
->initContext();
}
@ -100,6 +101,14 @@ class ApiController extends Zend_Controller_Action
exit();
}
public function pollCeleryAction() {
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$taskManager = TaskManager::getInstance();
$taskManager->runTask(TaskFactory::CELERY);
}
public function versionAction()
{
$this->_helper->json->sendJson( array(
@ -527,13 +536,13 @@ class ApiController extends Zend_Controller_Action
$mime_type = finfo_buffer($f, $blob, FILEINFO_MIME_TYPE);
finfo_close($f);
header("Content-type: " . $mime_type);
header("Content-Type: " . $mime_type);
echo $blob;
} else {
header('HTTP/1.0 401 Unauthorized');
print _('You are not allowed to access this resource. ');
print _('You are not allowed to access this resource.');
exit;
}
}
}
public function scheduleAction()
@ -1516,9 +1525,40 @@ class ApiController extends Zend_Controller_Action
$streamData = Application_Model_StreamSetting::getEnabledStreamData();
foreach ($streamData as $stream) {
$m3uFile .= "#EXTINF,".$stationName." - " . strtoupper($stream['codec']) . "\r\n";
$m3uFile .= "#EXTINF," . $stationName . " - " . strtoupper($stream['codec']) . "\r\n";
$m3uFile .= $stream['url'] . "\r\n\r\n";
}
echo $m3uFile;
}
public function recalculateScheduleAction()
{
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
Zend_Session::start();
$scheduler = new Application_Model_Scheduler();
session_write_close();
$now = new DateTime("now", new DateTimeZone("UTC"));
$showInstances = CcShowInstancesQuery::create()
->filterByDbStarts($now, Criteria::GREATER_THAN)
//->filterByDbModifiedInstance(false)
->orderByDbStarts()
->find();
//->find($this->con);
$total = $showInstances->count();
$progress = 0;
foreach ($showInstances as $instance) {
echo(round(floatval($progress / $total)*100) . "% - " . $instance->getDbId() . "\n<br>");
flush();
ob_flush();
//while(@ob_end_clean());
$scheduler->removeGaps2($instance->getDbId());
$progress += 1;
}
echo("Recalculated $total shows.");
}
}

View file

@ -21,6 +21,12 @@ class BillingController extends Zend_Controller_Action {
public function upgradeAction()
{
//If you're not on a trial and you're suspended, we don't let you access the plans page and redirect you to the invoices
//page to force you to pay your bills first.
$isTrial = (Application_Model_Preference::GetPlanLevel() == 'trial');
if (!$isTrial && (Application_Model_Preference::getProvisioningStatus() == PROVISIONING_STATUS_SUSPENDED)) {
$this->_redirect('billing/invoices');
}
Zend_Layout::getMvcInstance()->assign('parent_page', 'Billing');

View file

@ -1,5 +1,7 @@
<?php
require_once(__DIR__.'/../common/widgets/Table.php');
class DashboardController extends Zend_Controller_Action
{
@ -99,11 +101,7 @@ class DashboardController extends Zend_Controller_Action
$this->_helper->layout->setLayout('livestream');
$logo = Application_Model_Preference::GetStationLogo();
if ($logo === DEFAULT_LOGO_PLACEHOLDER) {
$this->view->logo = "/".DEFAULT_LOGO_FILE;
} else {
$this->view->logo = "data:image/png;base64,".$logo;
}
$this->view->logo = "data:image/png;base64,".$logo;
}
public function helpAction()
@ -117,4 +115,17 @@ class DashboardController extends Zend_Controller_Action
$this->view->airtime_version = Application_Model_Preference::GetAirtimeVersion();
}
public function tableTestAction()
{
Zend_Layout::getMvcInstance()->assign('parent_page', 'Help');
$CC_CONFIG = Config::getConfig();
$baseUrl = Application_Common_OsPath::getBaseDir();
$headScript = $this->view->headScript();
AirtimeTableView::injectTableJavaScriptDependencies($headScript, $baseUrl, $CC_CONFIG['airtime_version']);
$this->view->headScript()->appendFile($baseUrl.'js/airtime/widgets/table-example.js?'.$CC_CONFIG['airtime_version']);
}
}

View file

@ -55,6 +55,10 @@ class EmbedController extends Zend_Controller_Action
array_push($availableDesktopStreams, $s);
}
}
} else if ($stream == "file") {
$this->view->playerMode = "file";
$this->view->streamURL = json_encode($request->getParam("file_url"));
$this->view->codec = $request->getParam("file_codec");
} elseif (!empty($stream)) {
$this->view->playerMode = "manual";
$selectedStreamData = $streamData[$stream];

View file

@ -0,0 +1,59 @@
<?php
class FeedsController extends Zend_Controller_Action
{
public function stationRssAction()
{
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
if ((Application_Model_Preference::getStationPodcastPrivacy()
&& $this->getRequest()->getParam("sharing_token") != Application_Model_Preference::getStationPodcastDownloadKey())
&& !RestAuth::verifyAuth(true, true, $this)) {
$this->getResponse()
->setHttpResponseCode(401);
return;
}
$rssData = Application_Service_PodcastService::createStationRssFeed();
$mimeType = "text/xml";
header("Content-Type: $mimeType; charset=UTF-8");
if (isset($_SERVER['HTTP_RANGE'])) {
header('HTTP/1.1 206 Partial Content');
} else {
header('HTTP/1.1 200 OK');
}
header("Content-Type: $mimeType");
header("Content-Transfer-Encoding: binary");
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Accept-Ranges: bytes');
$size = strlen($rssData);
$begin = 0;
$end = $size - 1;
//ob_start(); //Must start a buffer here for these header() functions
if (isset($_SERVER['HTTP_RANGE'])) {
if (preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) {
$begin = intval($matches[1]);
if (!empty($matches[2])) {
$end = intval($matches[2]);
}
}
}
if ($size > 0) {
header('Content-Length:' . (($end - $begin) + 1));
if (isset($_SERVER['HTTP_RANGE'])) {
header("Content-Range: bytes $begin-$end/$size");
}
}
echo $rssData;
}
}

View file

@ -13,14 +13,24 @@ class IndexController extends Zend_Controller_Action
$CC_CONFIG = Config::getConfig();
$baseUrl = Application_Common_OsPath::getBaseDir();
$this->view->headTitle(Application_Model_Preference::GetHeadTitle());
$this->view->headScript()->appendFile($baseUrl . 'js/libs/jquery-1.8.3.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript')
->appendFile($baseUrl . 'js/i18n/jquery.i18n.js?' . $CC_CONFIG['airtime_version'], 'text/javascript')
->appendFile($baseUrl . 'locale/general-translation-table?' . $CC_CONFIG['airtime_version'], 'text/javascript')
->appendScript("$.i18n.setDictionary(general_dict)")
->appendScript("var baseUrl='$baseUrl'");
$this->view->headScript()->appendFile($baseUrl . 'js/libs/jquery-1.8.3.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl . 'js/i18n/jquery.i18n.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl . 'locale/general-translation-table?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendScript("$.i18n.setDictionary(general_dict)");
$this->view->headScript()->appendScript("var baseUrl='$baseUrl'");
//jplayer
$this->view->headScript()->appendFile($baseUrl.'js/jplayer/jquery.jplayer.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/jplayer/jplayer.playlist.min.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headLink()->setStylesheet($baseUrl.'css/radio-page/radio-page.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/embed/weekly-schedule-widget.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/radio-page/station-podcast.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/bootstrap.css?'.$CC_CONFIG['airtime_version']);
//jplayer control buttons
$this->view->headLink()->appendStylesheet($baseUrl.'css/redmond/jquery-ui-1.8.8.custom.css?'.$CC_CONFIG['airtime_version']);
$this->_helper->layout->setLayout('radio-page');
@ -40,6 +50,40 @@ class IndexController extends Zend_Controller_Action
}
$this->view->displayLoginButton = $displayRadioPageLoginButtonValue;
//station feed episodes
$stationPodcastId = Application_Model_Preference::getStationPodcastId();
$podcastEpisodesService = new Application_Service_PodcastEpisodeService();
$episodes = $podcastEpisodesService->getPodcastEpisodes($stationPodcastId, 0, 0, PodcastEpisodesPeer::PUBLICATION_DATE, "DESC");
foreach ($episodes as $e => $v) {
$episodes[$e]["CcFiles"]["track_title"] = htmlspecialchars($v["CcFiles"]["track_title"], ENT_QUOTES);
$episodes[$e]["CcFiles"]["artist_name"] = htmlspecialchars($v["CcFiles"]["artist_name"], ENT_QUOTES);
$pubDate = explode(" ", $v["publication_date"]);
$episodes[$e]["publication_date"] = $pubDate[0];
$length = explode(".", $v["CcFiles"]["length"]);
$episodes[$e]["CcFiles"]["length"] = $length[0];
$episodes[$e]["mime"] = FileDataHelper::getAudioMimeTypeArray()[$v["CcFiles"]["mime"]];
if (is_null($v["CcFiles"]["description"])) {
$episodes[$e]["CcFiles"]["description"] = "";
}
}
$episodePages = array_chunk($episodes, 10);
$this->view->episodes = json_encode($episodePages, JSON_FORCE_OBJECT);
$this->view->displayRssTab = (!Application_Model_Preference::getStationPodcastPrivacy());
$stationPodcast = PodcastQuery::create()->findOneByDbId($stationPodcastId);
$url = $stationPodcast->getDbUrl();
$this->view->stationPodcastRssUrl = $url;
$stationName = Application_Model_Preference::GetStationName();
$this->view->podcastTitle = sprintf(_("%s Podcast"), !empty($stationName) ? $stationName : $CC_CONFIG["stationId"]);
$this->view->emptyPodcastMessage = _("No tracks have been published yet.");
}
public function mainAction()

View file

@ -18,6 +18,7 @@ class LibraryController extends Zend_Controller_Action
->addActionContext('get-file-metadata', 'html')
->addActionContext('set-num-entries', 'json')
->addActionContext('edit-file-md', 'json')
->addActionContext('publish-dialog', 'html')
->initContext();
}
@ -120,7 +121,8 @@ class LibraryController extends Zend_Controller_Action
}
if ($isAdminOrPM || $file->getFileOwnerId() == $user->getId()) {
$menu["del"] = array("name"=> _("Delete"), "icon" => "delete", "url" => $baseUrl."library/delete");
$menu["edit"] = array("name"=> _("Edit Metadata"), "icon" => "edit", "url" => $baseUrl."library/edit-file-md/id/{$id}");
$menu["edit"] = array("name"=> _("Edit..."), "icon" => "edit", "url" => $baseUrl."library/edit-file-md/id/{$id}");
$menu["publish"] = array("name"=> _("Publish..."), "icon" => "soundcloud", "url" => $baseUrl."library/publish/id/{$id}");
}
// It's important that we always return the parent id (cc_files id)
@ -133,33 +135,21 @@ class LibraryController extends Zend_Controller_Action
// SOUNDCLOUD MENU OPTION
$ownerId = empty($obj) ? $file->getFileOwnerId() : $obj->getCreatorId();
if ($isAdminOrPM || $ownerId == $user->getId()) {
$soundcloudService = new SoundcloudService();
$soundcloudService = new Application_Service_SoundcloudService();
if ($type === "audioclip" && $soundcloudService->hasAccessToken()) {
//create a menu separator
$menu["sep1"] = "-----------";
//create a sub menu for Soundcloud actions.
$menu["soundcloud"] = array("name" => _(SOUNDCLOUD), "icon" => "soundcloud", "items" => array());
$serviceId = $soundcloudService->getServiceId($id);
if (!is_null($file) && $serviceId != 0) {
$trackRef = ThirdPartyTrackReferencesQuery::create()
->filterByDbService(SOUNDCLOUD_SERVICE_NAME)
->findOneByDbFileId($id);
//create a menu separator
$menu["sep1"] = "-----------";
//create a sub menu for Soundcloud actions.
$menu["soundcloud"] = array("name" => _(SOUNDCLOUD), "icon" => "soundcloud", "items" => array());
$menu["soundcloud"]["items"]["view"] = array("name" => _("View track"), "icon" => "soundcloud", "url" => $baseUrl . "soundcloud/view-on-sound-cloud/id/{$id}");
$menu["soundcloud"]["items"]["remove"] = array("name" => _("Remove track"), "icon" => "soundcloud", "url" => $baseUrl . "soundcloud/delete/id/{$id}");
} else {
// If a reference exists for this file ID, that means the user has uploaded the track
// but we haven't yet gotten a response from Celery, so disable the menu item
if ($soundcloudService->referenceExists($id)) {
$menu["soundcloud"]["items"]["upload"] = array(
"name" => _("Upload track"), "icon" => "soundcloud",
"url" => $baseUrl . "soundcloud/upload/id/{$id}", "disabled" => true
);
} else {
$menu["soundcloud"]["items"]["upload"] = array(
"name" => _("Upload track"), "icon" => "soundcloud",
"url" => $baseUrl . "soundcloud/upload/id/{$id}"
);
}
$menu["soundcloud"]["items"]["update"] = array("name" => _("Update track"), "icon" => "soundcloud", "url" => $baseUrl . "soundcloud/update/id/{$trackRef->getDbForeignId()}");
}
}
}
@ -181,7 +171,7 @@ class LibraryController extends Zend_Controller_Action
if ($obj_sess->id !== $id && $screen == "playlist") {
if ($isAdminOrPM || $obj->getCreatorId() == $user->getId()) {
$menu["edit"] = array("name"=> _("Edit"), "icon" => "edit");
$menu["edit"] = array("name"=> _("Edit..."), "icon" => "edit");
}
}
@ -203,7 +193,7 @@ class LibraryController extends Zend_Controller_Action
}
if ($isAdminOrPM || $obj->getCreatorId() == $user->getId()) {
if ($screen == "playlist") {
$menu["edit"] = array("name"=> _("Edit"), "icon" => "edit", "url" => $baseUrl."library/edit-file-md/id/{$id}");
$menu["edit"] = array("name"=> _("Edit..."), "icon" => "edit", "url" => $baseUrl."library/edit-file-md/id/{$id}");
}
$menu["del"] = array("name"=> _("Delete"), "icon" => "delete", "url" => $baseUrl."library/delete");
}
@ -379,7 +369,6 @@ class LibraryController extends Zend_Controller_Action
$this->view->form = $form;
$this->view->id = $file_id;
$this->view->title = $file->getPropelOrm()->getDbTrackTitle();
$this->view->type = "md";
$this->view->html = $this->view->render('library/edit-file-md.phtml');
}
@ -453,4 +442,16 @@ class LibraryController extends Zend_Controller_Action
Logging::info($e->getMessage());
}
}
public function publishDialogAction() {
$this->_helper->layout->disableLayout();
if (!Billing::isStationPodcastAllowed()) {
$this->renderScript("podcast/featureupgrade-pane.phtml");
}
//This just spits out publish-dialog.phtml!
}
}

View file

@ -28,6 +28,7 @@ class LocaleController extends Zend_Controller_Action
$translations = array (
//common/common.js
"Audio Player" => _("Audio Player"),
"Something went wrong!" => _("Something went wrong!"),
//dashboard/dashboard.js
"Recording:" => _("Recording:"),
"Master Stream" => _("Master Stream"),
@ -57,6 +58,7 @@ class LocaleController extends Zend_Controller_Action
"You haven't added any webstreams" => _("You haven't added any webstreams"),
"Learn about tracks" => _("Learn about tracks"),
"Learn about playlists" => _("Learn about playlists"),
"Learn about podcasts" => _("Learn about podcasts"),
"Learn about smart blocks" => _("Learn about smart blocks"),
"Learn about webstreams" => _("Learn about webstreams"),
"Click 'New' to create one." => _("Click 'New' to create one."),
@ -108,6 +110,10 @@ class LocaleController extends Zend_Controller_Action
"Are you sure you want to delete the selected item?" => _("Are you sure you want to delete the selected item?"),
"Uploading in progress..." => _("Uploading in progress..."),
"Retrieving data from the server..." => _("Retrieving data from the server..."),
// SOUNDCLOUD
"Are you sure? SoundCloud stats and comments for this track will be permanently removed." => "Are you sure? SoundCloud stats and comments for this track will be permanently removed.",
"Your track is being deleted from SoundCloud" => "Your track is being deleted from SoundCloud",
"Your track is being uploaded and will appear on SoundCloud in a couple of minutes" => "Your track is being uploaded and will appear on SoundCloud in a couple of minutes",
"The soundcloud id for this file is: " => _("The soundcloud id for this file is: "),
"There was an error while uploading to soundcloud." => _("There was an error while uploading to soundcloud."),
"Error code: " => _("Error code: "),
@ -116,6 +122,7 @@ class LocaleController extends Zend_Controller_Action
"Input must be a number" => _("Input must be a number"),
"Input must be in the format: yyyy-mm-dd" => _("Input must be in the format: yyyy-mm-dd"),
"Input must be in the format: hh:mm:ss.t" => _("Input must be in the format: hh:mm:ss.t"),
"My Podcast" => _("My Podcast"),
//library/plupload.js
"You are currently uploading files. %sGoing to another screen will cancel the upload process. %sAre you sure you want to leave the page?"
=> _("You are currently uploading files. %sGoing to another screen will cancel the upload process. %sAre you sure you want to leave the page?"),

View file

@ -102,7 +102,7 @@ class PlaylistController extends Zend_Controller_Action
$this->view->form = $form;
$this->view->obj = $obj;
$this->view->type = "sb";
//$this->view->type = "sb";
$this->view->id = $obj->getId();
if ($isJson) {
@ -112,7 +112,7 @@ class PlaylistController extends Zend_Controller_Action
}
} else {
$this->view->obj = $obj;
$this->view->type = "pl";
//$this->view->type = "pl";
$this->view->id = $obj->getId();
if ($isJson) {
return $this->view->html = $this->view->render($viewPath);
@ -217,7 +217,6 @@ class PlaylistController extends Zend_Controller_Action
$id = $this->_getParam('id', null);
$type = $this->_getParam('type');
$objInfo = Application_Model_Library::getObjInfo($type);
Logging::info("editing {$type} {$id}");
// if (!is_null($id)) {
Application_Model_Library::changePlaylist($id, $type);
@ -573,7 +572,7 @@ class PlaylistController extends Zend_Controller_Action
$this->view->result = 1;
}
$this->view->name = $bl->getName();
$this->view->type = "sb";
//$this->view->type = "sb";
$this->view->id = $bl->getId();
$this->view->modified = $bl->getLastModified("U");
} else if ($params['type'] == 'playlist') {

View file

@ -0,0 +1,41 @@
<?php
class PodcastController extends Zend_Controller_Action {
public function init() {
$CC_CONFIG = Config::getConfig();
$baseUrl = Application_Common_OsPath::getBaseDir();
$headScript = $this->view->headScript();
AirtimeTableView::injectTableJavaScriptDependencies($headScript, $baseUrl, $CC_CONFIG['airtime_version']);
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/events/library_showbuilder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/widgets/table.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/podcast.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headLink()->appendStylesheet($baseUrl.'css/datatables/css/ColVis.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/datatables/css/dataTables.colReorder.min.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/station_podcast.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/dashboard.css?'.$CC_CONFIG['airtime_version']);
}
/**
* Renders the Station podcast view
*/
public function stationAction() {
if (!Billing::isStationPodcastAllowed()) {
$this->render("featureupgrade-page");
return;
}
$stationPodcastId = Application_Model_Preference::getStationPodcastId();
$podcast = Application_Service_PodcastService::getPodcastById($stationPodcastId);
$this->view->podcast = json_encode($podcast);
$this->view->form = new Application_Form_StationPodcast();
}
}

View file

@ -66,8 +66,10 @@ class PreferenceController extends Zend_Controller_Action
Application_Model_Preference::setTuneinPartnerId($values["tunein_partner_id"]);
// SoundCloud Preferences
Application_Model_Preference::setDefaultSoundCloudLicenseType($values["SoundCloudLicense"]);
Application_Model_Preference::setDefaultSoundCloudSharingType($values["SoundCloudSharing"]);
if (Billing::isStationPodcastAllowed()) {
Application_Model_Preference::setDefaultSoundCloudLicenseType($values["SoundCloudLicense"]);
Application_Model_Preference::setDefaultSoundCloudSharingType($values["SoundCloudSharing"]);
}
$this->view->statusMsg = "<div class='success'>". _("Preferences updated.")."</div>";
$form = new Application_Form_Preferences();
@ -83,6 +85,28 @@ class PreferenceController extends Zend_Controller_Action
$this->view->form = $form;
}
public function stationPodcastSettingsAction() {
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$values = json_decode($this->getRequest()->getRawBody());
if (!Application_Model_Preference::getStationPodcastPrivacy() && $values->stationPodcastPrivacy == 1) {
// Refresh the download key when enabling privacy
Application_Model_Preference::setStationPodcastDownloadKey();
}
// Append sharing token (download key) to Station podcast URL
$stationPodcast = PodcastQuery::create()->findOneByDbId(Application_Model_Preference::getStationPodcastId());
$key = Application_Model_Preference::getStationPodcastDownloadKey();
$url = Application_Common_HTTPHelper::getStationUrl() .
(((int) $values->stationPodcastPrivacy) ? "feeds/station-rss?sharing_token=$key" : "feeds/station-rss");
$stationPodcast->setDbUrl($url)->save();
Application_Model_Preference::setStationPodcastPrivacy($values->stationPodcastPrivacy);
$this->_helper->json->sendJson(array("url" => $url));
}
public function supportSettingAction()
{
$CC_CONFIG = Config::getConfig();

View file

@ -0,0 +1,20 @@
<?php
class RenderController extends Zend_Controller_Action {
public function init() {
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$csrf_namespace = new Zend_Session_Namespace('csrf_namespace');
$csrf_element = new Zend_Form_Element_Hidden('csrf_token');
$csrf_element->setValue($csrf_namespace->authtoken)->setRequired('true')->removeDecorator('HtmlTag')->removeDecorator('Label');
$this->view->csrf = $csrf_element;
}
public function podcastUrlDialogAction() {
$path = 'podcast/podcast_url_dialog.phtml';
$this->_helper->json->sendJson(array("html"=>$this->view->render($path)));
}
}

View file

@ -90,13 +90,8 @@ class ScheduleController extends Zend_Controller_Action
$this->view->headLink()->appendStylesheet($baseUrl.'css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);
//Start Show builder JS/CSS requirements
$this->view->headScript()->appendFile($baseUrl.'js/datatables/js/jquery.dataTables.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$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.min.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');
$headScript = $this->view->headScript();
AirtimeTableView::injectTableJavaScriptDependencies($headScript, $baseUrl, $CC_CONFIG['airtime_version']);
$this->view->headScript()->appendFile($baseUrl.'js/libs/moment.min.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/libs/moment-timezone-with-data-2010-2020.min.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');

View file

@ -32,14 +32,6 @@ class ShowbuilderController extends Zend_Controller_Action
$this->view->headLink()->appendStylesheet($baseUrl . 'css/redmond/jquery-ui-1.8.8.custom.css?' . $CC_CONFIG['airtime_version']);
$this->view->headScript()->appendFile($baseUrl.'js/contextmenu/jquery.contextMenu.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/datatables/js/jquery.dataTables.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$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.min.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.FixedHeader.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/datatables/plugin/dataTables.columnFilter.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/blockui/jquery.blockUI.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
@ -51,23 +43,28 @@ class ShowbuilderController extends Zend_Controller_Action
$this->view->headLink()->appendStylesheet($baseUrl.'css/datatables/css/dataTables.colReorder.min.css?'.$CC_CONFIG['airtime_version']);
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/events/library_showbuilder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$headScript = $this->view->headScript();
AirtimeTableView::injectTableJavaScriptDependencies($headScript, $baseUrl, $CC_CONFIG['airtime_version']);
// PLUPLOAD
$this->view->headScript()->appendFile($baseUrl.'js/libs/dropzone.min.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/timepicker/jquery.ui.timepicker.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/showbuilder/tabs.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/showbuilder/builder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/showbuilder/main_builder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
// MEDIA BUILDER
$this->view->headScript()->appendFile($baseUrl.'js/js-timezone-detect/jstz-1.0.4.min.js','text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/spl.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/podcast.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/publish.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/playlist/smart_blockbuilder.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headLink()->appendStylesheet($baseUrl.'css/playlist_builder.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/jquery.ui.timepicker.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/showbuilder.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/dashboard.css?'.$CC_CONFIG['airtime_version']); // TODO
$this->view->headLink()->appendStylesheet($baseUrl.'css/dashboard.css?'.$CC_CONFIG['airtime_version']);
$csrf_namespace = new Zend_Session_Namespace('csrf_namespace');
$csrf_element = new Zend_Form_Element_Hidden('csrf');

View file

@ -3,32 +3,110 @@
require_once "ThirdPartyController.php";
require_once "ise/php-soundcloud/src/Soundcloud/Service.php";
class SoundcloudController extends ThirdPartyController {
class SoundcloudController extends ThirdPartyController implements OAuth2Controller {
/**
* @var SoundcloudService
* @var Application_Service_SoundcloudService
*/
protected $_service;
/**
* @var string Application_Model_Preference service request token accessor function name
*/
protected $_SERVICE_TOKEN_ACCESSOR = 'setSoundCloudRequestToken';
/**
* Set up SoundCloud access variables.
*
* @return void
*/
public function init() {
parent::init();
$this->_service = new SoundcloudService();
$this->_service = new Application_Service_SoundcloudService();
}
/**
* Upload the file with the given id to SoundCloud
*
* @return void
*
* @throws Zend_Controller_Response_Exception thrown if upload fails for any reason
*/
public function uploadAction() {
$id = $this->getRequest()->getParam('id');
$this->_service->upload($id);
}
/**
* Update the file with the given id on SoundCloud
*
* @return void
*
* @throws Zend_Controller_Response_Exception thrown if upload fails for any reason
*/
public function updateAction() {
$id = $this->getRequest()->getParam('id');
$this->_service->update($id);
}
/**
* Download the file with the given id from SoundCloud
*
* @return void
*
* @throws Zend_Controller_Response_Exception thrown if download fails for any reason
*/
public function downloadAction() {
$id = $this->getRequest()->getParam('id');
$this->_service->download($id);
}
/**
* Delete the file with the given id from SoundCloud
*
* @return void
*
* @throws Zend_Controller_Response_Exception thrown if deletion fails for any reason
*/
public function deleteAction() {
$id = $this->getRequest()->getParam('id');
$this->_service->delete($id);
}
/**
* Send user to SoundCloud to authorize before being redirected
*
* @return void
*/
public function authorizeAction() {
$auth_url = $this->_service->getAuthorizeUrl();
header('Location: ' . $auth_url);
}
/**
* Clear the previously saved request token from preferences
*
* @return void
*/
public function deauthorizeAction() {
Application_Model_Preference::setSoundCloudRequestToken("");
header('Location: ' . $this->_baseUrl . 'preference'); // Redirect back to the preference page
}
/**
* Called when user successfully completes SoundCloud authorization
* Store the returned request token for future requests
*
* @return void
*/
public function redirectAction() {
$code = $_GET['code'];
$this->_service->requestNewAccessToken($code);
header('Location: ' . $this->_baseUrl . 'preference'); // Redirect back to the preference page
}
/**
* Fetch the permalink to a file on SoundCloud and redirect to it.
*
* @return void
*/
public function viewOnSoundCloudAction() {
$request = $this->getRequest();
$id = $request->getParam('id');
$id = $this->getRequest()->getParam('id');
try {
$soundcloudLink = $this->_service->getLinkToFile($id);
header('Location: ' . $soundcloudLink);

View file

@ -11,83 +11,20 @@ abstract class ThirdPartyController extends Zend_Controller_Action {
protected $_baseUrl;
/**
* @var ThirdPartyService third party service object
* @var Application_Service_ThirdPartyService third party service object
*/
protected $_service;
/**
* @var string Application_Model_Preference service request token accessor function name
*/
protected $_SERVICE_TOKEN_ACCESSOR;
/**
* Disable controller rendering and initialize
*
* @return void
*/
public function init() {
$CC_CONFIG = Config::getConfig();
$this->_baseUrl = 'http://' . $CC_CONFIG['baseUrl'] . ":" . $CC_CONFIG['basePort'] . "/";
$this->_baseUrl = Application_Common_HTTPHelper::getStationUrl();
$this->view->layout()->disableLayout(); // Don't inject the standard Now Playing header.
$this->_helper->viewRenderer->setNoRender(true); // Don't use (phtml) templates
}
/**
* Send user to a third-party service to authorize before being redirected
*
* @return void
*/
public function authorizeAction() {
$auth_url = $this->_service->getAuthorizeUrl();
header('Location: ' . $auth_url);
}
/**
* Clear the previously saved request token from the preferences
*
* @return void
*/
public function deauthorizeAction() {
$function = $this->_SERVICE_TOKEN_ACCESSOR;
Application_Model_Preference::$function("");
header('Location: ' . $this->_baseUrl . 'preference'); // Redirect back to the preference page
}
/**
* Called when user successfully completes third-party authorization
* Store the returned request token for future requests
*
* @return void
*/
public function redirectAction() {
$code = $_GET['code'];
$this->_service->requestNewAccessToken($code);
header('Location: ' . $this->_baseUrl . 'preference'); // Redirect back to the preference page
}
/**
* Upload the file with the given id to a third-party service
*
* @return void
*
* @throws Zend_Controller_Response_Exception thrown if upload fails for any reason
*/
public function uploadAction() {
$request = $this->getRequest();
$id = $request->getParam('id');
$this->_service->upload($id);
}
/**
* Delete the file with the given id from a third-party service
*
* @return void
*
* @throws Zend_Controller_Response_Exception thrown if deletion fails for any reason
*/
public function deleteAction() {
$request = $this->getRequest();
$id = $request->getParam('id');
$this->_service->delete($id);
}
}

View file

@ -0,0 +1 @@
ALTER TABLE third_party_track_references ALTER COLUMN file_id SET NOT NULL;

View file

@ -0,0 +1,11 @@
ALTER TABLE cc_files DROP COLUMN description;
DELETE FROM cc_pref WHERE keystr = 'station_podcast_id';
DROP TABLE IF EXISTS "podcast" CASCADE;
DROP TABLE IF EXISTS "imported_podcast" CASCADE;
DROP TABLE IF EXISTS "station_podcast" CASCADE;
DROP TABLE IF EXISTS "podcast_episodes" CASCADE;

View file

@ -120,7 +120,8 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
"upgrade",
'whmcs-login',
"provisioning",
"embed"
"embed",
"feeds"
)))
{
$this->setRoleName("G");
@ -174,6 +175,12 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
// we need to check the CSRF token
if ($_SERVER['REQUEST_METHOD'] != "GET" && $request->getModuleName() == "rest") {
$token = $request->getParam("csrf_token");
// PUT requests don't parameterize the data in the body, so we can't
// fetch it with getParam or getPost; instead we have to parse the body and
// check for the token in the JSON. (Hopefully we can find a better way to do this) -- Duncan
if (empty($token)) {
$token = json_decode($this->getRequest()->getRawBody(), true)["csrf_token"];
}
$tokenValid = $this->verifyCSRFToken($token);
if (!$tokenValid) {
@ -205,18 +212,18 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
/** Check if the controller/action can be accessed by the current user */
if (!$this->getAcl()->has($resourceName)
|| !$this->getAcl()->isAllowed($this->_roleName,
|| !$this->getAcl()->isAllowed($this->_roleName,
$resourceName,
$request->getActionName())) {
/** Redirect to access denied page */
$this->setErrorPage('error403');
$this->denyAccess(); /* This results in a 404! */
$this->denyAccess();
}
}
}
private function verifyAuth() {
if ($this->verifyAPIKey()) {
if ($this->verifyAPIKey() || $this->isVerifiedDownload()) {
return true;
}
@ -226,7 +233,30 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
return false;
}
/**
* Check if the requested file can be downloaded.
* It should satisfy the following requirements:
* * request path is /rest/media/:id/download
* * download key is correct
* * requested file belongs to the station podcast
*
* @return bool
*/
private function isVerifiedDownload() {
$request = $this->getRequest();
$fileId = $request->getParam("id");
$key = $request->getParam("download_key");
$module = $request->getModuleName();
$controller = $request->getControllerName();
$action = $request->getActionName();
$stationPodcast = StationPodcastQuery::create()
->findOneByDbPodcastId(Application_Model_Preference::getStationPodcastId());
return $module == "rest" && $controller == "media" && $action == "download"
&& $key === Application_Model_Preference::getStationPodcastDownloadKey()
&& $stationPodcast->hasEpisodeForFile($fileId);
}
private function verifyCSRFToken($token) {
return SecurityHelper::verifyCSRFToken($token);
}

View file

@ -36,7 +36,8 @@ class PageLayoutInitPlugin extends Zend_Controller_Plugin_Abstract
"upgrade",
'whmcs-login',
"provisioning",
"embed"
"embed",
"feeds"
))
) {
//Start the session
@ -54,6 +55,24 @@ class PageLayoutInitPlugin extends Zend_Controller_Plugin_Abstract
$this->_initTranslationGlobals();
$this->_initViewHelpers();
}
// Skip upgrades and task management when running unit tests
if (getenv("AIRTIME_UNIT_TEST") != 1) {
$taskManager = TaskManager::getInstance();
// Run the upgrade on each request (if it needs to be run)
// We can't afford to wait 7 minutes to run an upgrade: users could
// have several minutes of database errors while waiting for a
// schema change upgrade to happen after a deployment
$taskManager->runTask(TaskFactory::UPGRADE);
// Piggyback the TaskManager onto API calls. This provides guaranteed consistency
// (there is at least one API call made from pypo to Airtime every 7 minutes) and
// greatly reduces the chances of lock contention on cc_pref while the TaskManager runs
if ($controller == "api") {
$taskManager->runTasks();
}
}
}
protected function _initGlobals()
@ -157,6 +176,7 @@ class PageLayoutInitPlugin extends Zend_Controller_Plugin_Abstract
$view->headScript()->appendFile($baseUrl . 'js/libs/jquery-1.8.3.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript')
->appendFile($baseUrl . 'js/libs/jquery-ui-1.8.24.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript')
->appendFile($baseUrl . 'js/libs/angular.min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript')
->appendFile($baseUrl . 'js/bootstrap/bootstrap.js?' . $CC_CONFIG['airtime_version'], 'text/javascript')
->appendFile($baseUrl . 'js/libs/underscore-min.js?' . $CC_CONFIG['airtime_version'], 'text/javascript')

View file

@ -0,0 +1 @@
ALTER TABLE third_party_track_references ALTER COLUMN file_id DROP NOT NULL;

View file

@ -0,0 +1,90 @@
ALTER TABLE cc_files ADD COLUMN description VARCHAR(512);
-----------------------------------------------------------------------
-- podcast
-----------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS "podcast"
(
"id" serial NOT NULL,
"url" VARCHAR(4096) NOT NULL,
"title" VARCHAR(4096) NOT NULL,
"creator" VARCHAR(4096),
"description" VARCHAR(4096),
"language" VARCHAR(4096),
"copyright" VARCHAR(4096),
"link" VARCHAR(4096),
"itunes_author" VARCHAR(4096),
"itunes_keywords" VARCHAR(4096),
"itunes_summary" VARCHAR(4096),
"itunes_subtitle" VARCHAR(4096),
"itunes_category" VARCHAR(4096),
"itunes_explicit" VARCHAR(4096),
"owner" INTEGER,
PRIMARY KEY ("id")
);
-----------------------------------------------------------------------
-- station_podcast
-----------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS "station_podcast"
(
"id" serial NOT NULL,
"podcast_id" INTEGER NOT NULL,
PRIMARY KEY ("id")
);
-----------------------------------------------------------------------
-- imported_podcast
-----------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS "imported_podcast"
(
"id" serial NOT NULL,
"auto_ingest" BOOLEAN DEFAULT 'f' NOT NULL,
"auto_ingest_timestamp" TIMESTAMP,
"podcast_id" INTEGER NOT NULL,
PRIMARY KEY ("id")
);
-----------------------------------------------------------------------
-- podcast_episodes
-----------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS "podcast_episodes"
(
"id" serial NOT NULL,
"file_id" INTEGER,
"podcast_id" INTEGER NOT NULL,
"publication_date" TIMESTAMP NOT NULL,
"download_url" VARCHAR(4096) NOT NULL,
"episode_guid" VARCHAR(4096) NOT NULL,
PRIMARY KEY ("id")
);
ALTER TABLE "podcast" ADD CONSTRAINT "podcast_owner_fkey"
FOREIGN KEY ("owner")
REFERENCES "cc_subjs" ("id")
ON DELETE CASCADE;
ALTER TABLE "station_podcast" ADD CONSTRAINT "podcast_id_fkey"
FOREIGN KEY ("podcast_id")
REFERENCES "podcast" ("id")
ON DELETE CASCADE;
ALTER TABLE "imported_podcast" ADD CONSTRAINT "podcast_id_fkey"
FOREIGN KEY ("podcast_id")
REFERENCES "podcast" ("id")
ON DELETE CASCADE;
ALTER TABLE "podcast_episodes" ADD CONSTRAINT "podcast_episodes_cc_files_fkey"
FOREIGN KEY ("file_id")
REFERENCES "cc_files" ("id")
ON DELETE CASCADE;
ALTER TABLE "podcast_episodes" ADD CONSTRAINT "podcast_episodes_podcast_id_fkey"
FOREIGN KEY ("podcast_id")
REFERENCES "podcast" ("id")
ON DELETE CASCADE;