From d3f5b046f383fb87b96bcb4501207c3df9b4e5fa Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Fri, 25 Sep 2015 17:56:29 -0400 Subject: [PATCH 01/38] Colour palette improved for the calendar --- airtime_mvc/application/models/Show.php | 47 +++++++++++++++++++++---- airtime_mvc/public/css/fullcalendar.css | 6 ++-- airtime_mvc/public/css/styles.css | 2 +- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/airtime_mvc/application/models/Show.php b/airtime_mvc/application/models/Show.php index 832186d80..2653fe848 100644 --- a/airtime_mvc/application/models/Show.php +++ b/airtime_mvc/application/models/Show.php @@ -1044,17 +1044,31 @@ SQL; $event["nowPlaying"] = false; } - //event colouring - if ($show["color"] != "") { - $event["textColor"] = "#".$show["color"]; - } - if (!empty($show["background_color"])) { $event["color"] = "#" . $show["background_color"]; } else { $event["color"] = "#" . self::getDefaultBackgroundColor($startsDT);//DEFAULT_SHOW_COLOR; } + + //event colouring + if ($show["color"] != "") { + $event["textColor"] = "#".$show["color"]; + } else { + $bg = $event["color"]; + //Calculate the text colour (black or white) based on the brightness of the background. + $r = intval(substr($bg, 1, 2), 16); + $g = intval(substr($bg, 3, 2), 16); + $b = intval(substr($bg, 5, 2), 16); + $brightness = 0.299*floatval($r) + 0.587*floatval($g) + 0.114*floatval($b); + if ($brightness > 130) { + $event["textColor"] = "#000000"; + } else { + $event["textColor"] = "#fcfcfc"; + } + } + + foreach ($options as $key => $value) { $event[$key] = $value; } @@ -1067,12 +1081,33 @@ SQL; /** Get a palettized colour for the show. */ private static function getDefaultBackgroundColor($date) { + $basePalette = ['A22BE8', '2FFF8D', 'FF743C', '2ED4FF', 'E8D82B']; + // 'B23F11', 'FF7E4A', 'FF6C31' + + /* $palette = [['42d5a1', '56bd99', '65ab93', '7b938b'], ['42a4d5', '569bbd', '6594ab', '7b8b93'], ['4264d5', '566fbd', '6576ab', '7b8193']]; + */ + $palette = []; + for ($baseColorIdx = 0; $baseColorIdx < count($basePalette); $baseColorIdx++) { + $dayPalette = []; + for ($shade = 0.0; $shade < 0.8; $shade += 0.1) { + $origColour = $basePalette[$baseColorIdx]; + $r = intval(substr($origColour, 0, 2), 16); + $g = intval(substr($origColour, 2, 2), 16); + $b = intval(substr($origColour, 4, 2), 16); + $r = floatval($r) * (1.0 - $shade); + $g = floatval($g) * (1.0 - $shade); + $b = floatval($b) * (1.0 - $shade); + $color = sprintf("%02x%02x%02x", $r, $g, $b); + array_push($dayPalette, $color); + } + array_push($palette, $dayPalette); + } //$hashValue = (md5($date->format('d'))[0] % $cols) + ((intval($date->format('h'))/24) % $rows); - $row = intval($date->format('d')) % sizeof($palette); + $row = intval($date->format('w')) % sizeof($palette); $foo = $date->format('H'); $col = intval(intval($date->format('H'))/24.0 * sizeof($palette[0])); //$color = $palette[$hashValue % sizeof($palette)]; diff --git a/airtime_mvc/public/css/fullcalendar.css b/airtime_mvc/public/css/fullcalendar.css index 2ce2e3a52..e0c991eb5 100644 --- a/airtime_mvc/public/css/fullcalendar.css +++ b/airtime_mvc/public/css/fullcalendar.css @@ -605,9 +605,9 @@ table.fc-border-separate { left: 0; width: 100%; height: 100%; - background: #fff; - opacity: 0.3; - filter: alpha(opacity=30); + background: #C3C3C3; + opacity: 0.15; + filter: alpha(opacity=15); } .fc .ui-draggable-dragging .fc-event-bg, /* TODO: something nicer like .fc-opacity */ diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index 9f4294be8..a36363894 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -1371,7 +1371,7 @@ thead tr.fc-first height: 32px; line-height: 32px; } - +ful /** Extremely nasty workaround for a fullcalendar bug, where clicking "Add Show" would cause this large space under the table header. The CSS to get to that is ridiculously complicated and set in the HTML (!) dynamically with JS, From febc5fa99d45896e99446954bc329132e705da83 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Mon, 28 Sep 2015 10:17:40 -0400 Subject: [PATCH 02/38] SAAS-1089 - fix scrolling in metadata editor --- airtime_mvc/public/css/dashboard.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/airtime_mvc/public/css/dashboard.css b/airtime_mvc/public/css/dashboard.css index c78d2a639..cc10dbbe9 100644 --- a/airtime_mvc/public/css/dashboard.css +++ b/airtime_mvc/public/css/dashboard.css @@ -477,9 +477,6 @@ li.ui-state-default { bottom: 4px; top: 4px; left: 4px; -} - -[data-tab-type='md'] > .editor_pane_wrapper { overflow-x: hidden; /* Show the y-direction scrollbar (magic!) */ } From 375d83ab431d010041a0af27eb4fb1964cb9e061 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Mon, 28 Sep 2015 10:40:04 -0400 Subject: [PATCH 03/38] Update to podcast frontend --- .../application/models/airtime/Podcast.php | 9 +++- .../rest/controllers/PodcastController.php | 3 -- .../public/js/airtime/common/common.js | 11 ++++- .../public/js/airtime/library/podcast.js | 43 ++++++++++++++----- .../public/js/airtime/showbuilder/tabs.js | 8 ++-- .../public/js/airtime/widgets/table.js | 15 ++++--- 6 files changed, 62 insertions(+), 27 deletions(-) diff --git a/airtime_mvc/application/models/airtime/Podcast.php b/airtime_mvc/application/models/airtime/Podcast.php index f948bd353..facecc5f2 100644 --- a/airtime_mvc/application/models/airtime/Podcast.php +++ b/airtime_mvc/application/models/airtime/Podcast.php @@ -139,13 +139,20 @@ class Podcast extends BasePodcast * @return array */ private static function _generatePodcastArray($podcast, $rss) { - $podcastArray = $podcast->toArray(BasePeer::TYPE_FIELDNAME); + $ingestedEpisodes = PodcastEpisodesQuery::create() + ->findByDbPodcastId($podcast->getDbId()); + $episodeIds = array(); + foreach ($ingestedEpisodes as $e) { + array_push($episodeIds, $e->getDbEpisodeGuid()); + } + $podcastArray = $podcast->toArray(BasePeer::TYPE_FIELDNAME); $podcastArray["episodes"] = array(); foreach ($rss->get_items() as $item) { /** @var SimplePie_Item $item */ array_push($podcastArray["episodes"], array( "guid" => $item->get_id(), + "ingested" => in_array($item->get_id(), $episodeIds), "title" => $item->get_title(), "author" => $item->get_author()->get_name(), "description" => $item->get_description(), diff --git a/airtime_mvc/application/modules/rest/controllers/PodcastController.php b/airtime_mvc/application/modules/rest/controllers/PodcastController.php index 138da8c3c..e18b49de9 100644 --- a/airtime_mvc/application/modules/rest/controllers/PodcastController.php +++ b/airtime_mvc/application/modules/rest/controllers/PodcastController.php @@ -90,9 +90,6 @@ class Rest_PodcastController extends Zend_Rest_Controller $this->_helper->json->sendJson(array( "podcast"=>json_encode($podcast), "html"=>$this->view->render($path), - "type"=>"podcast", // TODO: get rid of these extraneous fields - "id"=>$podcast["id"] - // "id"=>$podcast->getDbId() )); // $this->getResponse() diff --git a/airtime_mvc/public/js/airtime/common/common.js b/airtime_mvc/public/js/airtime/common/common.js index 27f93ad61..76adaf00d 100644 --- a/airtime_mvc/public/js/airtime/common/common.js +++ b/airtime_mvc/public/js/airtime/common/common.js @@ -58,6 +58,15 @@ var i18n_days_short = [ $.i18n._("Sa") ]; +var HTTPMethods = Object.freeze({ + GET: "GET", + POST: "POST", + PUT: "PUT", + PATCH: "PATCH", + DELETE: "DELETE", + OPTIONS: "OPTIONS" +}); + var dateStartId = "#sb_date_start", timeStartId = "#sb_time_start", dateEndId = "#sb_date_end", @@ -69,7 +78,7 @@ function getDatatablesStrings(overrideDict) { "sEmptyTable": $.i18n._("No data available in table"), "sInfo": $.i18n._("Showing _START_ to _END_ of _TOTAL_ entries"), "sInfoEmpty": $.i18n._("Showing 0 to 0 of 0 entries"), - "sInfoFiltered": $.i18n._("(filtered from _MAX_ total entries)"), + "sInfoFiltered": "", // $.i18n._("(filtered from _MAX_ total entries)"), "sInfoPostFix": $.i18n._(""), "sInfoThousands": $.i18n._(","), "sLengthMenu": $.i18n._("Show _MENU_"), diff --git a/airtime_mvc/public/js/airtime/library/podcast.js b/airtime_mvc/public/js/airtime/library/podcast.js index fe1c0f246..75219b322 100644 --- a/airtime_mvc/public/js/airtime/library/podcast.js +++ b/airtime_mvc/public/js/airtime/library/podcast.js @@ -25,7 +25,7 @@ var AIRTIME = (function (AIRTIME) { podcastData.episodes = episodeTable.getSelectedRows(); $http.put(endpoint + $scope.podcast.id, { csrf_token: jQuery("#csrf").val(), podcast: podcastData }) .success(function() { - // TODO + // TODO refresh the table here somehow.. }); }; @@ -36,15 +36,20 @@ var AIRTIME = (function (AIRTIME) { }); function _bulkAction(method, callback) { - var selected = $("#podcast_table").find(".selected"), - ids = []; - var selectedData = AIRTIME.library.podcastTableWidget.getSelectedRows(); + var ids = [], selectedData = AIRTIME.library.podcastTableWidget.getSelectedRows(); selectedData.forEach(function(el) { - ids.push(el.id); + var uid = AIRTIME.library.MediaTypeStringEnum.PODCAST+"_"+el.id; + var t = AIRTIME.tabs.get(uid); + if (t && method == HTTPMethods.DELETE) { + t.close(); + } + if (!(t && method == HTTPMethods.GET)) ids.push(el.id); }); - // Bulk methods should use post because we're sending data in the request body - $.post(endpoint + "bulk", { csrf_token: $("#csrf").val(), method: method, ids: ids }, callback); + if (ids.length > 0) { + // Bulk methods should use post because we're sending data in the request body + $.post(endpoint + "bulk", {csrf_token: $("#csrf").val(), method: method, ids: ids}, callback); + } } function _bootstrapAngularApp(podcast, tab, table) { @@ -81,7 +86,7 @@ var AIRTIME = (function (AIRTIME) { }; mod.editSelectedPodcasts = function() { - _bulkAction("GET", function(json) { + _bulkAction(HTTPMethods.GET, function(json) { json.forEach(function(el) { var podcast = JSON.parse(el.podcast); var uid = AIRTIME.library.MediaTypeStringEnum.PODCAST+"_"+podcast.id, @@ -94,7 +99,7 @@ var AIRTIME = (function (AIRTIME) { mod.deleteSelectedPodcasts = function() { if (confirm($.i18n._("Are you sure you want to delete the selected podcasts from your library?"))) { - _bulkAction("DELETE", function () { + _bulkAction(HTTPMethods.DELETE, function () { AIRTIME.library.podcastDataTable.fnDraw(); }); } @@ -102,7 +107,7 @@ var AIRTIME = (function (AIRTIME) { mod.initPodcastEpisodeDatatable = function(episodes) { var aoColumns = [ - /* GUID */ { "sTitle" : "" , "mDataProp" : "guid" , "sClass" : "podcast_episodes_guid" , "bVisible" : false }, + /* GUID */ { "sTitle" : "" , "mDataProp" : "guid" , "sClass" : "podcast_episodes_guid" , "bVisible" : false }, /* 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" }, @@ -110,10 +115,26 @@ var AIRTIME = (function (AIRTIME) { /* Publication Date */ { "sTitle" : $.i18n._("Publication Date") , "mDataProp" : "pub_date" , "sClass" : "podcast_episodes_pub_date" , "sWidth" : "170px" } ]; + var PodcastTable = function(wrapperDOMNode, bItemSelection, toolbarButtons, dataTablesOptions) { + // Just call the superconstructor. For clarity/extensibility + 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({ + 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 + return AIRTIME.widgets.Table.prototype._datatablesCheckboxDataDelegate.call(this, rowData, callType, dataToSave); + }; + + // This method is static, so use AIRTIME.widgets.Table var podcastToolbarButtons = AIRTIME.widgets.Table.getStandardToolbarButtons(); // Set up the div with id "podcast_table" as a datatable. - var podcastEpisodesTableWidget = new AIRTIME.widgets.Table( + var podcastEpisodesTableWidget = new PodcastTable( AIRTIME.tabs.getActiveTab().contents.find('#podcast_episodes'), // DOM node to create the table inside. true, // Enable item selection podcastToolbarButtons, // Toolbar buttons diff --git a/airtime_mvc/public/js/airtime/showbuilder/tabs.js b/airtime_mvc/public/js/airtime/showbuilder/tabs.js index f04680ffb..c6d1e284f 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/tabs.js +++ b/airtime_mvc/public/js/airtime/showbuilder/tabs.js @@ -316,12 +316,12 @@ var AIRTIME = (function(AIRTIME){ /** * Given a tab id, get the corresponding Tab object * - * @param {int} id the tab id of the Tab to retrieve - * @returns {Tab|undefined} the Tab object with the given id, or undefined - * if no Tab with the given id exists + * @param {int|string} id the tab or object ID of the Tab to retrieve + * @returns {Tab|undefined} the Tab object with the given ID, or undefined + * if no Tab with the given ID exists */ mod.get = function(id) { - return $openTabs[$tabMap[id]]; + return $.isNumeric(id) ? $openTabs[$tabMap[id]] : $openTabs[id]; }; /** diff --git a/airtime_mvc/public/js/airtime/widgets/table.js b/airtime_mvc/public/js/airtime/widgets/table.js index 2c14394ce..012ca853a 100644 --- a/airtime_mvc/public/js/airtime/widgets/table.js +++ b/airtime_mvc/public/js/airtime/widgets/table.js @@ -23,11 +23,6 @@ var AIRTIME = (function(AIRTIME) { MULTI_CTRL : 2 }; - // Internal enum for repeated jQuery selectors - self._SELECTORS = Object.freeze({ - SELECTION_CHECKBOX: ".airtime_table_checkbox" - }); - //Member variables self._datatable = null; self._selectedRows = []; //An array containing the underlying objects for each selected row. (Easy to use!) @@ -109,8 +104,8 @@ var AIRTIME = (function(AIRTIME) { * than having a per-row callback...) */ if (bItemSelection) { - $(self._datatable, 'tbody tr').on('click contextmenu', 'tr', function (e) { - var aData = self._datatable.fnGetData($(this).index()); // $(this).data(); //Neat trick - thanks DataTables! + $(self._datatable, 'tbody tr').on('click contextmenu', self._SELECTORS.SELECTION_TABLE_ROW, function (e) { + var aData = self._datatable.fnGetData($(this).index()); var iDisplayIndex = $(this).index(); //The index of the row in the current page in the table. var nRow = this; @@ -427,6 +422,12 @@ 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" + }); + + Table.TOOLBAR_BUTTON_ROLES = { NEW : 0, EDIT : 1, From c1a16d7973dd1aace829fe0ca69febc0c63f6b2a Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Mon, 28 Sep 2015 14:14:01 -0400 Subject: [PATCH 04/38] Add filter string to generic Table object; add call to refetch podcast episode table data --- .../views/scripts/showbuilder/index.phtml | 2 +- airtime_mvc/public/css/dashboard.css | 10 +++- .../public/js/airtime/library/library.js | 9 ++-- .../public/js/airtime/library/podcast.js | 26 ++++++++--- .../public/js/airtime/widgets/table.js | 46 +++++++++---------- 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/airtime_mvc/application/views/scripts/showbuilder/index.phtml b/airtime_mvc/application/views/scripts/showbuilder/index.phtml index d36794257..7f5cfdcd3 100644 --- a/airtime_mvc/application/views/scripts/showbuilder/index.phtml +++ b/airtime_mvc/application/views/scripts/showbuilder/index.phtml @@ -7,7 +7,7 @@
diff --git a/airtime_mvc/public/css/dashboard.css b/airtime_mvc/public/css/dashboard.css index cc10dbbe9..544e3510c 100644 --- a/airtime_mvc/public/css/dashboard.css +++ b/airtime_mvc/public/css/dashboard.css @@ -252,11 +252,19 @@ thead th.ui-state-default:focus { margin-top: 4px; } -#filter_message { +#lib-filter-message, .filter-message { border-top: none !important; text-shadow: none; } +.filter-message { + position: relative; + font-size: 13px; + float: right; + line-height: 26px; + padding: 0 6px; +} + #advanced-options { float: right; z-index: 1004; diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 9ebc6159e..fbb438d19 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -659,18 +659,19 @@ var AIRTIME = (function(AIRTIME) { "success": fnCallback, "error": handleAjaxError }).done(function (data) { + var filterMessage = $libContent.find('.filter-message'); if (data.iTotalRecords > data.iTotalDisplayRecords) { - $('#filter_message').text( + filterMessage.text( $.i18n._("Filtering out ") + (data.iTotalRecords - data.iTotalDisplayRecords) + $.i18n._(" of ") + data.iTotalRecords + $.i18n._(" records") ); $('#library_empty').hide(); - $('#library_display').find('tr:has(td.dataTables_empty)').show(); + $libTable.find('tr:has(td.dataTables_empty)').show(); } else { - $('#filter_message').text(""); + filterMessage.text(""); } - $('#library_content').find('.dataTables_filter input[type="text"]') + $libContent.find('.dataTables_filter input[type="text"]') .css('padding-right', $('#advanced-options').find('button').outerWidth()); }); }, diff --git a/airtime_mvc/public/js/airtime/library/podcast.js b/airtime_mvc/public/js/airtime/library/podcast.js index 75219b322..211e3996a 100644 --- a/airtime_mvc/public/js/airtime/library/podcast.js +++ b/airtime_mvc/public/js/airtime/library/podcast.js @@ -26,6 +26,7 @@ var AIRTIME = (function (AIRTIME) { $http.put(endpoint + $scope.podcast.id, { csrf_token: jQuery("#csrf").val(), podcast: podcastData }) .success(function() { // TODO refresh the table here somehow.. + episodeTable.reload($scope.podcast.id); }); }; @@ -87,10 +88,10 @@ var AIRTIME = (function (AIRTIME) { mod.editSelectedPodcasts = function() { _bulkAction(HTTPMethods.GET, function(json) { - json.forEach(function(el) { - var podcast = JSON.parse(el.podcast); + json.forEach(function(data) { + var podcast = JSON.parse(data.podcast); var uid = AIRTIME.library.MediaTypeStringEnum.PODCAST+"_"+podcast.id, - tab = AIRTIME.tabs.openTab(el, uid, null); + tab = AIRTIME.tabs.openTab(data, uid, null); var table = mod.initPodcastEpisodeDatatable(podcast.episodes); _bootstrapAngularApp(podcast, tab, table); }); @@ -129,6 +130,15 @@ var AIRTIME = (function (AIRTIME) { if (rowData.ingested) return null; // Don't create checkboxes for ingested items return AIRTIME.widgets.Table.prototype._datatablesCheckboxDataDelegate.call(this, rowData, callType, dataToSave); }; + // Since we're 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) { + var dt = this._datatable; + $.get(endpoint + id, function(json) { + dt.fnClearTable(); + dt.fnAddData(JSON.parse(json).episodes); + }); + }; // This method is static, so use AIRTIME.widgets.Table var podcastToolbarButtons = AIRTIME.widgets.Table.getStandardToolbarButtons(); @@ -139,10 +149,14 @@ var AIRTIME = (function (AIRTIME) { true, // Enable item selection podcastToolbarButtons, // Toolbar buttons { // Datatables overrides. - 'aoColumns' : aoColumns, - 'bServerSide': false, + 'aoColumns' : aoColumns, + 'bServerSide' : false, + // We want to make as few round trips as possible, so we get + // the episode data alongside the Podcast data and pass it in + // as json. Doing this caches all the episode data on the front-end, + // which means we also don't need to go back to the server for pagination 'sAjaxSource' : null, - 'aaData' : episodes, + 'aaData' : episodes, "oColVis": { "sAlign": "right", "aiExclude": [0, 1], diff --git a/airtime_mvc/public/js/airtime/widgets/table.js b/airtime_mvc/public/js/airtime/widgets/table.js index 012ca853a..165b1b2b7 100644 --- a/airtime_mvc/public/js/airtime/widgets/table.js +++ b/airtime_mvc/public/js/airtime/widgets/table.js @@ -141,10 +141,32 @@ var AIRTIME = (function(AIRTIME) { }); } + // On filter, display the number of total and filtered results in the search bar + $(self._datatable).on('filter', function() { + var dt = self._datatable, f = dt.closest(".dataTables_wrapper").find(".filter-message"), + totalRecords = dt.fnSettings().fnRecordsTotal(), + totalDisplayRecords = dt.fnSettings().fnRecordsDisplay(); + + if (f.length === 0) { + var el = document.createElement("span"); + el.setAttribute("class", "filter-message"); + f = dt.closest(".dataTables_wrapper").find(".dataTables_filter").append(el).find(".filter-message"); + } + + f.text(totalRecords > totalDisplayRecords ? + $.i18n._("Filtering out ") + (totalRecords - totalDisplayRecords) + + $.i18n._(" of ") + totalRecords + + $.i18n._(" records") : "" + ); + + dt.closest(".dataTables_wrapper").find('.dataTables_filter input[type="text"]') + .css('padding-right', f.outerWidth()); + }); + $(self._datatable).on('init', function(e) { self._setupToolbarButtons(self._toolbarButtons); }); - } + }; /** @@ -252,12 +274,6 @@ var AIRTIME = (function(AIRTIME) { var foundAtIdx = $.inArray(aData, self._selectedRows); console.log('checkbox mouse', iVisualRowIdx, foundAtIdx); - //XXX: Debugging -- Bug here-ish - if (foundAtIdx >= 0) { - console.log(aData, self._selectedRows[foundAtIdx]); - } else { - console.log("clicked row not detected as already selected"); - } //If the clicked row is already selected, deselect it. if (foundAtIdx >= 0 && self._selectedRows.length >= 1) { @@ -370,22 +386,6 @@ var AIRTIME = (function(AIRTIME) { fnCallback(json); }, "error": self._handleAjaxError - }).done(function (data) { - /* - if (data.iTotalRecords > data.iTotalDisplayRecords) { - $('#filter_message').text( - $.i18n._("Filtering out ") + (data.iTotalRecords - data.iTotalDisplayRecords) - + $.i18n._(" of ") + data.iTotalRecords - + $.i18n._(" records") - ); - $('#library_empty').hide(); - $('#library_display').find('tr:has(td.dataTables_empty)').show(); - } else { - $('#filter_message').text(""); - } - $('#library_content').find('.dataTables_filter input[type="text"]') - .css('padding-right', $('#advanced-options').find('button').outerWidth()); - */ }); }; From d09941dffac88b5347c13ba9d8dd4d0e00b9396d Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Mon, 28 Sep 2015 14:15:08 -0400 Subject: [PATCH 05/38] Set TaskManager run interval back to 30 seconds --- airtime_mvc/application/common/TaskManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/common/TaskManager.php b/airtime_mvc/application/common/TaskManager.php index 61e559e04..2792116d4 100644 --- a/airtime_mvc/application/common/TaskManager.php +++ b/airtime_mvc/application/common/TaskManager.php @@ -22,7 +22,7 @@ final class TaskManager { /** * @var int TASK_INTERVAL_SECONDS how often, in seconds, to run the TaskManager tasks */ - const TASK_INTERVAL_SECONDS = 300; // 5 minutes - will be run on every pypo request + const TASK_INTERVAL_SECONDS = 30; /** * @var $con PDO Propel connection object From 67c20630a30d9d77067f32c856bc976924d745f1 Mon Sep 17 00:00:00 2001 From: Duncan Sommerville Date: Mon, 28 Sep 2015 17:19:02 -0400 Subject: [PATCH 06/38] UI revamp what's new dialog --- airtime_mvc/application/configs/constants.php | 2 + .../application/configs/navigation.php | 5 ++ .../controllers/LocaleController.php | 3 +- .../application/layouts/scripts/layout.phtml | 48 +++++++++++++++++++ airtime_mvc/application/models/Preference.php | 11 +++++ airtime_mvc/build/sql/defaultdata.sql | 2 + airtime_mvc/public/css/styles.css | 31 ++++++++++++ 7 files changed, 101 insertions(+), 1 deletion(-) diff --git a/airtime_mvc/application/configs/constants.php b/airtime_mvc/application/configs/constants.php index c1b1edca1..dd421a9d9 100644 --- a/airtime_mvc/application/configs/constants.php +++ b/airtime_mvc/application/configs/constants.php @@ -21,6 +21,8 @@ define('ABOUT_AIRTIME_URL' , 'https://www.airtime.pro/support/'); define('AIRTIME_TRANSIFEX_URL' , 'https://www.transifex.com/projects/p/airtime/'); define('WHMCS_PASSWORD_RESET_URL' , 'https://account.sourcefabric.com/pwreset.php'); define('SUPPORT_TICKET_URL' , 'https://sourcefabricberlin.zendesk.com/hc/en-us/requests/new'); +define('UI_REVAMP_EMBED_URL' , 'https://www.youtube.com/embed/nqpNnCKGluY'); +define('UI_REVAMP_YOUTUBE_URL' , 'https://www.youtube.com/watch?v=nqpNnCKGluY&feature=youtu.be'); define('LICENSE_VERSION' , 'GNU AGPL v.3'); define('LICENSE_URL' , 'http://www.gnu.org/licenses/agpl-3.0-standalone.html'); diff --git a/airtime_mvc/application/configs/navigation.php b/airtime_mvc/application/configs/navigation.php index 40bb48484..f5076a691 100644 --- a/airtime_mvc/application/configs/navigation.php +++ b/airtime_mvc/application/configs/navigation.php @@ -172,6 +172,11 @@ $pages = array( 'label' => _(sprintf("Help Translate %s", PRODUCT_NAME)), 'uri' => AIRTIME_TRANSIFEX_URL, 'target' => "_blank" + ), + array( + 'label' => _('Get to know the new Airtime'), + 'uri' => UI_REVAMP_YOUTUBE_URL, + 'target' => "_blank" ) ) ), diff --git a/airtime_mvc/application/controllers/LocaleController.php b/airtime_mvc/application/controllers/LocaleController.php index 81cebc183..6dbcf786b 100644 --- a/airtime_mvc/application/controllers/LocaleController.php +++ b/airtime_mvc/application/controllers/LocaleController.php @@ -422,8 +422,9 @@ class LocaleController extends Zend_Controller_Action "Next", "Previous", ": activate to sort column ascending", - ": activate to sort column descending" + ": activate to sort column descending", //End of datatables + "Welcome to the new Airtime Pro!" => _("Welcome to the new Airtime Pro!") ); $this->view->layout()->disableLayout(); $this->_helper->viewRenderer->setNoRender(true); diff --git a/airtime_mvc/application/layouts/scripts/layout.phtml b/airtime_mvc/application/layouts/scripts/layout.phtml index e2e486415..6558b2bd0 100644 --- a/airtime_mvc/application/layouts/scripts/layout.phtml +++ b/airtime_mvc/application/layouts/scripts/layout.phtml @@ -169,6 +169,54 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
+ + +
+
+ +

