* SAAS-1153 - more work on station podcast frontend. Add delete and edit button functionality for episode table

* Various fixes and backend updates
* Move station podcast creation to id getter in Preferences
This commit is contained in:
Duncan Sommerville 2015-11-03 16:23:17 -05:00
parent c0d8b8b39c
commit 22f8b0f328
16 changed files with 102 additions and 72 deletions

View file

@ -42,23 +42,14 @@ class IndexController extends Zend_Controller_Action
//station feed episodes
$stationPodcastId = Application_Model_Preference::getStationPodcastId();
if (!empty($stationPodcastId)) {
$podcastEpisodesService = new Application_Service_PodcastEpisodeService();
$episodes = $podcastEpisodesService->getPodcastEpisodes($stationPodcastId);
foreach ($episodes as $e => $v) {
$episodes[$e]["track_metadata"]["track_title"] = htmlspecialchars($v["track_metadata"]["track_title"], ENT_QUOTES);
$episodes[$e]["track_metadata"]["artist_name"] = htmlspecialchars($v["track_metadata"]["artist_name"], ENT_QUOTES);
}
} else {
// Station podcast does not exist yet
// (creation is implicitly done when a new podcast is created in the dashboard)
// return empty list of episodes
$episodes = [];
$episodes[$e]["CcFiles"]["track_title"] = htmlspecialchars($v["CcFiles"]["track_title"], ENT_QUOTES);
$episodes[$e]["CcFiles"]["artist_name"] = htmlspecialchars($v["CcFiles"]["artist_name"], ENT_QUOTES);
}
$this->view->episodes = json_encode($episodes);
$this->view->displayRssTab = (!Application_Model_Preference::getStationPodcastPrivacy());
}

View file

@ -120,7 +120,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 Station Podcast" => _("My Station Podcast"),
"Station Podcast" => _("Station 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

@ -49,7 +49,7 @@ class Application_Form_EditAudioMD extends Zend_Form
$this->addElement($album_title);
// Description field
$description = new Zend_Form_Element_Text('description');
$description = new Zend_Form_Element_Textarea('description');
$description->class = 'input_text';
$description->setLabel(_('Description:'))
->setFilters(array('StringTrim'))

View file

@ -1518,7 +1518,12 @@ class Application_Model_Preference
public static function getStationPodcastId()
{
return self::getValue("station_podcast_id");
// Create the Station podcast if it doesn't exist.
$stationPodcastId = self::getValue("station_podcast_id");
if (empty($stationPodcastId)) {
$stationPodcastId = Application_Service_PodcastService::createStationPodcast();
}
return $stationPodcastId;
}
public static function setStationPodcastId($value)

View file

@ -28,19 +28,12 @@ class Rest_PodcastController extends Zend_Rest_Controller
$sortDir = $this->_getParam('sort_dir', Criteria::ASC);
$stationPodcastId = Application_Model_Preference::getStationPodcastId();
if (!empty($stationPodcastId)) {
$query = PodcastQuery::create()
// Don't return the Station podcast - we fetch it separately
->filterByDbId($stationPodcastId, Criteria::NOT_EQUAL)
->setLimit($limit)
->setOffset($offset)
->orderBy($sortColumn, $sortDir);
} else {
$query = PodcastQuery::create()
->setLimit($limit)
->setOffset($offset)
->orderBy($sortColumn, $sortDir);
}
$queryResult = $query->find();
@ -89,7 +82,7 @@ class Rest_PodcastController extends Zend_Rest_Controller
try {
//$requestData = json_decode($this->getRequest()->getRawBody(), true);
$requestData = $this->getRequest()->getPost();
$podcast = PodcastFactory::create($requestData["url"]);
$podcast = Application_Service_PodcastService::createFromFeedUrl($requestData["url"]);
$path = 'podcast/podcast.phtml';
$this->view->podcast = $podcast;

View file

@ -236,15 +236,15 @@ class Application_Service_PodcastEpisodeService extends Application_Service_Thir
}
$sortDir = ($sortDir === "DESC") ? $sortDir = Criteria::DESC : Criteria::ASC;
$isStationPodcast = $podcastId === Application_Model_Preference::getStationPodcastId();
$isStationPodcast = $podcastId == Application_Model_Preference::getStationPodcastId();
$episodes = PodcastEpisodesQuery::create()
->joinWithCcFiles('files')
->filterByDbPodcastId($podcastId);
if ($isStationPodcast) {
$episodes = $episodes->setLimit($limit);
}
$episodes = $episodes->setOffset($offset)
$episodes = $episodes->joinWith('PodcastEpisodes.CcFiles')
->setOffset($offset)
->orderBy($sortColumn, $sortDir)
->find();
@ -257,7 +257,6 @@ class Application_Service_PodcastEpisodeService extends Application_Service_Thir
foreach ($episodes as $episode) {
/** @var PodcastEpisodes $episode */
$episodeArr = $episode->toArray(BasePeer::TYPE_FIELDNAME, true, [], true);
Logging::info($episodeArr);
array_push($episodesArray, $episodeArr);
}
return $episodesArray;

View file

@ -161,6 +161,7 @@ class Application_Service_PodcastService
// Set the download key when we create the station podcast
// The value is randomly generated in the setter
Application_Model_Preference::setStationPodcastDownloadKey();
return $podcast->getDbId();
}
//TODO move this somewhere where it makes sense
@ -243,6 +244,7 @@ class Application_Service_PodcastService
if ($podcast) {
$podcast->delete();
// FIXME: I don't think we should be able to delete the station podcast...
if ($podcastId == Application_Model_Preference::getStationPodcastId()) {
Application_Model_Preference::setStationPodcastId(null);
}

View file

@ -7,7 +7,7 @@ class Application_Service_PublishService {
*/
private static $SOURCES = array(
"soundcloud" => SOUNDCLOUD,
"station_podcast" => "My Station Podcast"
"station_podcast" => "Station Podcast"
);
/**

View file

@ -59,15 +59,16 @@ document.getElementById(id).width= (newwidth) + "px";
$.each(<?php echo $this->episodes ?>, function(index, value){
// map mime to format muses recognizes
// TODO: this doesn't make a difference
if (value.track_metadata.mime == "audio/mp3") {
value.track_metadata.mime = "mp3";
} else if (value.track_metadata.mime == "audio/vorbis") {
value.track_metadata.mime = "ogg";
var metadata = value.CcFiles;
if (metadata.mime == "audio/mp3") {
metadata.mime = "mp3";
} else if (metadata.mime == "audio/vorbis") {
metadata.mime = "ogg";
}
$("#tab-4").append("<div>"+value.track_metadata.artist_name+" - "+value.track_metadata.track_title+
$("#tab-4").append("<div>"+metadata.artist_name+" - "+metadata.track_title+
" <a id='rss-download-link' href='"+value.download_url+"'>Download</a>" +
" <a id='rss-play-link' data-metaartist='"+value.track_metadata.artist_name+"' data-metatitle='"+value.track_metadata.track_title+"' data-streamurl='"+value.download_url+"' data-streamcodec='"+value.track_metadata.mime+"' href='#'>Play</a></div>");
" <a id='rss-play-link' data-metaartist='"+metadata.artist_name+"' data-metatitle='"+metadata.track_title+"' data-streamurl='"+value.download_url+"' data-streamcodec='"+metadata.mime+"' href='#'>Play</a></div>");
});
$("a#rss-play-link").click(function() {

View file

@ -7,7 +7,7 @@
<button class="btn"><?php echo _("View Feed") ?></button>
</a>
</div>
<div class="inner_editor_wrapper">
<div class="inner_editor_wrapper station_podcast_wrapper">
<form class="podcast-metadata">
<p>
<?php echo _("Check out the ") ?><a target="_blank" href="http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements"><?php echo _("RSS specification") ?></a>

View file

@ -3972,7 +3972,7 @@ li .ui-state-hover {
margin: 5px 10px 0 0;
}
.angular_wrapper .inner_editor_wrapper {
.angular_wrapper > .station_podcast_wrapper {
flex: 1 100%;
}

View file

@ -1159,7 +1159,7 @@ var AIRTIME = (function(AIRTIME) {
if (oItems.publish !== undefined) {
if (data.ftype === "audioclip") {
callback = function() {
AIRTIME.publish.publishTrack(data.id);
AIRTIME.publish.openPublishDialog(data.id);
};
}
oItems.publish.callback = callback;
@ -1264,7 +1264,7 @@ var AIRTIME = (function(AIRTIME) {
podcastToolbarButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE].eventHandlers.click = AIRTIME.podcast.deleteSelectedPodcasts;
// Add a button to view the station podcast
podcastToolbarButtons["StationPodcast"] = {
title : $.i18n._("My Station Podcast"),
title : $.i18n._("Station Podcast"),
iconClass : "icon-music",
extraBtnClass : "btn-small",
elementId : "",

View file

@ -7,7 +7,7 @@ var AIRTIME = (function (AIRTIME) {
mod = AIRTIME.podcast;
var endpoint = 'rest/podcast/', PodcastTable;
var endpoint = 'rest/podcast/', PodcastTable, $stationPodcastTab;
/**
* PodcastController constructor.
@ -69,7 +69,7 @@ var AIRTIME = (function (AIRTIME) {
*
* Save each of the selected episodes and update the podcast object.
*/
$scope.savePodcast = $scope.savePodcast || function () {
$scope.savePodcast = function () {
var episodes = self.episodeTable.getSelectedRows();
// TODO: Should we implement a batch endpoint for this instead?
jQuery.each(episodes, function () {
@ -92,6 +92,8 @@ var AIRTIME = (function (AIRTIME) {
self.$scope = $scope;
self.$http = $http;
self.initialize();
return self;
}
/**
@ -154,7 +156,8 @@ var AIRTIME = (function (AIRTIME) {
*/
function StationPodcastController($scope, $http, podcast, tab) {
// Super call to parent controller
PodcastController.call(this, $scope, $http, podcast, tab);
var self = PodcastController.call(this, $scope, $http, podcast, tab);
$stationPodcastTab = tab;
/**
* For the station podcast.
@ -166,7 +169,23 @@ var AIRTIME = (function (AIRTIME) {
};
$scope.deleteSelectedEpisodes = function () {
// TODO
var episodes = self.episodeTable.getSelectedRows();
jQuery.each(episodes, function () {
$http.delete(endpoint + $scope.podcast.id + '/episodes/' + this.id + '?csrf_token=' + $scope.csrf)
.success(function () {
self.reloadEpisodeTable();
});
});
};
$scope.openSelectedTabEditors = function () {
var episodes = self.episodeTable.getSelectedRows();
$.each(episodes, function () {
var uid = AIRTIME.library.MediaTypeStringEnum.FILE + "_" + this.file_id;
$.get(baseUrl + "library/edit-file-md/id/" + this.file_id, {format: "json"}, function (json) {
AIRTIME.playlist.fileMdEdit(json, uid);
});
});
};
}
@ -185,15 +204,29 @@ var AIRTIME = (function (AIRTIME) {
StationPodcastController.prototype._initTable = function() {
var $scope = this.$scope,
buttons = {
editBtn: {
title : $.i18n._('Edit'),
iconClass : 'icon-pencil',
extraBtnClass : '',
elementId : '',
eventHandlers : {
click: function(e) {
$scope.openSelectedTabEditors();
}
}
},
deleteBtn: {
title : $.i18n._('Delete'),
iconClass : 'icon-trash',
extraBtnClass : 'btn-small btn-danger',
extraBtnClass : 'btn-danger',
elementId : '',
eventHandlers : {
click: $scope.deleteSelectedEpisodes
click: function(e) {
$scope.deleteSelectedEpisodes();
}
}
}
},
params = {
sAjaxSource : endpoint + $scope.podcast.id + '/episodes',
@ -231,12 +264,13 @@ var AIRTIME = (function (AIRTIME) {
*
* Bulk methods use a POST request because we need to send data in the request body.
*
* @param selectedData the data to operate on
* @param method HTTP request method type
* @param callback function to run upon success
* @private
*/
function _bulkAction(method, callback) {
var ids = [], selectedData = AIRTIME.library.podcastTableWidget.getSelectedRows();
function _bulkAction(selectedData, method, callback) {
var ids = [];
selectedData.forEach(function(el) {
var uid = AIRTIME.library.MediaTypeStringEnum.PODCAST+"_"+el.id,
t = AIRTIME.tabs.get(uid);
@ -357,16 +391,20 @@ var AIRTIME = (function (AIRTIME) {
* Open a tab to view and edit the station podcast.
*/
mod.openStationPodcast = function() {
if (typeof $stationPodcastTab === 'undefined') {
$.get(endpoint + 'station', function(json) {
_initAppFromResponse(json);
})
});
} else if ($stationPodcastTab != AIRTIME.tabs.getActiveTab()) {
$stationPodcastTab.switchTo();
}
};
/**
* Create a bulk request to edit all currently selected podcasts.
*/
mod.editSelectedPodcasts = function() {
_bulkAction(HTTPMethods.GET, function(json) {
_bulkAction(AIRTIME.library.podcastTableWidget.getSelectedRows(), HTTPMethods.GET, function(json) {
json.forEach(function(data) {
_initAppFromResponse(data);
});
@ -378,7 +416,7 @@ var AIRTIME = (function (AIRTIME) {
*/
mod.deleteSelectedPodcasts = function() {
if (confirm($.i18n._("Are you sure you want to delete the selected podcasts from your library?"))) {
_bulkAction(HTTPMethods.DELETE, function () {
_bulkAction(AIRTIME.library.podcastTableWidget.getSelectedRows(), HTTPMethods.DELETE, function () {
AIRTIME.library.podcastDataTable.fnDraw();
});
}

View file

@ -121,7 +121,7 @@ var AIRTIME = (function (AIRTIME) {
};
mod.publishTrack = function(mediaId) {
mod.openPublishDialog = function(mediaId) {
jQuery.get(dialogUrl, { csrf_token: jQuery("#csrf").val() })
.success(function(html) {

View file

@ -1051,11 +1051,12 @@ var AIRTIME = (function(AIRTIME){
});
newTab.contents.find(".md-publish").on("click", function() {
AIRTIME.publish.publishTrack(fileId);
AIRTIME.publish.openPublishDialog(fileId);
});
newTab.wrapper.find('.edit-md-dialog').on("keyup", function(event) {
if (event.keyCode === 13) {
// Don't submit if the user hits enter in a textarea (description)
if ($(event.target).is('input') && event.keyCode === 13) {
newTab.wrapper.find('.md-save').click();
}
});

View file

@ -87,7 +87,7 @@ var AIRTIME = (function(AIRTIME) {
}
self._datatable = self._$wrapperDOMNode.dataTable(options);
self._datatable.fnDraw(); //Load the AJAX data now that our event handlers have been bound.
// self._datatable.fnDraw(); //Load the AJAX data now that our event handlers have been bound.
self._setupEventHandlers(bItemSelection);
//return self._datatable;