From bd409fa1009028f27554c5ad62166c2a924cce82 Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 20 Dec 2018 13:23:13 -0600 Subject: [PATCH 1/7] podcasts: open edit pane upon submission of popup --- airtime_mvc/public/js/airtime/library/podcast.js | 1 + 1 file changed, 1 insertion(+) diff --git a/airtime_mvc/public/js/airtime/library/podcast.js b/airtime_mvc/public/js/airtime/library/podcast.js index ac90eb588..31cb4bd84 100644 --- a/airtime_mvc/public/js/airtime/library/podcast.js +++ b/airtime_mvc/public/js/airtime/library/podcast.js @@ -507,6 +507,7 @@ var AIRTIME = (function (AIRTIME) { AIRTIME.library.podcastTableWidget.clearSelection(); AIRTIME.library.setCurrentTable(AIRTIME.library.DataTableTypeEnum.PODCAST_EPISODES); $("#podcast_url_dialog").dialog("close"); + _initAppFromResponse(json); }).fail(function (e) { var errors = $("#podcast_url_dialog").find(".errors"); errors.show(200).text(e.responseText); From e672e81d8f7b74e4e55d2d1a9fcb4f8b366a84ee Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 26 Dec 2018 18:06:07 -0600 Subject: [PATCH 2/7] restore station podcast onSaveCallback --- .../public/js/airtime/library/podcast.js | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/airtime_mvc/public/js/airtime/library/podcast.js b/airtime_mvc/public/js/airtime/library/podcast.js index 31cb4bd84..976ffacda 100644 --- a/airtime_mvc/public/js/airtime/library/podcast.js +++ b/airtime_mvc/public/js/airtime/library/podcast.js @@ -145,21 +145,20 @@ var AIRTIME = (function (AIRTIME) { function StationPodcastController($scope, $http, podcast, tab) { // Super call to parent controller PodcastController.call(this, $scope, $http, podcast, tab); - // @frecuencialibre commenting this out since i think it is never called. @todo delete once confirmed - // this.onSaveCallback = function () { - // $http({ - // method: 'POST', - // url: '/preference/station-podcast-settings', - // headers: {'Content-Type': 'application/x-www-form-urlencoded'}, - // data: { stationPodcastPrivacy: $("#podcast-settings").find("input:checked").val() } - // }).success(function (data) { - // jQuery.extend($scope.podcast, data); - // $(".success").text($.i18n._("Podcast settings saved")).slideDown("fast"); - // setTimeout(function () { - // $(".success").slideUp("fast"); - // }, 2000); - // }); - // }; + this.onSaveCallback = function () { + $http({ + method: 'POST', + url: '/preference/station-podcast-settings', + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + data: { stationPodcastPrivacy: $("#podcast-settings").find("input:checked").val() } + }).success(function (data) { + jQuery.extend($scope.podcast, data); + $(".success").text($.i18n._("Podcast settings saved")).slideDown("fast"); + setTimeout(function () { + $(".success").slideUp("fast"); + }, 2000); + }); + }; return this; } @@ -324,7 +323,7 @@ var AIRTIME = (function (AIRTIME) { uid = AIRTIME.library.MediaTypeStringEnum.PODCAST+"_"+podcast.id, tab = AIRTIME.tabs.openTab(data.html, uid, null); _bootstrapAngularApp(podcast, tab); - + $(".album_names.help_icon").qtip({ content: { text: $.i18n._('Overwrite downloaded podcast episodes\' "Album" metadata tag with the Podcast Name specified above. This album name can then be used as a search criteria by a smartblock.') From 8f8e9ef3f317b6c3ec5b2e7c6b5505a8bcd9bfaa Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 27 Dec 2018 16:48:28 -0600 Subject: [PATCH 3/7] progress commit: display podcast episodes in right-side pane --- .../public/js/airtime/library/library.js | 138 ++++------------ .../public/js/airtime/library/podcast.js | 155 ++++++++++-------- 2 files changed, 125 insertions(+), 168 deletions(-) diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index b1e3b5075..ff5561871 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -1398,31 +1398,6 @@ var AIRTIME = (function(AIRTIME) { } }); - var openPodcastEpisodeTable = function (podcast) { - $("#library_filter").append(" - " + podcast.title); - mod.podcastEpisodeTableWidget.reload(podcast.id); - mod.podcastTableWidget.clearSelection(); - mod.setCurrentTable(mod.DataTableTypeEnum.PODCAST_EPISODES); - mod.podcastEpisodeDataTable.closest(".dataTables_wrapper").find(".dataTables_processing") - .addClass("block-overlay").css("visibility", "visible"); - }; - - podcastToolbarButtons["ViewEpisodes"] = { - title : $.i18n._("View Episodes"), - iconClass : "icon-chevron-right", - extraBtnClass : "btn-small", - elementId : "", - eventHandlers : { - click: function () { - var podcast = mod.podcastTableWidget.getSelectedRows()[0]; - openPodcastEpisodeTable(podcast); - } - }, - validateConstraints: function () { - return this.getSelectedRows().length == 1; - } - }; - //Set up the div with id "podcast_table" as a datatable. mod.podcastTableWidget = new AIRTIME.widgets.Table( $('#podcast_table'), //DOM node to create the table inside. @@ -1439,12 +1414,9 @@ var AIRTIME = (function(AIRTIME) { } }); - mod._initPodcastEpisodeDatatable(); - // On double click, open a table showing the selected podcast's episodes - // in the left-hand pane. + // Edit podcast in right-side pane upon double click mod.podcastTableWidget.assignDblClickHandler(function () { - var podcast = mod.podcastDataTable.fnGetData(this); - openPodcastEpisodeTable(podcast); + AIRTIME.podcast.editSelectedPodcasts(); }); mod.podcastDataTable = mod.podcastTableWidget.getDatatable(); @@ -1452,27 +1424,9 @@ var AIRTIME = (function(AIRTIME) { }; /** - * Initialize the podcast episode view for the left-hand pane - * - * @private + * Initialize the podcast episode table with working buttons */ - mod._initPodcastEpisodeDatatable = function () { - var buttons = { - backBtn: { - title : $.i18n._('Back to Podcasts'), - iconClass : 'icon-chevron-left', - extraBtnClass : 'btn-small', - elementId : '', - eventHandlers : { - click: function () { - $("#library_filter").text($.i18n._("Podcasts")); - mod.setCurrentTable(mod.DataTableTypeEnum.PODCAST); - } - }, - validateConstraints: function () { return true; } - } - }, - defaults = AIRTIME.widgets.Table.getStandardToolbarButtons(); + mod.initPodcastEpisodeDatatableWithButtonEvents = function (domNode) { /** * Check the import statuses of each selected episode to see which @@ -1498,7 +1452,8 @@ var AIRTIME = (function(AIRTIME) { }; // Setup the default buttons (new, edit, delete) - $.extend(true, defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW], + podcastEpisodeButtons = AIRTIME.widgets.Table.getStandardToolbarButtons(); + $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW], { title: "Import", eventHandlers: { @@ -1511,7 +1466,7 @@ var AIRTIME = (function(AIRTIME) { return checkSelectedEpisodeImportStatus.call(this, false); } }); - $.extend(true, defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT], + $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT], { eventHandlers: { click: function () { @@ -1523,7 +1478,7 @@ var AIRTIME = (function(AIRTIME) { return checkSelectedEpisodeImportStatus.call(this, true); } }); - $.extend(true, defaults[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE], + $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE], { eventHandlers: { click: function () { @@ -1531,7 +1486,8 @@ var AIRTIME = (function(AIRTIME) { $.each(episodes, function () { data.push({id: this.file.id, type: this.file.ftype}); }); - mod.fnDeleteItems(data); + console.log("podcast deletion:", data); + AIRTIME.podcast.deleteSelectedEpisodes(data, mod.podcastEpisodeTableWidget); } }, validateConstraints: function () { @@ -1541,30 +1497,30 @@ var AIRTIME = (function(AIRTIME) { // Reassign these because integer keys take precedence in iteration order - we want to order based on insertion // FIXME: this is a pretty flimsy way to try to set up iteration order (possibly not xbrowser compatible?) - 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] + podcastEpisodeButtons = { + newBtn : podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW], + editBtn: podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT], + delBtn : podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE] }; - $.extend(true, buttons, defaults, { - addToScheduleBtn: { - title : $.i18n._('Add to Schedule'), - iconClass : '', - extraBtnClass : 'btn-small', - elementId : '', - eventHandlers : { - click: function () { - var data = [], selected = mod.podcastEpisodeTableWidget.getSelectedRows(); - $.each(selected, function () { data.push(this.file); }); - mod.addToSchedule(data); - } - }, - validateConstraints: function () { - // TODO: change text + behaviour for playlists, smart blocks, etc. - return checkSelectedEpisodeImportStatus.call(this, true); - } - }, + $.extend(true, podcastEpisodeButtons, { + // addToScheduleBtn: { + // title : $.i18n._('Add to Schedule'), + // iconClass : '', + // extraBtnClass : 'btn-small', + // elementId : '', + // eventHandlers : { + // click: function () { + // var data = [], selected = mod.podcastEpisodeTableWidget.getSelectedRows(); + // $.each(selected, function () { data.push(this.file); }); + // mod.addToSchedule(data); + // } + // }, + // validateConstraints: function () { + // // TODO: change text + behaviour for playlists, smart blocks, etc. + // return checkSelectedEpisodeImportStatus.call(this, true); + // } + // }, viewDescBtn: { title : $.i18n._("View"), iconClass : "icon-globe", @@ -1580,32 +1536,8 @@ var AIRTIME = (function(AIRTIME) { }); mod.podcastEpisodeTableWidget = AIRTIME.podcast.initPodcastEpisodeDatatable( - $("#podcast_episodes_table"), - { - 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 : { - buttonText: $.i18n._("Columns"), - iOverlayFade: 0, - aiExclude: [0, 1, 2] - }, - oColReorder: { - iFixedColumns: 3 // Checkbox + imported - } - }, - buttons, + domNode, + podcastEpisodeButtons, { hideIngestCheckboxes: false, emptyPlaceholder: { @@ -1628,6 +1560,8 @@ var AIRTIME = (function(AIRTIME) { } } }); + + return mod.podcastEpisodeTableWidget; }; mod.libraryInit = libraryInit; diff --git a/airtime_mvc/public/js/airtime/library/podcast.js b/airtime_mvc/public/js/airtime/library/podcast.js index 976ffacda..6d946b961 100644 --- a/airtime_mvc/public/js/airtime/library/podcast.js +++ b/airtime_mvc/public/js/airtime/library/podcast.js @@ -345,6 +345,12 @@ var AIRTIME = (function (AIRTIME) { } }); + // Add podcast episode table in right-side panel below podcast edit form + var episodeTable = AIRTIME.library.initPodcastEpisodeDatatableWithButtonEvents( + $("#podcast_episodes_" + podcast.id), + ); + episodeTable.reload(podcast.id); + episodeTable.clearSelection() } /** @@ -501,11 +507,13 @@ var AIRTIME = (function (AIRTIME) { */ mod.addPodcast = function () { $.post(endpoint, $("#podcast_url_dialog").find("form").serialize(), function(json) { - // Open the episode view for the newly created podcast in the left-hand pane - AIRTIME.library.podcastEpisodeTableWidget.reload(JSON.parse(json.podcast).id); - AIRTIME.library.podcastTableWidget.clearSelection(); - AIRTIME.library.setCurrentTable(AIRTIME.library.DataTableTypeEnum.PODCAST_EPISODES); + // Refresh left-side library pane to show newly created podcast + AIRTIME.library.podcastDataTable.fnDraw(); + + // close modal $("#podcast_url_dialog").dialog("close"); + + // open newly created podcast in right-side edit pane _initAppFromResponse(json); }).fail(function (e) { var errors = $("#podcast_url_dialog").find(".errors"); @@ -560,6 +568,7 @@ var AIRTIME = (function (AIRTIME) { * @param {PodcastEpisodeTable} dt PodcastEpisode table containing the data */ mod.importSelectedEpisodes = function (episodes, dt) { + console.log("importSelectedEpisodes", episodes, dt); $.each(episodes, function () { // remainingDiskSpace is defined in layout.phtml if (this.enclosure.length > remainingDiskSpace) { @@ -567,12 +576,11 @@ var AIRTIME = (function (AIRTIME) { return false; } if (this.file && Object.keys(this.file).length > 0) return false; - var podcastId = this.podcast_id; - $.post(endpoint + podcastId + '/episodes', JSON.stringify({ + $.post(endpoint + this.podcast_id + '/episodes', JSON.stringify({ csrf_token: $("#csrf").val(), episode: this }), function () { - dt.reload(podcastId); + dt.reload(this.podcast_id); }); remainingDiskSpace -= this.enclosure.length; @@ -581,6 +589,20 @@ var AIRTIME = (function (AIRTIME) { dt.clearSelection(); }; + /** + * Delete one or more podcast episodes. + * + * @param {id:string, type:string}[] data Array of data objects to be deleted + * @param {PodcastEpisodeTable} dt PodcastEpisode table containing the data + */ + mod.deleteSelectedEpisodes = function (data, dt) { + $.each(data, function () { + AIRTIME.library.fnDeleteItems(data); + }); + dt.reload(this.podcast_id); + dt.clearSelection(); + }; + /** * Initialize the internal datatable for the podcast editor view to hold episode data passed back from the server. * @@ -594,73 +616,74 @@ var AIRTIME = (function (AIRTIME) { * * @returns {Table} the created Table object */ - 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: {}, - validateConstraints: function () { return true; } - } - }, buttons); - } - params = $.extend(true, params, - { - bDeferRender: true, - oColVis: { - buttonText: $.i18n._("Columns"), - iOverlayFade: 0, - aiExclude: [0] - }, - oColReorder: { - iFixedColumns: 1 // Checkbox - }, - fnCreatedRow: function(nRow, aData, iDataIndex) { - var self = this; - if (aData.file && Object.keys(aData.file).length > 0) { - $(nRow).draggable({ - helper: function () { - var $row = $(this), data = self._datatable.fnGetData(nRow); - $row.data("aData", data.file); - self.selectRow(this, data, self.SELECTION_MODE.SINGLE, $row.index()); - var selected = self.getSelectedRows().length, container, - width = self._$wrapperDOMNode.closest(".dataTables_wrapper").outerWidth(), message; + mod.initPodcastEpisodeDatatable = function (domNode, buttons, config) { - message = sprintf($.i18n._(selected > 1 ? "Adding %s Items" : "Adding %s Item"), selected); - container = $('
').attr('id', 'draggingContainer').append('') - .find("tr").append('').find("td") - .attr("colspan", 100).width(width).css("max-width", "none") - .addClass("ui-state-highlight").append(message).end().end(); + params = { + 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 : { + buttonText: $.i18n._("Columns"), + iOverlayFade: 0, + aiExclude: [0, 1, 2] + }, + bDeferRender: true, + oColReorder: { + iFixedColumns: 3 // Checkbox + imported + }, + fnCreatedRow: function(nRow, aData, iDataIndex) { + var self = this; + if (aData.file && Object.keys(aData.file).length > 0) { + $(nRow).draggable({ + helper: function () { + var $row = $(this), data = self._datatable.fnGetData(nRow); + $row.data("aData", data.file); + self.selectRow(this, data, self.SELECTION_MODE.SINGLE, $row.index()); + var selected = self.getSelectedRows().length, container, + width = self._$wrapperDOMNode.closest(".dataTables_wrapper").outerWidth(), message; - return container; - }, - tolerance: 'pointer', - cursor: 'move', - cursorAt: { - top: 20, - left: Math.floor(self._datatable.outerWidth() / 2) - }, - distance: 25, // min-distance for dragging - connectToSortable: $("#show_builder_table, .active-tab .spl_sortable") - }); - } - }, - fnDrawCallback: function () { - AIRTIME.library.drawEmptyPlaceholder(this); - // Hide the processing div - var dt = this.getDatatable(); - !dt || dt.closest(".dataTables_wrapper").find(".dataTables_processing").css("visibility", "hidden"); + message = sprintf($.i18n._(selected > 1 ? "Adding %s Items" : "Adding %s Item"), selected); + container = $('
').attr('id', 'draggingContainer').append('') + .find("tr").append('').find("td") + .attr("colspan", 100).width(width).css("max-width", "none") + .addClass("ui-state-highlight").append(message).end().end(); + + return container; + }, + tolerance: 'pointer', + cursor: 'move', + cursorAt: { + top: 20, + left: Math.floor(self._datatable.outerWidth() / 2) + }, + distance: 25, // min-distance for dragging + connectToSortable: $("#show_builder_table, .active-tab .spl_sortable") + }); } + }, + fnDrawCallback: function () { + AIRTIME.library.drawEmptyPlaceholder(this); + // Hide the processing div + var dt = this.getDatatable(); + !dt || dt.closest(".dataTables_wrapper").find(".dataTables_processing").css("visibility", "hidden"); } - ); + } if (typeof PodcastEpisodeTable === 'undefined') { _initPodcastEpisodeTable(); } - + var podcastEpisodesTableWidget = new PodcastEpisodeTable( domNode, // DOM node to create the table inside. true, // Enable item selection From fb35881df7e3611a9ed39d9ebc2ad8d103511fdf Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 28 Dec 2018 14:19:40 -0600 Subject: [PATCH 4/7] move initPodcastEpisodeDatatable functions within podcast.js --- .../public/js/airtime/library/library.js | 143 +---------------- .../public/js/airtime/library/podcast.js | 151 +++++++++++++++++- 2 files changed, 147 insertions(+), 147 deletions(-) diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index ff5561871..37f1e5253 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -504,6 +504,8 @@ var AIRTIME = (function(AIRTIME) { } chosenItems = {}; + + // TODO: correct check whether used to delete episodes if (oTable == $datatables[mod.DataTableTypeEnum.PODCAST_EPISODES]) { mod.podcastEpisodeTableWidget.reload(); } else { @@ -1423,147 +1425,6 @@ var AIRTIME = (function(AIRTIME) { $datatables[mod.DataTableTypeEnum.PODCAST] = mod.podcastDataTable; }; - /** - * Initialize the podcast episode table with working buttons - */ - mod.initPodcastEpisodeDatatableWithButtonEvents = function (domNode) { - - /** - * Check the import statuses of each selected episode to see which - * buttons should be enabled or disabled. - * - * @param shouldBeImported whether or not the selected item(s) - * should be imported to obtain a valid result. - * - * @returns {boolean} true if all selected episodes are valid and - * the button should be enabled, otherwise false. - */ - var checkSelectedEpisodeImportStatus = function (shouldBeImported) { - var selected = this.getSelectedRows(), isValid = true; - if (selected.length == 0) return false; - $.each(selected, function () { - if (this.ingested < 0) isValid = false; - var isImported = !$.isEmptyObject(this.file); - if (shouldBeImported ? !isImported : isImported) { - isValid = false; - } - }); - return isValid; - }; - - // Setup the default buttons (new, edit, delete) - podcastEpisodeButtons = AIRTIME.widgets.Table.getStandardToolbarButtons(); - $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW], - { - title: "Import", - eventHandlers: { - click: function () { - var episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); - AIRTIME.podcast.importSelectedEpisodes(episodes, mod.podcastEpisodeTableWidget); - } - }, - validateConstraints: function () { - return checkSelectedEpisodeImportStatus.call(this, false); - } - }); - $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT], - { - eventHandlers: { - click: function () { - var episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); - AIRTIME.podcast.editSelectedEpisodes(episodes); - } - }, - validateConstraints: function () { - return checkSelectedEpisodeImportStatus.call(this, true); - } - }); - $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE], - { - eventHandlers: { - click: function () { - var data = [], episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); - $.each(episodes, function () { - data.push({id: this.file.id, type: this.file.ftype}); - }); - console.log("podcast deletion:", data); - AIRTIME.podcast.deleteSelectedEpisodes(data, mod.podcastEpisodeTableWidget); - } - }, - validateConstraints: function () { - return checkSelectedEpisodeImportStatus.call(this, true); - } - }); - - // Reassign these because integer keys take precedence in iteration order - we want to order based on insertion - // FIXME: this is a pretty flimsy way to try to set up iteration order (possibly not xbrowser compatible?) - podcastEpisodeButtons = { - newBtn : podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW], - editBtn: podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT], - delBtn : podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE] - }; - - $.extend(true, podcastEpisodeButtons, { - // addToScheduleBtn: { - // title : $.i18n._('Add to Schedule'), - // iconClass : '', - // extraBtnClass : 'btn-small', - // elementId : '', - // eventHandlers : { - // click: function () { - // var data = [], selected = mod.podcastEpisodeTableWidget.getSelectedRows(); - // $.each(selected, function () { data.push(this.file); }); - // mod.addToSchedule(data); - // } - // }, - // validateConstraints: function () { - // // TODO: change text + behaviour for playlists, smart blocks, etc. - // return checkSelectedEpisodeImportStatus.call(this, true); - // } - // }, - viewDescBtn: { - title : $.i18n._("View"), - iconClass : "icon-globe", - extraBtnClass : "btn-small", - elementId : "", - eventHandlers : { - click: mod.openPodcastEpisodeDialog - }, - validateConstraints: function () { - return this.getSelectedRows().length == 1; - } - } - }); - - mod.podcastEpisodeTableWidget = AIRTIME.podcast.initPodcastEpisodeDatatable( - domNode, - podcastEpisodeButtons, - { - hideIngestCheckboxes: false, - emptyPlaceholder: { - iconClass: "icon-white icon-th-list", - html: $.i18n._("This podcast doesn't have any episodes!") - + "
" + $.i18n._("Make sure the RSS feed contains audio items (with enclosure tags).") - + "
" + $.i18n._("Learn about podcasts") + "" - } - } - ); - - mod.podcastEpisodeDataTable = $datatables[mod.DataTableTypeEnum.PODCAST_EPISODES] = mod.podcastEpisodeTableWidget.getDatatable(); - mod.podcastEpisodeTableWidget.assignDblClickHandler(function () { - var data = mod.podcastEpisodeDataTable.fnGetData(this); - if (!$.isEmptyObject(data.file)) { - mod.dblClickAdd(data.file, data.file.ftype); - } else { - if (data.ingested >= 0) { // Only import if the file isn't pending - AIRTIME.podcast.importSelectedEpisodes([data], mod.podcastEpisodeTableWidget); - } - } - }); - - return mod.podcastEpisodeTableWidget; - }; - mod.libraryInit = libraryInit; return AIRTIME; diff --git a/airtime_mvc/public/js/airtime/library/podcast.js b/airtime_mvc/public/js/airtime/library/podcast.js index 6d946b961..9c4965b24 100644 --- a/airtime_mvc/public/js/airtime/library/podcast.js +++ b/airtime_mvc/public/js/airtime/library/podcast.js @@ -346,7 +346,7 @@ var AIRTIME = (function (AIRTIME) { }); // Add podcast episode table in right-side panel below podcast edit form - var episodeTable = AIRTIME.library.initPodcastEpisodeDatatableWithButtonEvents( + var episodeTable = AIRTIME.podcast.initPodcastEpisodeDatatableWithButtonEvents( $("#podcast_episodes_" + podcast.id), ); episodeTable.reload(podcast.id); @@ -568,7 +568,6 @@ var AIRTIME = (function (AIRTIME) { * @param {PodcastEpisodeTable} dt PodcastEpisode table containing the data */ mod.importSelectedEpisodes = function (episodes, dt) { - console.log("importSelectedEpisodes", episodes, dt); $.each(episodes, function () { // remainingDiskSpace is defined in layout.phtml if (this.enclosure.length > remainingDiskSpace) { @@ -603,13 +602,153 @@ var AIRTIME = (function (AIRTIME) { dt.clearSelection(); }; + /** + * Initialize the podcast episode table with working buttons + */ + mod.initPodcastEpisodeDatatableWithButtonEvents = function (domNode) { + + /** + * Check the import statuses of each selected episode to see which + * buttons should be enabled or disabled. + * + * @param shouldBeImported whether or not the selected item(s) + * should be imported to obtain a valid result. + * + * @returns {boolean} true if all selected episodes are valid and + * the button should be enabled, otherwise false. + */ + var checkSelectedEpisodeImportStatus = function (shouldBeImported) { + var selected = this.getSelectedRows(), isValid = true; + if (selected.length == 0) return false; + $.each(selected, function () { + if (this.ingested < 0) isValid = false; + var isImported = !$.isEmptyObject(this.file); + if (shouldBeImported ? !isImported : isImported) { + isValid = false; + } + }); + return isValid; + }; + + // Setup the default buttons (new, edit, delete) + podcastEpisodeButtons = AIRTIME.widgets.Table.getStandardToolbarButtons(); + $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW], + { + title: "Import", + eventHandlers: { + click: function () { + var episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); + AIRTIME.podcast.importSelectedEpisodes(episodes, mod.podcastEpisodeTableWidget); + } + }, + validateConstraints: function () { + return checkSelectedEpisodeImportStatus.call(this, false); + } + }); + $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT], + { + eventHandlers: { + click: function () { + var episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); + AIRTIME.podcast.editSelectedEpisodes(episodes); + } + }, + validateConstraints: function () { + return checkSelectedEpisodeImportStatus.call(this, true); + } + }); + $.extend(true, podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE], + { + eventHandlers: { + click: function () { + var data = [], episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); + $.each(episodes, function () { + data.push({id: this.file.id, type: this.file.ftype}); + }); + console.log("podcast deletion:", data, mod.podcastEpisodeTableWidget); + AIRTIME.podcast.deleteSelectedEpisodes(data, mod.podcastEpisodeTableWidget); + } + }, + validateConstraints: function () { + return checkSelectedEpisodeImportStatus.call(this, true); + } + }); + + // Reassign these because integer keys take precedence in iteration order - we want to order based on insertion + // FIXME: this is a pretty flimsy way to try to set up iteration order (possibly not xbrowser compatible?) + podcastEpisodeButtons = { + newBtn : podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.NEW], + editBtn: podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.EDIT], + delBtn : podcastEpisodeButtons[AIRTIME.widgets.Table.TOOLBAR_BUTTON_ROLES.DELETE] + }; + + $.extend(true, podcastEpisodeButtons, { + // addToScheduleBtn: { + // title : $.i18n._('Add to Schedule'), + // iconClass : '', + // extraBtnClass : 'btn-small', + // elementId : '', + // eventHandlers : { + // click: function () { + // var data = [], selected = mod.podcastEpisodeTableWidget.getSelectedRows(); + // $.each(selected, function () { data.push(this.file); }); + // mod.addToSchedule(data); + // } + // }, + // validateConstraints: function () { + // // TODO: change text + behaviour for playlists, smart blocks, etc. + // return checkSelectedEpisodeImportStatus.call(this, true); + // } + // }, + viewDescBtn: { + title : $.i18n._("View"), + iconClass : "icon-globe", + extraBtnClass : "btn-small", + elementId : "", + eventHandlers : { + click: mod.openPodcastEpisodeDialog + }, + validateConstraints: function () { + return this.getSelectedRows().length == 1; + } + } + }); + + mod.podcastEpisodeTableWidget = AIRTIME.podcast.initPodcastEpisodeDatatable( + domNode, + podcastEpisodeButtons, + { + hideIngestCheckboxes: false, + emptyPlaceholder: { + iconClass: "icon-white icon-th-list", + html: $.i18n._("This podcast doesn't have any episodes!") + + "
" + $.i18n._("Make sure the RSS feed contains audio items (with enclosure tags).") + + "
" + $.i18n._("Learn about podcasts") + "" + } + } + ); + + mod.podcastEpisodeDataTable = mod.podcastEpisodeTableWidget.getDatatable(); + mod.podcastEpisodeTableWidget.assignDblClickHandler(function () { + var data = mod.podcastEpisodeDataTable.fnGetData(this); + if (!$.isEmptyObject(data.file)) { + mod.dblClickAdd(data.file, data.file.ftype); + } else { + if (data.ingested >= 0) { // Only import if the file isn't pending + AIRTIME.podcast.importSelectedEpisodes([data], mod.podcastEpisodeTableWidget); + } + } + }); + + return mod.podcastEpisodeTableWidget; + }; + /** * 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 {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 @@ -684,7 +823,7 @@ var AIRTIME = (function (AIRTIME) { _initPodcastEpisodeTable(); } - var podcastEpisodesTableWidget = new PodcastEpisodeTable( + var podcastEpisodeTableObj = new PodcastEpisodeTable( domNode, // DOM node to create the table inside. true, // Enable item selection buttons, // Toolbar buttons @@ -692,8 +831,8 @@ var AIRTIME = (function (AIRTIME) { config // Internal config ); - podcastEpisodesTableWidget.getDatatable().addTitles("td"); - return podcastEpisodesTableWidget; + podcastEpisodeTableObj.getDatatable().addTitles("td"); + return podcastEpisodeTableObj; }; return AIRTIME; From 28ca9c0e0f1765095624b8a31349de9992bd3fab Mon Sep 17 00:00:00 2001 From: ryan Date: Mon, 7 Jan 2019 21:13:15 -0600 Subject: [PATCH 5/7] add ability to use multiple episode dataTables --- .../public/js/airtime/library/library.js | 14 +-- .../public/js/airtime/library/podcast.js | 89 ++++++++++--------- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 37f1e5253..39b7e806c 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -480,7 +480,7 @@ var AIRTIME = (function(AIRTIME) { oTable.fnStandingRedraw(); }; - mod.fnDeleteItems = function(aMedia) { + mod.fnDeleteItems = function(aMedia, podcastId) { //Prevent the user from spamming the delete button while the AJAX request is in progress AIRTIME.button.disableButton("btn-group #sb-delete", false); var openTabObjectIds = $(".obj_id"), @@ -505,11 +505,11 @@ var AIRTIME = (function(AIRTIME) { chosenItems = {}; - // TODO: correct check whether used to delete episodes - if (oTable == $datatables[mod.DataTableTypeEnum.PODCAST_EPISODES]) { - mod.podcastEpisodeTableWidget.reload(); - } else { + if (typeof(podcastId) === "undefined") { oTable.fnStandingRedraw(); + } else { + AIRTIME.podcast.episodeTables[podcastId].reload(this.podcast_id); + AIRTIME.podcast.episodeTables[podcastId].clearSelection(); } //Re-enable the delete button @@ -1331,8 +1331,8 @@ var AIRTIME = (function(AIRTIME) { return oTable; }; - mod.openPodcastEpisodeDialog = function () { - var episode = mod.podcastEpisodeTableWidget.getSelectedRows()[0]; + mod.openPodcastEpisodeDialog = function (podcastId) { + var episode = AIRTIME.podcast.episodeTables[podcastId].getSelectedRows()[0]; $("body").append("
"); var dialog = $("#podcast_episode_dialog").html(episode.description); dialog.html(dialog.text()); diff --git a/airtime_mvc/public/js/airtime/library/podcast.js b/airtime_mvc/public/js/airtime/library/podcast.js index 9c4965b24..c7ce2c548 100644 --- a/airtime_mvc/public/js/airtime/library/podcast.js +++ b/airtime_mvc/public/js/airtime/library/podcast.js @@ -6,6 +6,7 @@ var AIRTIME = (function (AIRTIME) { } mod = AIRTIME.podcast; + mod.episodeTables = {}; var endpoint = '/rest/podcast/', PodcastEpisodeTable; @@ -41,6 +42,7 @@ var AIRTIME = (function (AIRTIME) { successMsg.hide("fast"); }, 5000); AIRTIME.library.podcastDataTable.fnDraw(); + self.$scope.tab.setName(self.$scope.podcast.title); }; /** @@ -121,13 +123,14 @@ var AIRTIME = (function (AIRTIME) { self.$scope.tab.setName(self.$scope.podcast.title); // Add an onclose hook to the tab to remove the table object and the // import listener so we don't cause memory leaks. - if (self.episodeTable) { - self.$scope.tab.assignOnCloseHandler(function () { - self.episodeTable.destroy(); - self.episodeTable = null; + var podcastId = self.$scope.podcast.id.toString(); + self.$scope.tab.assignOnCloseHandler(function () { + if ( AIRTIME.podcast.episodeTables.hasOwnProperty(podcastId) ) { + AIRTIME.podcast.episodeTables[podcastId].destroy(); + AIRTIME.podcast.episodeTables[podcastId] = null; self.$scope.$destroy(); - }); - } + } + }); }; /** @@ -348,6 +351,7 @@ var AIRTIME = (function (AIRTIME) { // Add podcast episode table in right-side panel below podcast edit form var episodeTable = AIRTIME.podcast.initPodcastEpisodeDatatableWithButtonEvents( $("#podcast_episodes_" + podcast.id), + podcast.id.toString() ); episodeTable.reload(podcast.id); episodeTable.clearSelection() @@ -436,7 +440,7 @@ var AIRTIME = (function (AIRTIME) { var self = this; self.importListener = setInterval(function () { var podcastId = self.config.podcastId, pendingRows = []; - if (!podcastId) return false; + 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 () { @@ -579,7 +583,7 @@ var AIRTIME = (function (AIRTIME) { csrf_token: $("#csrf").val(), episode: this }), function () { - dt.reload(this.podcast_id); + dt.reload(dt.config.podcastId); }); remainingDiskSpace -= this.enclosure.length; @@ -592,20 +596,18 @@ var AIRTIME = (function (AIRTIME) { * Delete one or more podcast episodes. * * @param {id:string, type:string}[] data Array of data objects to be deleted - * @param {PodcastEpisodeTable} dt PodcastEpisode table containing the data + * @param podcastId:string */ - mod.deleteSelectedEpisodes = function (data, dt) { + mod.deleteSelectedEpisodes = function (data, podcastId) { $.each(data, function () { - AIRTIME.library.fnDeleteItems(data); + AIRTIME.library.fnDeleteItems(data, podcastId); }); - dt.reload(this.podcast_id); - dt.clearSelection(); }; /** * Initialize the podcast episode table with working buttons */ - mod.initPodcastEpisodeDatatableWithButtonEvents = function (domNode) { + mod.initPodcastEpisodeDatatableWithButtonEvents = function (domNode, podcastId) { /** * Check the import statuses of each selected episode to see which @@ -637,8 +639,8 @@ var AIRTIME = (function (AIRTIME) { title: "Import", eventHandlers: { click: function () { - var episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); - AIRTIME.podcast.importSelectedEpisodes(episodes, mod.podcastEpisodeTableWidget); + var episodes = mod.episodeTables[podcastId].getSelectedRows(); + AIRTIME.podcast.importSelectedEpisodes(episodes, mod.episodeTables[podcastId]); } }, validateConstraints: function () { @@ -649,7 +651,7 @@ var AIRTIME = (function (AIRTIME) { { eventHandlers: { click: function () { - var episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); + var episodes = mod.episodeTables[podcastId].getSelectedRows(); AIRTIME.podcast.editSelectedEpisodes(episodes); } }, @@ -661,12 +663,11 @@ var AIRTIME = (function (AIRTIME) { { eventHandlers: { click: function () { - var data = [], episodes = mod.podcastEpisodeTableWidget.getSelectedRows(); + var data = [], episodes = mod.episodeTables[podcastId].getSelectedRows(); $.each(episodes, function () { data.push({id: this.file.id, type: this.file.ftype}); }); - console.log("podcast deletion:", data, mod.podcastEpisodeTableWidget); - AIRTIME.podcast.deleteSelectedEpisodes(data, mod.podcastEpisodeTableWidget); + AIRTIME.podcast.deleteSelectedEpisodes(data, podcastId); } }, validateConstraints: function () { @@ -683,30 +684,32 @@ var AIRTIME = (function (AIRTIME) { }; $.extend(true, podcastEpisodeButtons, { - // addToScheduleBtn: { - // title : $.i18n._('Add to Schedule'), - // iconClass : '', - // extraBtnClass : 'btn-small', - // elementId : '', - // eventHandlers : { - // click: function () { - // var data = [], selected = mod.podcastEpisodeTableWidget.getSelectedRows(); - // $.each(selected, function () { data.push(this.file); }); - // mod.addToSchedule(data); - // } - // }, - // validateConstraints: function () { - // // TODO: change text + behaviour for playlists, smart blocks, etc. - // return checkSelectedEpisodeImportStatus.call(this, true); - // } - // }, + addToScheduleBtn: { + title : $.i18n._('Add to Schedule'), + iconClass : 'icon-plus', + extraBtnClass : 'btn-small', + elementId : '', + eventHandlers : { + click: function () { + var data = [], selected = AIRTIME.podcast.episodeTables[podcastId].getSelectedRows(); + $.each(selected, function () { data.push(this.file); }); + AIRTIME.library.addToSchedule(data); + } + }, + validateConstraints: function () { + // TODO: change text + behaviour for playlists, smart blocks, etc. + return checkSelectedEpisodeImportStatus.call(this, true); + } + }, viewDescBtn: { title : $.i18n._("View"), iconClass : "icon-globe", extraBtnClass : "btn-small", elementId : "", eventHandlers : { - click: mod.openPodcastEpisodeDialog + click: function () { + AIRTIME.library.openPodcastEpisodeDialog(podcastId); + } }, validateConstraints: function () { return this.getSelectedRows().length == 1; @@ -714,7 +717,7 @@ var AIRTIME = (function (AIRTIME) { } }); - mod.podcastEpisodeTableWidget = AIRTIME.podcast.initPodcastEpisodeDatatable( + mod.episodeTables[podcastId] = AIRTIME.podcast.initPodcastEpisodeDatatable( domNode, podcastEpisodeButtons, { @@ -728,19 +731,19 @@ var AIRTIME = (function (AIRTIME) { } ); - mod.podcastEpisodeDataTable = mod.podcastEpisodeTableWidget.getDatatable(); - mod.podcastEpisodeTableWidget.assignDblClickHandler(function () { + mod.podcastEpisodeDataTable = mod.episodeTables[podcastId].getDatatable(); + mod.episodeTables[podcastId].assignDblClickHandler(function () { var data = mod.podcastEpisodeDataTable.fnGetData(this); if (!$.isEmptyObject(data.file)) { mod.dblClickAdd(data.file, data.file.ftype); } else { if (data.ingested >= 0) { // Only import if the file isn't pending - AIRTIME.podcast.importSelectedEpisodes([data], mod.podcastEpisodeTableWidget); + AIRTIME.podcast.importSelectedEpisodes([data], mod.episodeTables[podcastId]); } } }); - return mod.podcastEpisodeTableWidget; + return mod.episodeTables[podcastId]; }; /** From 3f85c3813725dc0c3f717c02e72a3c2e7bca72f2 Mon Sep 17 00:00:00 2001 From: ryan Date: Tue, 8 Jan 2019 13:01:05 -0600 Subject: [PATCH 6/7] cleanup podcast episodes table orphaned code --- airtime_mvc/application/views/scripts/showbuilder/index.phtml | 1 - airtime_mvc/public/js/airtime/library/library.js | 1 - 2 files changed, 2 deletions(-) diff --git a/airtime_mvc/application/views/scripts/showbuilder/index.phtml b/airtime_mvc/application/views/scripts/showbuilder/index.phtml index 9a8a4316d..b5093d583 100644 --- a/airtime_mvc/application/views/scripts/showbuilder/index.phtml +++ b/airtime_mvc/application/views/scripts/showbuilder/index.phtml @@ -16,7 +16,6 @@
-
diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 39b7e806c..8921da1b2 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -92,7 +92,6 @@ var AIRTIME = (function(AIRTIME) { mod.DataTableTypeEnum = Object.freeze({ LIBRARY : "library", PODCAST : "podcast", - PODCAST_EPISODES: "podcastEpisodes" }); // TODO: once the new manual pages are added, change links! From 3ce27c6c4048d83b30d66aa3f6b869f192c72ec3 Mon Sep 17 00:00:00 2001 From: ryan Date: Sat, 12 Jan 2019 07:28:58 -0600 Subject: [PATCH 7/7] include markup into which to create episode tables --- airtime_mvc/application/views/scripts/podcast/podcast.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/podcast/podcast.phtml b/airtime_mvc/application/views/scripts/podcast/podcast.phtml index 2a2c8122e..9342636f2 100644 --- a/airtime_mvc/application/views/scripts/podcast/podcast.phtml +++ b/airtime_mvc/application/views/scripts/podcast/podcast.phtml @@ -41,7 +41,7 @@
- +