+

+
  • +
  • +
  • +
  • + +
    + +
    + + + diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index d02868c75..e4cdd73fb 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -1478,4 +1478,15 @@ class Application_Model_Preference { self::setValue("lang_tz_setup_complete", $value); } + + public static function getWhatsNewDialogViewed() + { + $val = self::getValue("whats_new_dialog_viewed", true); + return empty($val) ? false : $val; + } + + public static function setWhatsNewDialogViewed($value) + { + self::setValue("whats_new_dialog_viewed", $value, true); + } } diff --git a/airtime_mvc/build/sql/defaultdata.sql b/airtime_mvc/build/sql/defaultdata.sql index 4c2ca0af7..85736f336 100644 --- a/airtime_mvc/build/sql/defaultdata.sql +++ b/airtime_mvc/build/sql/defaultdata.sql @@ -19,6 +19,8 @@ INSERT INTO cc_pref("keystr", "valstr") VALUES('stream_bitrate', '24, 32, 48, 64 INSERT INTO cc_pref("keystr", "valstr") VALUES('num_of_streams', '3'); INSERT INTO cc_pref("keystr", "valstr") VALUES('max_bitrate', '320'); INSERT INTO cc_pref("keystr", "valstr") VALUES('plan_level', 'disabled'); +-- For now, just needs to be truthy - to be updated later; we should find a better way to implement this... +INSERT INTO cc_pref("keystr", "valstr") VALUES('whats_new_dialog_viewed', 1); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('output_sound_device', 'false', 'boolean'); INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('output_sound_device_type', 'ALSA', 'string'); diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css index a36363894..15b2587eb 100644 --- a/airtime_mvc/public/css/styles.css +++ b/airtime_mvc/public/css/styles.css @@ -3914,5 +3914,36 @@ li .ui-state-hover { float: left; } +/* UI Revamp Video */ +#whatsnew { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + max-width: 600px; + max-height: 460px; + padding: 10px 0; +} + +#whatsnew > button { + margin-top: 10px; + align-self: flex-end; + flex: 1 0 auto; +} + +#whatsnew > div { + text-align: center; + font-size: 16px; + overflow-x: hidden; +} + +#whatsnew h2 { + margin-top: 10px; + font-weight: bold; +} + +#whatsnew li { + margin-top: 20px; +} \ No newline at end of file From 6658fca10b7c4574ffbb093db9371bc19be3507e Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Mon, 28 Sep 2015 22:17:34 -0400 Subject: [PATCH 07/38] Some polish for the What's New popup --- .../application/layouts/scripts/layout.phtml | 8 ++++---- airtime_mvc/public/css/styles.css | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/airtime_mvc/application/layouts/scripts/layout.phtml b/airtime_mvc/application/layouts/scripts/layout.phtml index 6558b2bd0..17d9da5c0 100644 --- a/airtime_mvc/application/layouts/scripts/layout.phtml +++ b/airtime_mvc/application/layouts/scripts/layout.phtml @@ -176,11 +176,11 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= if (!Application_Model_Preference::getWhatsNewDialogViewed()) { ?>
    -
    +

    -

    • @@ -191,7 +191,7 @@ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= experience, no matter how you're connected."); ?>
    - +