From 753c05ce5b2bfce484ee1e098813f0da698bf335 Mon Sep 17 00:00:00 2001 From: Naomi Aro Date: Thu, 23 Feb 2012 17:46:07 +0100 Subject: [PATCH] CC-3335 : Timeline: Drag and drop usability improvements cursor must be used to add tracks to timeline, not check boxes. --- .../controllers/ShowbuilderController.php | 2 +- .../application/models/ShowBuilder.php | 33 ++- airtime_mvc/application/models/User.php | 5 +- airtime_mvc/public/css/showbuilder.css | 5 +- .../library/events/library_showbuilder.js | 30 ++- .../public/js/airtime/showbuilder/builder.js | 202 +++++++++++------- 6 files changed, 163 insertions(+), 114 deletions(-) diff --git a/airtime_mvc/application/controllers/ShowbuilderController.php b/airtime_mvc/application/controllers/ShowbuilderController.php index 39511bbe9..7232eefbd 100644 --- a/airtime_mvc/application/controllers/ShowbuilderController.php +++ b/airtime_mvc/application/controllers/ShowbuilderController.php @@ -107,7 +107,7 @@ class ShowbuilderController extends Zend_Controller_Action public function scheduleRemoveAction() { $request = $this->getRequest(); - $items = $request->getParam("items", null); + $items = $request->getParam("items", array()); try { $scheduler = new Application_Model_Scheduler(); diff --git a/airtime_mvc/application/models/ShowBuilder.php b/airtime_mvc/application/models/ShowBuilder.php index 01fe8561d..327c18d1b 100644 --- a/airtime_mvc/application/models/ShowBuilder.php +++ b/airtime_mvc/application/models/ShowBuilder.php @@ -9,12 +9,13 @@ class Application_Model_ShowBuilder { private $opts; private $contentDT; + private $epoch_now; private $defaultRowArray = array( "header" => false, "footer" => false, "empty" => false, - "checkbox" => false, + "allowed" => false, "id" => 0, "instance" => "", "starts" => "", @@ -37,6 +38,7 @@ class Application_Model_ShowBuilder { $this->timezone = date_default_timezone_get(); $this->user = Application_Model_User::GetCurrentUser(); $this->opts = $p_opts; + $this->epoch_now = time(); } /* @@ -89,6 +91,7 @@ class Application_Model_ShowBuilder { private function makeFooterRow($p_item) { $row = $this->defaultRowArray; + $this->isAllowed($p_item, $row); $row["footer"] = true; $showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC")); @@ -101,6 +104,16 @@ class Application_Model_ShowBuilder { return $row; } + private function isAllowed($p_item, &$row) { + + $showStartDT = new DateTime($p_item["si_starts"], new DateTimeZone("UTC")); + + //can only schedule the show if it hasn't started and you are allowed. + if ($this->epoch_now < $showStartDT->format('U') && $this->user->canSchedule($p_item["show_id"]) == true) { + $row["allowed"] = true; + } + } + private function getRowTimestamp($p_item, &$row) { if (is_null($p_item["si_last_scheduled"])) { @@ -116,6 +129,8 @@ class Application_Model_ShowBuilder { private function makeHeaderRow($p_item) { $row = $this->defaultRowArray; + $this->isAllowed($p_item, $row); + Logging::log("making header for show id ".$p_item["show_id"]); $this->getRowTimestamp($p_item, $row); $showStartDT = new DateTime($p_item["si_starts"], new DateTimeZone("UTC")); @@ -137,16 +152,10 @@ class Application_Model_ShowBuilder { private function makeScheduledItemRow($p_item) { $row = $this->defaultRowArray; - $epoch_now = time(); - $showStartDT = new DateTime($p_item["si_starts"], new DateTimeZone("UTC")); + $this->isAllowed($p_item, $row); $this->getRowTimestamp($p_item, $row); - //can only schedule the show if it hasn't started and you are allowed. - if ($epoch_now < $showStartDT->format('U') && $this->user->canSchedule($p_item["show_id"]) == true) { - $row["checkbox"] = true; - } - if (isset($p_item["sched_starts"])) { $schedStartDT = new DateTime($p_item["sched_starts"], new DateTimeZone("UTC")); @@ -172,6 +181,8 @@ class Application_Model_ShowBuilder { $row["empty"] = true; $row["id"] = 0 ; $row["instance"] = intval($p_item["si_id"]); + + //return null; } return $row; @@ -219,7 +230,11 @@ class Application_Model_ShowBuilder { } //make a normal data row. - $display_items[] = $this->makeScheduledItemRow($item); + $row = $this->makeScheduledItemRow($item); + //don't display the empty rows. + if (isset($row)) { + $display_items[] = $row; + } } //make the last footer if there were any scheduled items. diff --git a/airtime_mvc/application/models/User.php b/airtime_mvc/application/models/User.php index f06a3c918..755a0fbe1 100644 --- a/airtime_mvc/application/models/User.php +++ b/airtime_mvc/application/models/User.php @@ -37,15 +37,16 @@ class Application_Model_User { public function canSchedule($p_showId) { $type = $this->getType(); + $result = false; if ( $type === UTYPE_ADMIN || $type === UTYPE_PROGRAM_MANAGER || CcShowHostsQuery::create()->filterByDbShow($p_showId)->filterByDbHost($this->getId())->count() > 0 ) { - return true; + $result = true; } - return false; + return $result; } public function isUserType($type, $showId=''){ diff --git a/airtime_mvc/public/css/showbuilder.css b/airtime_mvc/public/css/showbuilder.css index 4d6fe4979..fd2cc1e14 100644 --- a/airtime_mvc/public/css/showbuilder.css +++ b/airtime_mvc/public/css/showbuilder.css @@ -4,8 +4,7 @@ width:100px; } -table tr.selected-row td, table tr.selected-row th, -table.datatable tr.selected-row td, table.datatable tr.selected-row th { +table.datatable tr.cursor-selected-row td, table.datatable tr.cursor-selected-row th { border-bottom: 2px solid #db0000 !important; } .innerWrapper { @@ -21,6 +20,6 @@ table.datatable tr.selected-row td, table.datatable tr.selected-row th { background:url(images/tl-arrow.png) no-repeat 0 0; display:block; } -tr.selected-row .marker { +tr.cursor-selected-row .marker { background-position: 0 -15px; } \ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js index b4588dc34..e73296237 100644 --- a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js +++ b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js @@ -41,33 +41,31 @@ var AIRTIME = (function(AIRTIME){ fnAddSelectedItems = function() { var oLibTT = TableTools.fnGetInstance('library_display'), - oSchedTT = TableTools.fnGetInstance('show_builder_table'), aData = oLibTT.fnGetSelectedData(), - item, + i, + length, temp, aMediaIds = [], aSchedIds = []; //process selected files/playlists. - for (item in aData) { - temp = aData[item]; - if (temp !== null && temp.hasOwnProperty('id')) { - aMediaIds.push({"id": temp.id, "type": temp.ftype}); - } + for (i=0, length = aData.length; i < length; i++) { + temp = aData[i]; + aMediaIds.push({"id": temp.id, "type": temp.ftype}); } + + aData = []; + $("#show_builder_table tr.cursor-selected-row").each(function(i, el){ + aData.push($(el).data("aData")); + }); - aData = oSchedTT.fnGetSelectedData(); - //process selected schedule rows to add media after. - for (item in aData) { - temp = aData[item]; - if (temp !== null && temp.hasOwnProperty('id')) { - aSchedIds.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp}); - } + for (i=0, length = aData.length; i < length; i++) { + temp = aData[i]; + aSchedIds.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp}); } - AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds); - + AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds); }; //[0] = button text //[1] = id diff --git a/airtime_mvc/public/js/airtime/showbuilder/builder.js b/airtime_mvc/public/js/airtime/showbuilder/builder.js index 6cecba411..7bbbb17d4 100644 --- a/airtime_mvc/public/js/airtime/showbuilder/builder.js +++ b/airtime_mvc/public/js/airtime/showbuilder/builder.js @@ -62,8 +62,7 @@ $(document).ready(function() { fnAddSelectedItems, fnRemoveSelectedItems, oRange, - fnServerData, - fnShowBuilderRowCallback; + fnServerData; oBaseDatePickerSettings = { dateFormat: 'yy-mm-dd', @@ -178,87 +177,17 @@ $(document).ready(function() { fnServerData.start = oRange.start; fnServerData.end = oRange.end; - fnShowBuilderRowCallback = function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ){ - var i, - sSeparatorHTML, - fnPrepareSeparatorRow, - node, - cl=""; - - //save some info for reordering purposes. - $(nRow).data({"aData": aData}); - - fnPrepareSeparatorRow = function(sRowContent, sClass, iNodeIndex) { - - node = nRow.children[iNodeIndex]; - node.innerHTML = sRowContent; - node.setAttribute('colspan',100); - for (i = iNodeIndex + 1; i < nRow.children.length; i = i+1) { - node = nRow.children[i]; - node.innerHTML = ""; - node.setAttribute("style", "display : none"); - } - - nRow.className = sClass; - }; - - if (aData.header === true) { - cl = 'sb-header'; - - sSeparatorHTML = ''+aData.title+''+aData.starts+''+aData.ends+''; - fnPrepareSeparatorRow(sSeparatorHTML, cl, 0); - } - else if (aData.footer === true) { - node = nRow.children[0]; - cl = 'sb-footer'; - - //check the show's content status. - if (aData.runtime > 0) { - node.innerHTML = ''; - cl = cl + ' ui-state-highlight'; - } - else { - node.innerHTML = ''; - cl = cl + ' ui-state-error'; - } - - sSeparatorHTML = ''+aData.fRuntime+''; - fnPrepareSeparatorRow(sSeparatorHTML, cl, 1); - } - else { - - node = nRow.children[0]; - if (aData.checkbox === true) { - var height = $(node).height(); - node.innerHTML = '
'; - } - else { - node.innerHTML = ''; - cl = cl + " sb-not-allowed"; - } - - if (aData.empty === true) { - - sSeparatorHTML = 'Show Empty'; - cl = cl + " sb-empty odd"; - - fnPrepareSeparatorRow(sSeparatorHTML, cl, 1); - } - } - }; - fnRemoveSelectedItems = function() { var oTT = TableTools.fnGetInstance('show_builder_table'), aData = oTT.fnGetSelectedData(), - item, + i, + length, temp, aItems = []; - for (item in aData) { - temp = aData[item]; - if (temp !== null && temp.hasOwnProperty('id')) { - aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp}); - } + for (i=0, length = aData.length; i < length; i++) { + temp = aData[i]; + aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp}); } AIRTIME.showbuilder.fnRemove(aItems); @@ -266,7 +195,7 @@ $(document).ready(function() { oTable = tableDiv.dataTable( { "aoColumns": [ - /* checkbox */ {"mDataProp": "checkbox", "sTitle": "", "sWidth": "15px"}, + /* checkbox */ {"mDataProp": "allowed", "sTitle": "", "sWidth": "15px"}, /* starts */{"mDataProp": "starts", "sTitle": "Start"}, /* ends */{"mDataProp": "ends", "sTitle": "End"}, /* runtime */{"mDataProp": "runtime", "sTitle": "Duration"}, @@ -341,7 +270,100 @@ $(document).ready(function() { }, "fnServerData": fnServerData, - "fnRowCallback": fnShowBuilderRowCallback, + "fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) { + var i, + sSeparatorHTML, + fnPrepareSeparatorRow, + node, + cl=""; + + //save some info for reordering purposes. + $(nRow).data({"aData": aData}); + + if (aData.allowed !== true) { + $(nRow).addClass("sb-not-allowed"); + } + + fnPrepareSeparatorRow = function(sRowContent, sClass, iNodeIndex) { + + node = nRow.children[iNodeIndex]; + node.innerHTML = sRowContent; + node.setAttribute('colspan',100); + for (i = iNodeIndex + 1; i < nRow.children.length; i = i+1) { + node = nRow.children[i]; + node.innerHTML = ""; + node.setAttribute("style", "display : none"); + } + + $(nRow).addClass(sClass); + }; + + if (aData.header === true) { + cl = 'sb-header'; + + sSeparatorHTML = ''+aData.title+''+aData.starts+''+aData.ends+''; + fnPrepareSeparatorRow(sSeparatorHTML, cl, 0); + } + else if (aData.footer === true) { + node = nRow.children[0]; + cl = 'sb-footer'; + + //check the show's content status. + if (aData.runtime > 0) { + node.innerHTML = ''; + cl = cl + ' ui-state-highlight'; + } + else { + node.innerHTML = ''; + cl = cl + ' ui-state-error'; + } + + sSeparatorHTML = ''+aData.fRuntime+''; + fnPrepareSeparatorRow(sSeparatorHTML, cl, 1); + } + else if (aData.empty === true) { + + sSeparatorHTML = 'Show Empty'; + cl = cl + " sb-empty odd"; + + fnPrepareSeparatorRow(sSeparatorHTML, cl, 0); + } + else { + + node = nRow.children[0]; + if (aData.allowed === true) { + node.innerHTML = ''; + } + else { + node.innerHTML = ''; + } + } + }, + "fnDrawCallback": function(oSettings, json) { + var wrapperDiv, + markerDiv, + td; + + //create cursor arrows. + tableDiv.find("tr:not(:first, .sb-footer, .sb-empty, .sb-not-allowed)").each(function(i, el) { + td = $(el).find("td:first"); + if (td.hasClass("dataTables_empty")) { + return false; + } + + wrapperDiv = $("
", { + "class": "innerWrapper", + "css": { + "height": td.height() + } + }); + markerDiv = $("
", { + "class": "marker" + }); + + td.append(markerDiv).wrapInner(wrapperDiv); + }); + }, "fnHeaderCallback": function(nHead) { $(nHead).find("input[type=checkbox]").attr("checked", false); }, @@ -366,8 +388,8 @@ $(document).ready(function() { var node = e.currentTarget; //don't select separating rows, or shows without privileges. if ($(node).hasClass("sb-header") - || $(node).hasClass("sb-footer") - || $(node).hasClass("sb-not-allowed")) { + || $(node).hasClass("sb-footer") + || $(node).hasClass("sb-not-allowed")) { return false; } return true; @@ -408,7 +430,7 @@ $(document).ready(function() { if ($(this).is(":checked")) { var allowedNodes; - allowedNodes = oTable.find('tr:not(:first):not(.sb-header):not(.sb-footer):not(.sb-not-allowed)'); + allowedNodes = oTable.find('tr:not(:first, .sb-header, .sb-footer, .sb-not-allowed)'); allowedNodes.each(function(i, el){ oTT.fnSelect(el); @@ -515,7 +537,7 @@ $(document).ready(function() { return { placeholder: "placeholder show-builder-placeholder ui-state-highlight", forcePlaceholderSize: true, - items: 'tr:not(:first):not(.sb-header):not(.sb-footer):not(.sb-not-allowed)', + items: 'tr:not(:first, :last, .sb-header, .sb-footer, .sb-not-allowed)', receive: fnReceive, update: fnUpdate, start: function(event, ui) { @@ -533,4 +555,18 @@ $(document).ready(function() { //set things like a reference to the table. AIRTIME.showbuilder.init(oTable); + //add event to cursors. + tableDiv.find("tbody").on("click", "div.marker", function(event){ + var tr = $(this).parents("tr"); + + if (tr.hasClass("cursor-selected-row")) { + tr.removeClass("cursor-selected-row"); + } + else { + tr.addClass("cursor-selected-row"); + } + + return false; + }); + });