SAAS-1165 - implement most functionality for left-hand podcast episodes view
This commit is contained in:
parent
a984cee13a
commit
0be26d621b
11 changed files with 375 additions and 150 deletions
|
@ -64,7 +64,7 @@ class PodcastManager {
|
|||
// episodes in the list of episodes to ingest, don't skip this episode - we should try to ingest the
|
||||
// most recent episode when the user first sets the podcast to automatic ingest.
|
||||
// If the publication date of this episode is before the ingest timestamp, we don't need to ingest it
|
||||
if ((empty($ts) && !empty($episodes)) || strtotime($episodeData["pub_date"]) < strtotime($ts)) {
|
||||
if ((empty($ts) && ($i > 0)) || strtotime($episodeData["pub_date"]) < strtotime($ts)) {
|
||||
continue;
|
||||
}
|
||||
$episode = PodcastEpisodesQuery::create()->findOneByDbEpisodeGuid($episodeData["guid"]);
|
||||
|
|
|
@ -222,6 +222,7 @@ class LibraryController extends Zend_Controller_Action
|
|||
$message = null;
|
||||
$noPermissionMsg = _("You don't have permission to delete selected items.");
|
||||
|
||||
Logging::info($mediaItems);
|
||||
foreach ($mediaItems as $media) {
|
||||
|
||||
if ($media["type"] === "audioclip") {
|
||||
|
|
|
@ -267,16 +267,23 @@ class Application_Service_PodcastEpisodeService extends Application_Service_Thir
|
|||
public function _getImportedPodcastEpisodeArray($podcast, $episodes) {
|
||||
$rss = Application_Service_PodcastService::getPodcastFeed($podcast->getDbUrl());
|
||||
$episodeIds = array();
|
||||
$episodeFiles = array();
|
||||
foreach ($episodes as $e) {
|
||||
array_push($episodeIds, $e->getDbEpisodeGuid());
|
||||
$episodeFiles[$e->getDbEpisodeGuid()] = $e->getDbFileId();
|
||||
}
|
||||
|
||||
$episodesArray = array();
|
||||
foreach ($rss->get_items() as $item) {
|
||||
$itemId = $item->get_id();
|
||||
$ingested = in_array($itemId, $episodeIds) ? (empty($episodeFiles[$itemId]) ? -1 : 1) : 0;
|
||||
$file = $ingested > 0 && !empty($episodeFiles[$itemId]) ?
|
||||
CcFiles::getSanitizedFileById($episodeFiles[$itemId]) : array();
|
||||
/** @var SimplePie_Item $item */
|
||||
array_push($episodesArray, array(
|
||||
"guid" => $item->get_id(),
|
||||
"ingested" => in_array($item->get_id(), $episodeIds),
|
||||
"podcast_id" => $podcast->getDbId(),
|
||||
"guid" => $itemId,
|
||||
"ingested" => $ingested,
|
||||
"title" => $item->get_title(),
|
||||
// From the RSS spec best practices:
|
||||
// 'An item's author element provides the e-mail address of the person who wrote the item'
|
||||
|
@ -284,7 +291,8 @@ class Application_Service_PodcastEpisodeService extends Application_Service_Thir
|
|||
"description" => $item->get_description(),
|
||||
"pub_date" => $item->get_gmdate(),
|
||||
"link" => $item->get_link(),
|
||||
"enclosure" => $item->get_enclosure()
|
||||
"enclosure" => $item->get_enclosure(),
|
||||
"file" => $file
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
<div class="btn-toolbar clearfix">
|
||||
<div class="btn-group pull-right">
|
||||
<button ng-click="discard()" class="btn" type="button" name="cancel">
|
||||
<?php echo _("Cancel") ?>
|
||||
<?php echo _("Close") ?>
|
||||
</button>
|
||||
</div>
|
||||
<div class='btn-group pull-right'>
|
||||
|
|
|
@ -200,7 +200,7 @@ thead th.ui-state-default {
|
|||
color: #ccc;
|
||||
}
|
||||
|
||||
thead th.ui-state-default:not([class*='checkbox']):not([class*='type']):not([class*='image']) {
|
||||
thead th.ui-state-default:not([class*='checkbox']):not([class*='type']):not([class*='image']):not([class*='imported']) {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@
|
|||
}
|
||||
|
||||
.btn-toolbar {
|
||||
margin: 0;
|
||||
/*margin: 0;*/
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
|
|
@ -908,7 +908,7 @@ dl.inline-list dd {
|
|||
.DataTables_sort_wrapper .ui-icon {
|
||||
display: block;
|
||||
float: right;
|
||||
margin: 5px 0;
|
||||
margin: 6px 0 0 5px;
|
||||
}
|
||||
.dataTables_type {
|
||||
float:right;
|
||||
|
@ -3953,12 +3953,20 @@ li .ui-state-hover {
|
|||
|
||||
/* Podcasts */
|
||||
|
||||
.DataTables_sort_wrapper {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-justify-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
[id^="podcast_episodes"][id$="_wrapper"] {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
float: left;
|
||||
flex: 1 100%;
|
||||
margin: 4px 0 !important;
|
||||
/*margin: 4px 0 0 !important;*/
|
||||
min-height: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
|
@ -4029,9 +4037,29 @@ li .ui-state-hover {
|
|||
float: left;
|
||||
}
|
||||
|
||||
.podcast_episodes_imported {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.imported-flag {
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.loading-icon {
|
||||
background: url("img/loading.gif") no-repeat center center;
|
||||
min-width: 24px;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* UI Revamp Video */
|
||||
|
||||
#whatsnew {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
@ -4087,6 +4115,10 @@ li .ui-state-hover {
|
|||
}
|
||||
|
||||
.publish-sources {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
|
|
@ -263,6 +263,40 @@ var AIRTIME = (function(AIRTIME) {
|
|||
return true;
|
||||
}
|
||||
|
||||
mod.addToSchedule = function (selected) {
|
||||
console.log(selected);
|
||||
var aMediaIds = [], aSchedIds = [], aData = [];
|
||||
|
||||
$.each(selected, function () {
|
||||
aMediaIds.push({
|
||||
"id": this.id,
|
||||
"type": this.ftype
|
||||
});
|
||||
});
|
||||
|
||||
// process selected files/playlists.
|
||||
$("#show_builder_table").find("tr.sb-selected").each(function (i, el) {
|
||||
aData.push($(el).data("aData"));
|
||||
});
|
||||
|
||||
// process selected schedule rows to add media after.
|
||||
$.each(aData, function () {
|
||||
aSchedIds.push({
|
||||
"id": this.id,
|
||||
"instance": this.instance,
|
||||
"timestamp": this.timestamp
|
||||
});
|
||||
});
|
||||
|
||||
if (aSchedIds.length == 0) {
|
||||
if (!addToCurrentOrNext(aSchedIds)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
|
||||
};
|
||||
|
||||
mod.setupLibraryToolbar = function() {
|
||||
var $toolbar = $(".lib-content .fg-toolbar:first");
|
||||
|
||||
|
@ -284,45 +318,14 @@ var AIRTIME = (function(AIRTIME) {
|
|||
return;
|
||||
}
|
||||
|
||||
var selected = AIRTIME.library.getSelectedData(), data, i, length, temp, aMediaIds = [], aSchedIds = [], aData = [];
|
||||
var selected = AIRTIME.library.getSelectedData(), aMediaIds = [];
|
||||
|
||||
if ($("#show_builder_table").is(":visible")) {
|
||||
for (i = 0, length = selected.length; i < length; i++) {
|
||||
data = selected[i];
|
||||
aMediaIds.push({
|
||||
"id": data.id,
|
||||
"type": data.ftype
|
||||
});
|
||||
}
|
||||
|
||||
// process selected files/playlists.
|
||||
$("#show_builder_table tr.sb-selected").each(function (i, el) {
|
||||
aData.push($(el).data("aData"));
|
||||
});
|
||||
|
||||
// process selected schedule rows to add media
|
||||
// after.
|
||||
for (i = 0, length = aData.length; i < length; i++) {
|
||||
temp = aData[i];
|
||||
aSchedIds.push({
|
||||
"id": temp.id,
|
||||
"instance": temp.instance,
|
||||
"timestamp": temp.timestamp
|
||||
});
|
||||
}
|
||||
|
||||
if (aSchedIds.length == 0) {
|
||||
if (!addToCurrentOrNext(aSchedIds)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
|
||||
mod.addToSchedule(selected);
|
||||
} else {
|
||||
for (i = 0, length = selected.length; i < length; i++) {
|
||||
data = selected[i];
|
||||
aMediaIds.push([data.id, data.ftype]);
|
||||
}
|
||||
$.each(selected, function () {
|
||||
aMediaIds.push([this.id, this.ftype]);
|
||||
});
|
||||
|
||||
// check if a playlist/block is open before adding items
|
||||
if ($('.active-tab .obj_type').val() == 'playlist'
|
||||
|
|
|
@ -476,7 +476,11 @@ var AIRTIME = (function(AIRTIME) {
|
|||
}
|
||||
|
||||
chosenItems = {};
|
||||
if (oTable == $datatables[mod.DataTableTypeEnum.PODCAST_EPISODES]) {
|
||||
mod.podcastEpisodeTableWidget.reload();
|
||||
} else {
|
||||
oTable.fnStandingRedraw();
|
||||
}
|
||||
|
||||
//Re-enable the delete button
|
||||
AIRTIME.button.enableButton("btn-group #sb-trash", false);
|
||||
|
@ -1246,6 +1250,8 @@ var AIRTIME = (function(AIRTIME) {
|
|||
};
|
||||
|
||||
mod.setCurrentTable = function (table) {
|
||||
// FIXME: This is hacky...
|
||||
mod.podcastEpisodeDataTable.fnClearTable();
|
||||
var dt = $datatables[table],
|
||||
wrapper = $(dt).closest(".dataTables_wrapper");
|
||||
$("#library_content").find(".dataTables_wrapper").hide();
|
||||
|
@ -1264,10 +1270,9 @@ var AIRTIME = (function(AIRTIME) {
|
|||
var aoColumns = [
|
||||
/* Title */ { "sTitle" : $.i18n._("Title") , "mDataProp" : "title" , "sClass" : "library_title" , "sWidth" : "170px" },
|
||||
/* Creator */ { "sTitle" : $.i18n._("Creator") , "mDataProp" : "creator" , "sClass" : "library_creator" , "sWidth" : "160px" },
|
||||
/* Upload Time { "sTitle" : $.i18n._("Uploaded") , "mDataProp" : "utime" , "bVisible" : false , "sClass" : "library_upload_time" , "sWidth" : "155px" }, */
|
||||
/* Website */ { "sTitle" : $.i18n._("Description") , "mDataProp" : "description" , "bVisible" : false , "sWidth" : "150px" },
|
||||
/* Year */ { "sTitle" : $.i18n._("Owner") , "mDataProp" : "owner" , "bVisible" : false , "sWidth" : "60px" },
|
||||
/* URL */ { "sTitle" : $.i18n._("Feed URL") , "mDataProp" : "url" , "bVisible" : false , "sWidth" : "60px" },
|
||||
/* URL */ { "sTitle" : $.i18n._("Feed URL") , "mDataProp" : "url" , "bVisible" : false , "sWidth" : "60px" }
|
||||
|
||||
];
|
||||
var ajaxSourceURL = baseUrl+"rest/podcast";
|
||||
|
@ -1277,6 +1282,21 @@ var AIRTIME = (function(AIRTIME) {
|
|||
podcastToolbarButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW].eventHandlers.click = AIRTIME.podcast.createUrlDialog;
|
||||
podcastToolbarButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT].eventHandlers.click = AIRTIME.podcast.editSelectedPodcasts;
|
||||
podcastToolbarButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE].eventHandlers.click = AIRTIME.podcast.deleteSelectedPodcasts;
|
||||
// TODO: only enable this if user has exactly one podcast selected
|
||||
podcastToolbarButtons["ViewPodcast"] = {
|
||||
title : $.i18n._("View Podcast"),
|
||||
iconClass : "icon-chevron-right",
|
||||
extraBtnClass : "btn-small",
|
||||
elementId : "",
|
||||
eventHandlers : {
|
||||
click: function () {
|
||||
var podcast = mod.podcastTableWidget.getSelectedRows()[0];
|
||||
mod.podcastEpisodeTableWidget.reload(podcast.id);
|
||||
mod.podcastTableWidget._clearSelection();
|
||||
mod.setCurrentTable(mod.DataTableTypeEnum.PODCAST_EPISODES);
|
||||
}
|
||||
}
|
||||
};
|
||||
// Add a button to view the station podcast
|
||||
podcastToolbarButtons["StationPodcast"] = {
|
||||
title : $.i18n._("Station Podcast"),
|
||||
|
@ -1305,6 +1325,7 @@ var AIRTIME = (function(AIRTIME) {
|
|||
mod.podcastTableWidget.assignDblClickHandler(function () {
|
||||
var podcast = mod.podcastDataTable.fnGetData($(this).index());
|
||||
mod.podcastEpisodeTableWidget.reload(podcast.id);
|
||||
mod.podcastTableWidget._clearSelection();
|
||||
mod.setCurrentTable(mod.DataTableTypeEnum.PODCAST_EPISODES);
|
||||
});
|
||||
|
||||
|
@ -1319,6 +1340,41 @@ var AIRTIME = (function(AIRTIME) {
|
|||
*/
|
||||
mod._initPodcastEpisodeDatatable = function () {
|
||||
var buttons = {
|
||||
backBtn: {
|
||||
title : $.i18n._('Back to Podcasts'),
|
||||
iconClass : 'icon-chevron-left',
|
||||
extraBtnClass : 'btn-small',
|
||||
elementId : '',
|
||||
eventHandlers : {
|
||||
click: function (e) {
|
||||
mod.setCurrentTable(mod.DataTableTypeEnum.PODCAST);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
defaults = AIRTIME.widgets.Table.getStandardToolbarButtons();
|
||||
defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW].title = "Import";
|
||||
defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW].eventHandlers.click = function () {
|
||||
var episodes = mod.podcastEpisodeTableWidget.getSelectedRows();
|
||||
AIRTIME.podcast.importSelectedEpisodes(episodes, mod.podcastEpisodeTableWidget);
|
||||
};
|
||||
|
||||
defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE].eventHandlers.click = function () {
|
||||
var podcastId, data = [], episodes = mod.podcastEpisodeTableWidget.getSelectedRows();
|
||||
$.each(episodes, function () {
|
||||
data.push({id: this.file.id, type: this.file.ftype});
|
||||
});
|
||||
mod.fnDeleteItems(data);
|
||||
};
|
||||
|
||||
// Reassign these because integer keys take precedence in iteration order - we want to order based on insertion
|
||||
defaults = {
|
||||
newBtn : defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW],
|
||||
editBtn: defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT],
|
||||
delBtn : defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE]
|
||||
};
|
||||
|
||||
$.extend(true, buttons, defaults, {
|
||||
addToScheduleBtn: {
|
||||
// TODO: compatibility with checkAddButton function
|
||||
title : $.i18n._('Add to Schedule'),
|
||||
|
@ -1326,33 +1382,52 @@ var AIRTIME = (function(AIRTIME) {
|
|||
extraBtnClass : 'btn-small',
|
||||
elementId : '',
|
||||
eventHandlers : {
|
||||
click: function (e) {
|
||||
console.log(mod.podcastEpisodeDataTable.fnGetData($(this).index()));
|
||||
click: function () {
|
||||
var data = [], selected = mod.podcastEpisodeTableWidget.getSelectedRows();
|
||||
$.each(selected, function () { data.push(this.file); });
|
||||
mod.addToSchedule(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
mod.podcastEpisodeTableWidget = AIRTIME.podcast.initPodcastEpisodeDatatable(
|
||||
$("#podcast_episodes_table"),
|
||||
{
|
||||
bServerSide : false,
|
||||
sAjaxSource : null,
|
||||
// Initialize the table with empty data so we can defer loading
|
||||
// If we load sequentially there's a delay before the table appears
|
||||
aaData : {},
|
||||
aoColumns : [
|
||||
/* GUID */ { "sTitle" : "" , "mDataProp" : "guid" , "sClass" : "podcast_episodes_guid" , "bVisible" : false },
|
||||
/* Ingested */ { "sTitle" : $.i18n._("Imported?") , "mDataProp" : "importIcon" , "sClass" : "podcast_episodes_imported" , "sWidth" : "120px" },
|
||||
/* Title */ { "sTitle" : $.i18n._("Title") , "mDataProp" : "title" , "sClass" : "podcast_episodes_title" , "sWidth" : "170px" },
|
||||
/* Author */ { "sTitle" : $.i18n._("Author") , "mDataProp" : "author" , "sClass" : "podcast_episodes_author" , "sWidth" : "170px" },
|
||||
/* Description */ { "sTitle" : $.i18n._("Description") , "mDataProp" : "description" , "sClass" : "podcast_episodes_description" , "sWidth" : "300px" },
|
||||
/* Link */ { "sTitle" : $.i18n._("Link") , "mDataProp" : "link" , "sClass" : "podcast_episodes_link" , "sWidth" : "170px" },
|
||||
/* Publication Date */ { "sTitle" : $.i18n._("Publication Date") , "mDataProp" : "pub_date" , "sClass" : "podcast_episodes_pub_date" , "sWidth" : "170px" }
|
||||
]
|
||||
],
|
||||
bServerSide : false,
|
||||
sAjaxSource : null,
|
||||
// Initialize the table with empty data so we can defer loading
|
||||
// If we load sequentially there's a delay before the table appears
|
||||
aaData : {},
|
||||
oColVis : {
|
||||
aiExclude: [0, 1, 2]
|
||||
},
|
||||
buttons
|
||||
oColReorder: {
|
||||
iFixedColumns: 3 // Checkbox + imported
|
||||
}
|
||||
},
|
||||
buttons,
|
||||
{ hideIngestCheckboxes: false }
|
||||
);
|
||||
|
||||
mod.podcastEpisodeDataTable = $datatables[mod.DataTableTypeEnum.PODCAST_EPISODES] = mod.podcastEpisodeTableWidget.getDatatable();
|
||||
mod.podcastEpisodeTableWidget.assignDblClickHandler(function () {
|
||||
var data = mod.podcastEpisodeDataTable.fnGetData($(this).index());
|
||||
if (data.file.length > 0) {
|
||||
mod.dblClickAdd(data.file, data.file.ftype);
|
||||
} else {
|
||||
AIRTIME.podcast.importSelectedEpisodes([data], mod.podcastEpisodeTableWidget);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
mod.libraryInit = libraryInit;
|
||||
|
|
|
@ -7,7 +7,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
|
||||
mod = AIRTIME.podcast;
|
||||
|
||||
var endpoint = 'rest/podcast/', PodcastTable, $stationPodcastTab;
|
||||
var endpoint = 'rest/podcast/', PodcastEpisodeTable, $stationPodcastTab;
|
||||
|
||||
/**
|
||||
* PodcastController constructor.
|
||||
|
@ -59,20 +59,6 @@ var AIRTIME = (function (AIRTIME) {
|
|||
});
|
||||
};
|
||||
|
||||
$scope.importEpisodes = function () {
|
||||
var episodes = self.episodeTable.getSelectedRows();
|
||||
// TODO: Should we implement a batch endpoint for this instead?
|
||||
jQuery.each(episodes, function () {
|
||||
$http.post(endpoint + $scope.podcast.id + '/episodes', {
|
||||
csrf_token: $scope.csrf,
|
||||
episode: this
|
||||
}).success(function () {
|
||||
self.reloadEpisodeTable();
|
||||
self.episodeTable.getDatatable().fnDraw();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the tab and discard any changes made to the podcast data.
|
||||
*/
|
||||
|
@ -122,7 +108,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
elementId : '',
|
||||
eventHandlers : {
|
||||
click: function () {
|
||||
$scope.importEpisodes();
|
||||
mod.importSelectedEpisodes(self.episodeTable.getSelectedRows(), self.episodeTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +116,11 @@ var AIRTIME = (function (AIRTIME) {
|
|||
self.episodeTable = AIRTIME.podcast.initPodcastEpisodeDatatable(
|
||||
$scope.tab.contents.find('.podcast_episodes'),
|
||||
params,
|
||||
buttons
|
||||
buttons,
|
||||
{
|
||||
hideIngestCheckboxes: true,
|
||||
podcastId: $scope.podcast.id
|
||||
}
|
||||
);
|
||||
self.reloadEpisodeTable();
|
||||
};
|
||||
|
@ -139,7 +129,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
* Reload the podcast episode table.
|
||||
*/
|
||||
PodcastController.prototype.reloadEpisodeTable = function() {
|
||||
this.episodeTable.reload(this.$scope.podcast.id);
|
||||
this.episodeTable.reload();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -166,13 +156,13 @@ var AIRTIME = (function (AIRTIME) {
|
|||
*/
|
||||
function StationPodcastController($scope, $http, podcast, tab) {
|
||||
// Super call to parent controller
|
||||
var self = PodcastController.call(this, $scope, $http, podcast, tab);
|
||||
PodcastController.call(this, $scope, $http, podcast, tab);
|
||||
// Store the station podcast tab in module scope so it can be checked if the user clicks the
|
||||
// Station Podcast button again - this way we don't have to go back to the server to get the ID.
|
||||
$stationPodcastTab = tab;
|
||||
|
||||
/**
|
||||
* Override the tab close function to 'unset' the module-scope $stationPodcastTab
|
||||
* Override the tab close function to 'unset' the module-scope $stationPodcastTab.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
|
@ -181,25 +171,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
$stationPodcastTab = undefined;
|
||||
};
|
||||
|
||||
self.deleteSelectedEpisodes = function () {
|
||||
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();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self.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);
|
||||
});
|
||||
});
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,6 +181,34 @@ var AIRTIME = (function (AIRTIME) {
|
|||
*/
|
||||
StationPodcastController.prototype = Object.create(PodcastController.prototype);
|
||||
|
||||
/**
|
||||
* Remove the selected episodes from the station podcast feed.
|
||||
*/
|
||||
StationPodcastController.prototype.unpublishSelectedEpisodes = function () {
|
||||
var self = this, $scope = self.$scope,
|
||||
episodes = self.episodeTable.getSelectedRows();
|
||||
jQuery.each(episodes, function () {
|
||||
self.$http.delete(endpoint + $scope.podcast.id + '/episodes/' + this.id + '?csrf_token=' + $scope.csrf)
|
||||
.success(function () {
|
||||
self.reloadEpisodeTable();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Open metadata editor tabs for each of the selected episodes.
|
||||
*/
|
||||
StationPodcastController.prototype.openSelectedTabEditors = function () {
|
||||
var self = this,
|
||||
episodes = self.episodeTable.getSelectedRows();
|
||||
jQuery.each(episodes, function () {
|
||||
var uid = AIRTIME.library.MediaTypeStringEnum.FILE + "_" + this.file_id;
|
||||
jQuery.get(baseUrl + "library/edit-file-md/id/" + this.file_id, {format: "json"}, function (json) {
|
||||
AIRTIME.playlist.fileMdEdit(json, uid);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the Station podcast episode table.
|
||||
*
|
||||
|
@ -223,9 +223,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
extraBtnClass : '',
|
||||
elementId : '',
|
||||
eventHandlers : {
|
||||
click: function () {
|
||||
self.openSelectedTabEditors();
|
||||
}
|
||||
click: self.openSelectedTabEditors.bind(self)
|
||||
}
|
||||
},
|
||||
deleteBtn: {
|
||||
|
@ -234,9 +232,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
extraBtnClass : 'btn-danger',
|
||||
elementId : '',
|
||||
eventHandlers : {
|
||||
click: function () {
|
||||
self.deleteSelectedEpisodes();
|
||||
}
|
||||
click: self.unpublishSelectedEpisodes.bind(self)
|
||||
}
|
||||
},
|
||||
slideToggle: {}
|
||||
|
@ -253,12 +249,20 @@ var AIRTIME = (function (AIRTIME) {
|
|||
this.episodeTable = AIRTIME.podcast.initPodcastEpisodeDatatable(
|
||||
$scope.tab.contents.find('.podcast_episodes'),
|
||||
params,
|
||||
buttons
|
||||
buttons,
|
||||
{
|
||||
hideIngestCheckboxes: true,
|
||||
podcastId: $scope.podcast.id
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the Station podcast.
|
||||
*/
|
||||
StationPodcastController.prototype.initialize = function() {
|
||||
PodcastController.prototype.initialize.call(this);
|
||||
// We want to override the default tab name behaviour and use "Station Podcast" for clarity
|
||||
this.$scope.tab.setName(jQuery.i18n._("Station Podcast"));
|
||||
};
|
||||
|
||||
|
@ -354,36 +358,118 @@ var AIRTIME = (function (AIRTIME) {
|
|||
*
|
||||
* @private
|
||||
*/
|
||||
function _initPodcastTable() {
|
||||
PodcastTable = function(wrapperDOMNode, bItemSelection, toolbarButtons, dataTablesOptions) {
|
||||
// Just call the superconstructor. For clarity/extensibility
|
||||
function _initPodcastEpisodeTable() {
|
||||
PodcastEpisodeTable = function(wrapperDOMNode, bItemSelection, toolbarButtons, dataTablesOptions, config) {
|
||||
this.config = config; // Internal configuration object
|
||||
this._setupImportListener();
|
||||
// Call the superconstructor
|
||||
return AIRTIME.widgets.Table.call(this, wrapperDOMNode, bItemSelection, toolbarButtons, dataTablesOptions);
|
||||
}; // Subclass AIRTIME.widgets.Table
|
||||
PodcastTable.prototype = Object.create(AIRTIME.widgets.Table.prototype);
|
||||
PodcastTable.prototype.constructor = PodcastTable;
|
||||
PodcastTable.prototype._SELECTORS = Object.freeze({
|
||||
PodcastEpisodeTable.prototype = Object.create(AIRTIME.widgets.Table.prototype);
|
||||
PodcastEpisodeTable.prototype.constructor = PodcastEpisodeTable;
|
||||
PodcastEpisodeTable.prototype._SELECTORS = Object.freeze({
|
||||
SELECTION_CHECKBOX: ".airtime_table_checkbox:has(input)",
|
||||
SELECTION_TABLE_ROW: "tr:has(td.airtime_table_checkbox > input)"
|
||||
});
|
||||
PodcastTable.prototype._datatablesCheckboxDataDelegate = function(rowData, callType, dataToSave) {
|
||||
if (rowData.ingested) return null; // Don't create checkboxes for ingested items
|
||||
|
||||
/**
|
||||
* @override
|
||||
*
|
||||
* Override the checkbox delegate function in the Table object to change
|
||||
* the row's checkbox and import status columns depending on the status
|
||||
* of the episode (unimported: 0, imported: 1, pending import: -1).
|
||||
*
|
||||
* @param rowData
|
||||
* @param callType
|
||||
* @param dataToSave
|
||||
*
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
PodcastEpisodeTable.prototype._datatablesCheckboxDataDelegate = function(rowData, callType, dataToSave) {
|
||||
var importIcon = "<span class='sp-checked-icon checked-icon imported-flag'></span>",
|
||||
pendingIcon = "<span class='loading-icon'></span>";
|
||||
if (this.config.hideIngestCheckboxes && rowData.ingested && rowData.ingested != 0) {
|
||||
return rowData.ingested > 0 ? importIcon : pendingIcon;
|
||||
}
|
||||
rowData.importIcon = (rowData.ingested != 0) ? (rowData.ingested > 0 ? importIcon : pendingIcon) : null;
|
||||
return AIRTIME.widgets.Table.prototype._datatablesCheckboxDataDelegate.call(this, rowData, callType, dataToSave);
|
||||
};
|
||||
// Since we're sometimes using a static source, define a separate function to fetch and 'reload' the table data
|
||||
// We use this when we save the Podcast because we need to flag rows the user is ingesting
|
||||
PodcastTable.prototype.reload = function (id) {
|
||||
|
||||
/**
|
||||
* Reload the episode table.
|
||||
* Since we're sometimes using a static source, define a separate function to fetch and reload the table data.
|
||||
* We use this when we save the Podcast because we need to flag rows the user is ingesting.
|
||||
*
|
||||
* @param [id] optional podcast identifier
|
||||
*/
|
||||
PodcastEpisodeTable.prototype.reload = function (id) {
|
||||
// When using static source data, we instantiate an empty table
|
||||
// and pass this function the ID of the podcast we want to display.
|
||||
if (id) this.config.podcastId = id;
|
||||
var dt = this._datatable;
|
||||
$.get(endpoint + id + '/episodes', function (json) {
|
||||
dt.block({
|
||||
message: "",
|
||||
theme: true,
|
||||
applyPlatformOpacityRules: false
|
||||
});
|
||||
$.get(endpoint + this.config.podcastId + '/episodes', function (json) {
|
||||
dt.fnClearTable();
|
||||
dt.fnAddData(JSON.parse(json));
|
||||
dt.fnDraw();
|
||||
dt.unblock();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup an interval that checks for any pending imports and reloads
|
||||
* the table once imports are finished.
|
||||
*
|
||||
* TODO: remember selection
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
PodcastEpisodeTable.prototype._setupImportListener = function () {
|
||||
var self = this;
|
||||
self.importListener = setInterval(function () {
|
||||
var podcastId = self.config.podcastId, pendingRows = [];
|
||||
if (!podcastId) return false;
|
||||
var dt = self.getDatatable(), data = dt.fnGetData();
|
||||
// Iterate over the table data to check for any rows pending import
|
||||
$.each(data, function () {
|
||||
if (this.ingested == -1) {
|
||||
pendingRows.push(this.guid);
|
||||
}
|
||||
});
|
||||
console.log(pendingRows);
|
||||
if (pendingRows.length > 0) {
|
||||
$.get(endpoint + podcastId + '/episodes', function (json) {
|
||||
data = JSON.parse(json);
|
||||
var delta = false;
|
||||
$.each(data, function () {
|
||||
var idx = pendingRows.indexOf(this.guid);
|
||||
console.log(idx);
|
||||
if (idx > -1 && this.ingested != -1) {
|
||||
delta = true;
|
||||
pendingRows.slice(idx, 0);
|
||||
}
|
||||
});
|
||||
if (delta) { // Has there been a change?
|
||||
// We already have the data, so there's no reason to call
|
||||
// reload() here; this also provides a smoother transition
|
||||
dt.fnClearTable();
|
||||
dt.fnAddData(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 15000); // Run every 15 seconds
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and show the URL dialog for podcast creation.
|
||||
*/
|
||||
mod.createUrlDialog = function() {
|
||||
mod.createUrlDialog = function () {
|
||||
$.get('/render/podcast-url-dialog', function(json) {
|
||||
$(document.body).append(json.html);
|
||||
$("#podcast_url_dialog").dialog({
|
||||
|
@ -402,7 +488,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
*
|
||||
* FIXME: we should probably be passing the serialized form into this function instead
|
||||
*/
|
||||
mod.addPodcast = function() {
|
||||
mod.addPodcast = function () {
|
||||
$.post(endpoint, $("#podcast_url_dialog").find("form").serialize(), function(json) {
|
||||
_initAppFromResponse(json);
|
||||
$("#podcast_url_dialog").dialog("close");
|
||||
|
@ -412,7 +498,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
/**
|
||||
* Open a tab to view and edit the station podcast.
|
||||
*/
|
||||
mod.openStationPodcast = function() {
|
||||
mod.openStationPodcast = function () {
|
||||
if (typeof $stationPodcastTab === 'undefined') {
|
||||
$.get(endpoint + 'station', function(json) {
|
||||
_initAppFromResponse(json);
|
||||
|
@ -425,7 +511,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
/**
|
||||
* Create a bulk request to edit all currently selected podcasts.
|
||||
*/
|
||||
mod.editSelectedPodcasts = function() {
|
||||
mod.editSelectedPodcasts = function () {
|
||||
_bulkAction(AIRTIME.library.podcastTableWidget.getSelectedRows(), HTTPMethods.GET, function(json) {
|
||||
json.forEach(function(data) {
|
||||
_initAppFromResponse(data);
|
||||
|
@ -436,7 +522,7 @@ var AIRTIME = (function (AIRTIME) {
|
|||
/**
|
||||
* Create a bulk request to delete all currently selected podcasts.
|
||||
*/
|
||||
mod.deleteSelectedPodcasts = function() {
|
||||
mod.deleteSelectedPodcasts = function () {
|
||||
if (confirm($.i18n._("Are you sure you want to delete the selected podcasts from your library?"))) {
|
||||
_bulkAction(AIRTIME.library.podcastTableWidget.getSelectedRows(), HTTPMethods.DELETE, function () {
|
||||
AIRTIME.library.podcastDataTable.fnDraw();
|
||||
|
@ -444,51 +530,73 @@ var AIRTIME = (function (AIRTIME) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Import one or more podcast episodes.
|
||||
*
|
||||
* @param {Array} episodes array of episode data to be imported
|
||||
* @param {PodcastEpisodeTable} dt PodcastEpisode table containing the data
|
||||
*/
|
||||
mod.importSelectedEpisodes = function (episodes, dt) {
|
||||
$.each(episodes, function () {
|
||||
var podcastId = this.podcast_id;
|
||||
$.post(endpoint + podcastId + '/episodes', JSON.stringify({
|
||||
csrf_token: $("#csrf").val(),
|
||||
episode: this
|
||||
}), function () {
|
||||
dt.reload(podcastId);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the internal datatable for the podcast editor view to hold episode data passed back from the server.
|
||||
*
|
||||
* Selection for the internal table represents episodes marked for ingest and is disabled for ingested episodes.
|
||||
*
|
||||
* @param {Object} domNode the jQuery DOM node to create the table inside.
|
||||
* @param {jQuery} domNode the jQuery DOM node to create the table inside.
|
||||
* @param {Object} params JSON object containing datatables parameters to override
|
||||
* @param {Object} buttons JSON object containing datatables button parameters
|
||||
* @param {Object} config JSON object containing internal PodcastEpisodeTable parameters
|
||||
* @param {boolean} config.hideIngestCheckboxes flag denoting whether or not to hide checkboxes for ingested items
|
||||
*
|
||||
* @returns {*} the created Table object
|
||||
* @returns {Table} the created Table object
|
||||
*/
|
||||
mod.initPodcastEpisodeDatatable = function(domNode, params, buttons) {
|
||||
if ('slideToggle' in buttons)
|
||||
mod.initPodcastEpisodeDatatable = function (domNode, params, buttons, config) {
|
||||
if ('slideToggle' in buttons) {
|
||||
buttons = $.extend(true, {
|
||||
slideToggle: {
|
||||
title : '',
|
||||
iconClass : 'spl-no-r-margin icon-chevron-up',
|
||||
extraBtnClass : 'toggle-editor-form',
|
||||
elementId : '',
|
||||
eventHandlers : {}
|
||||
title: '',
|
||||
iconClass: 'spl-no-r-margin icon-chevron-up',
|
||||
extraBtnClass: 'toggle-editor-form',
|
||||
elementId: '',
|
||||
eventHandlers: {}
|
||||
}
|
||||
}, buttons);
|
||||
params = $.extend(params,
|
||||
}
|
||||
params = $.extend(true, params,
|
||||
{
|
||||
oColVis: {
|
||||
sAlign : 'right',
|
||||
aiExclude : [0, 1],
|
||||
buttonText : $.i18n._("Columns"),
|
||||
sAlign: 'right',
|
||||
aiExclude: [0, 1],
|
||||
buttonText: $.i18n._("Columns"),
|
||||
iOverlayFade: 0,
|
||||
oColReorder : {
|
||||
oColReorder: {
|
||||
iFixedColumns: 1 // Checkbox
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (typeof PodcastTable === 'undefined') {
|
||||
_initPodcastTable();
|
||||
if (typeof PodcastEpisodeTable === 'undefined') {
|
||||
_initPodcastEpisodeTable();
|
||||
}
|
||||
|
||||
var podcastEpisodesTableWidget = new PodcastTable(
|
||||
var podcastEpisodesTableWidget = new PodcastEpisodeTable(
|
||||
domNode, // DOM node to create the table inside.
|
||||
true, // Enable item selection
|
||||
buttons, // Toolbar buttons
|
||||
params // Datatables overrides.
|
||||
params, // Datatables overrides.
|
||||
config // Internal config
|
||||
);
|
||||
|
||||
podcastEpisodesTableWidget.getDatatable().addTitles("td");
|
||||
|
|
|
@ -31,7 +31,6 @@ var AIRTIME = (function(AIRTIME) {
|
|||
self._selectedRowVisualIdxMax = -1;
|
||||
self._$wrapperDOMNode = null;
|
||||
self._toolbarButtons = null;
|
||||
|
||||
//Save some of the constructor parameters
|
||||
self._$wrapperDOMNode = $(wrapperDOMNode);
|
||||
self._toolbarButtons = toolbarButtons;
|
||||
|
@ -44,7 +43,7 @@ var AIRTIME = (function(AIRTIME) {
|
|||
// If selection is enabled, add in the checkbox column.
|
||||
if (bItemSelection) {
|
||||
dataTablesOptions["aoColumns"].unshift(
|
||||
/* Checkbox */ { "sTitle" : "", "mData" : self._datatablesCheckboxDataDelegate, "bSortable" : false , "bSearchable" : false , "sWidth" : "16px" , "sClass" : "airtime_table_checkbox" }
|
||||
/* Checkbox */ { "sTitle" : "", "mData" : self._datatablesCheckboxDataDelegate.bind(this), "bSortable" : false , "bSearchable" : false , "sWidth" : "24px" , "sClass" : "airtime_table_checkbox" }
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -68,7 +67,7 @@ var AIRTIME = (function(AIRTIME) {
|
|||
}),
|
||||
"oColVis": {
|
||||
"sAlign": "right",
|
||||
"aiExclude": self.colVisExcludeColumns,
|
||||
"aiExclude": self._colVisExcludeColumns,
|
||||
"buttonText": $.i18n._("Columns"),
|
||||
"iOverlayFade": 0
|
||||
},
|
||||
|
@ -428,7 +427,6 @@ var AIRTIME = (function(AIRTIME) {
|
|||
|
||||
//Static initializers / Class variables
|
||||
|
||||
/** Predefined toolbar buttons that you can add to the table. Use getStandardToolbarButtons(). */
|
||||
Table.prototype._SELECTORS = Object.freeze({
|
||||
SELECTION_CHECKBOX: ".airtime_table_checkbox",
|
||||
SELECTION_TABLE_ROW: "tr"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue