SAAS-1165 - implement most functionality for left-hand podcast episodes view

This commit is contained in:
Duncan Sommerville 2015-11-05 18:15:58 -05:00
parent a984cee13a
commit 0be26d621b
11 changed files with 375 additions and 150 deletions

View file

@ -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"]);

View file

@ -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") {

View file

@ -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
));
}

View 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'>

View file

@ -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;
}

View file

@ -186,7 +186,7 @@
}
.btn-toolbar {
margin: 0;
/*margin: 0;*/
padding: 5px;
}

View file

@ -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;
}

View file

@ -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'

View file

@ -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;

View file

@ -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");

View file

@ -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"