CC-3335 : Timeline: Drag and drop usability improvements

cursor must be used to add tracks to timeline, not check boxes.
This commit is contained in:
Naomi Aro 2012-02-23 17:46:07 +01:00
parent b357b80054
commit 753c05ce5b
6 changed files with 163 additions and 114 deletions

View file

@ -107,7 +107,7 @@ class ShowbuilderController extends Zend_Controller_Action
public function scheduleRemoveAction() public function scheduleRemoveAction()
{ {
$request = $this->getRequest(); $request = $this->getRequest();
$items = $request->getParam("items", null); $items = $request->getParam("items", array());
try { try {
$scheduler = new Application_Model_Scheduler(); $scheduler = new Application_Model_Scheduler();

View file

@ -9,12 +9,13 @@ class Application_Model_ShowBuilder {
private $opts; private $opts;
private $contentDT; private $contentDT;
private $epoch_now;
private $defaultRowArray = array( private $defaultRowArray = array(
"header" => false, "header" => false,
"footer" => false, "footer" => false,
"empty" => false, "empty" => false,
"checkbox" => false, "allowed" => false,
"id" => 0, "id" => 0,
"instance" => "", "instance" => "",
"starts" => "", "starts" => "",
@ -37,6 +38,7 @@ class Application_Model_ShowBuilder {
$this->timezone = date_default_timezone_get(); $this->timezone = date_default_timezone_get();
$this->user = Application_Model_User::GetCurrentUser(); $this->user = Application_Model_User::GetCurrentUser();
$this->opts = $p_opts; $this->opts = $p_opts;
$this->epoch_now = time();
} }
/* /*
@ -89,6 +91,7 @@ class Application_Model_ShowBuilder {
private function makeFooterRow($p_item) { private function makeFooterRow($p_item) {
$row = $this->defaultRowArray; $row = $this->defaultRowArray;
$this->isAllowed($p_item, $row);
$row["footer"] = true; $row["footer"] = true;
$showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC")); $showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC"));
@ -101,6 +104,16 @@ class Application_Model_ShowBuilder {
return $row; 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) { private function getRowTimestamp($p_item, &$row) {
if (is_null($p_item["si_last_scheduled"])) { if (is_null($p_item["si_last_scheduled"])) {
@ -116,6 +129,8 @@ class Application_Model_ShowBuilder {
private function makeHeaderRow($p_item) { private function makeHeaderRow($p_item) {
$row = $this->defaultRowArray; $row = $this->defaultRowArray;
$this->isAllowed($p_item, $row);
Logging::log("making header for show id ".$p_item["show_id"]);
$this->getRowTimestamp($p_item, $row); $this->getRowTimestamp($p_item, $row);
$showStartDT = new DateTime($p_item["si_starts"], new DateTimeZone("UTC")); $showStartDT = new DateTime($p_item["si_starts"], new DateTimeZone("UTC"));
@ -137,16 +152,10 @@ class Application_Model_ShowBuilder {
private function makeScheduledItemRow($p_item) { private function makeScheduledItemRow($p_item) {
$row = $this->defaultRowArray; $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); $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"])) { if (isset($p_item["sched_starts"])) {
$schedStartDT = new DateTime($p_item["sched_starts"], new DateTimeZone("UTC")); $schedStartDT = new DateTime($p_item["sched_starts"], new DateTimeZone("UTC"));
@ -172,6 +181,8 @@ class Application_Model_ShowBuilder {
$row["empty"] = true; $row["empty"] = true;
$row["id"] = 0 ; $row["id"] = 0 ;
$row["instance"] = intval($p_item["si_id"]); $row["instance"] = intval($p_item["si_id"]);
//return null;
} }
return $row; return $row;
@ -219,7 +230,11 @@ class Application_Model_ShowBuilder {
} }
//make a normal data row. //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. //make the last footer if there were any scheduled items.

View file

@ -37,15 +37,16 @@ class Application_Model_User {
public function canSchedule($p_showId) { public function canSchedule($p_showId) {
$type = $this->getType(); $type = $this->getType();
$result = false;
if ( $type === UTYPE_ADMIN || if ( $type === UTYPE_ADMIN ||
$type === UTYPE_PROGRAM_MANAGER || $type === UTYPE_PROGRAM_MANAGER ||
CcShowHostsQuery::create()->filterByDbShow($p_showId)->filterByDbHost($this->getId())->count() > 0 ) CcShowHostsQuery::create()->filterByDbShow($p_showId)->filterByDbHost($this->getId())->count() > 0 )
{ {
return true; $result = true;
} }
return false; return $result;
} }
public function isUserType($type, $showId=''){ public function isUserType($type, $showId=''){

View file

@ -4,8 +4,7 @@
width:100px; width:100px;
} }
table tr.selected-row td, table tr.selected-row th, table.datatable tr.cursor-selected-row td, table.datatable tr.cursor-selected-row th {
table.datatable tr.selected-row td, table.datatable tr.selected-row th {
border-bottom: 2px solid #db0000 !important; border-bottom: 2px solid #db0000 !important;
} }
.innerWrapper { .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; background:url(images/tl-arrow.png) no-repeat 0 0;
display:block; display:block;
} }
tr.selected-row .marker { tr.cursor-selected-row .marker {
background-position: 0 -15px; background-position: 0 -15px;
} }

View file

@ -41,33 +41,31 @@ var AIRTIME = (function(AIRTIME){
fnAddSelectedItems = function() { fnAddSelectedItems = function() {
var oLibTT = TableTools.fnGetInstance('library_display'), var oLibTT = TableTools.fnGetInstance('library_display'),
oSchedTT = TableTools.fnGetInstance('show_builder_table'),
aData = oLibTT.fnGetSelectedData(), aData = oLibTT.fnGetSelectedData(),
item, i,
length,
temp, temp,
aMediaIds = [], aMediaIds = [],
aSchedIds = []; aSchedIds = [];
//process selected files/playlists. //process selected files/playlists.
for (item in aData) { for (i=0, length = aData.length; i < length; i++) {
temp = aData[item]; temp = aData[i];
if (temp !== null && temp.hasOwnProperty('id')) { aMediaIds.push({"id": temp.id, "type": temp.ftype});
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. //process selected schedule rows to add media after.
for (item in aData) { for (i=0, length = aData.length; i < length; i++) {
temp = aData[item]; temp = aData[i];
if (temp !== null && temp.hasOwnProperty('id')) { aSchedIds.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp});
aSchedIds.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp});
}
} }
AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds); AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
}; };
//[0] = button text //[0] = button text
//[1] = id //[1] = id

View file

@ -62,8 +62,7 @@ $(document).ready(function() {
fnAddSelectedItems, fnAddSelectedItems,
fnRemoveSelectedItems, fnRemoveSelectedItems,
oRange, oRange,
fnServerData, fnServerData;
fnShowBuilderRowCallback;
oBaseDatePickerSettings = { oBaseDatePickerSettings = {
dateFormat: 'yy-mm-dd', dateFormat: 'yy-mm-dd',
@ -178,87 +177,17 @@ $(document).ready(function() {
fnServerData.start = oRange.start; fnServerData.start = oRange.start;
fnServerData.end = oRange.end; 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 = '<span>'+aData.title+'</span><span>'+aData.starts+'</span><span>'+aData.ends+'</span>';
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 = '<span class="ui-icon ui-icon-check"></span>';
cl = cl + ' ui-state-highlight';
}
else {
node.innerHTML = '<span class="ui-icon ui-icon-notice"></span>';
cl = cl + ' ui-state-error';
}
sSeparatorHTML = '<span>'+aData.fRuntime+'</span>';
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
}
else {
node = nRow.children[0];
if (aData.checkbox === true) {
var height = $(node).height();
node.innerHTML = '<div class="innerWrapper" height="'+height+'"><input type="checkbox" name="'+aData.id+'"></input><div class="marker"></div></div>';
}
else {
node.innerHTML = '';
cl = cl + " sb-not-allowed";
}
if (aData.empty === true) {
sSeparatorHTML = '<span>Show Empty</span>';
cl = cl + " sb-empty odd";
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
}
}
};
fnRemoveSelectedItems = function() { fnRemoveSelectedItems = function() {
var oTT = TableTools.fnGetInstance('show_builder_table'), var oTT = TableTools.fnGetInstance('show_builder_table'),
aData = oTT.fnGetSelectedData(), aData = oTT.fnGetSelectedData(),
item, i,
length,
temp, temp,
aItems = []; aItems = [];
for (item in aData) { for (i=0, length = aData.length; i < length; i++) {
temp = aData[item]; temp = aData[i];
if (temp !== null && temp.hasOwnProperty('id')) { aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp});
aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp});
}
} }
AIRTIME.showbuilder.fnRemove(aItems); AIRTIME.showbuilder.fnRemove(aItems);
@ -266,7 +195,7 @@ $(document).ready(function() {
oTable = tableDiv.dataTable( { oTable = tableDiv.dataTable( {
"aoColumns": [ "aoColumns": [
/* checkbox */ {"mDataProp": "checkbox", "sTitle": "<input type='checkbox' name='sb_cb_all'>", "sWidth": "15px"}, /* checkbox */ {"mDataProp": "allowed", "sTitle": "<input type='checkbox' name='sb_cb_all'>", "sWidth": "15px"},
/* starts */{"mDataProp": "starts", "sTitle": "Start"}, /* starts */{"mDataProp": "starts", "sTitle": "Start"},
/* ends */{"mDataProp": "ends", "sTitle": "End"}, /* ends */{"mDataProp": "ends", "sTitle": "End"},
/* runtime */{"mDataProp": "runtime", "sTitle": "Duration"}, /* runtime */{"mDataProp": "runtime", "sTitle": "Duration"},
@ -341,7 +270,100 @@ $(document).ready(function() {
}, },
"fnServerData": fnServerData, "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 = '<span>'+aData.title+'</span><span>'+aData.starts+'</span><span>'+aData.ends+'</span>';
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 = '<span class="ui-icon ui-icon-check"></span>';
cl = cl + ' ui-state-highlight';
}
else {
node.innerHTML = '<span class="ui-icon ui-icon-notice"></span>';
cl = cl + ' ui-state-error';
}
sSeparatorHTML = '<span>'+aData.fRuntime+'</span>';
fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
}
else if (aData.empty === true) {
sSeparatorHTML = '<span>Show Empty</span>';
cl = cl + " sb-empty odd";
fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
}
else {
node = nRow.children[0];
if (aData.allowed === true) {
node.innerHTML = '<input type="checkbox" name="'+aData.id+'"></input>';
}
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 = $("<div />", {
"class": "innerWrapper",
"css": {
"height": td.height()
}
});
markerDiv = $("<div />", {
"class": "marker"
});
td.append(markerDiv).wrapInner(wrapperDiv);
});
},
"fnHeaderCallback": function(nHead) { "fnHeaderCallback": function(nHead) {
$(nHead).find("input[type=checkbox]").attr("checked", false); $(nHead).find("input[type=checkbox]").attr("checked", false);
}, },
@ -366,8 +388,8 @@ $(document).ready(function() {
var node = e.currentTarget; var node = e.currentTarget;
//don't select separating rows, or shows without privileges. //don't select separating rows, or shows without privileges.
if ($(node).hasClass("sb-header") if ($(node).hasClass("sb-header")
|| $(node).hasClass("sb-footer") || $(node).hasClass("sb-footer")
|| $(node).hasClass("sb-not-allowed")) { || $(node).hasClass("sb-not-allowed")) {
return false; return false;
} }
return true; return true;
@ -408,7 +430,7 @@ $(document).ready(function() {
if ($(this).is(":checked")) { if ($(this).is(":checked")) {
var allowedNodes; 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){ allowedNodes.each(function(i, el){
oTT.fnSelect(el); oTT.fnSelect(el);
@ -515,7 +537,7 @@ $(document).ready(function() {
return { return {
placeholder: "placeholder show-builder-placeholder ui-state-highlight", placeholder: "placeholder show-builder-placeholder ui-state-highlight",
forcePlaceholderSize: true, 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, receive: fnReceive,
update: fnUpdate, update: fnUpdate,
start: function(event, ui) { start: function(event, ui) {
@ -533,4 +555,18 @@ $(document).ready(function() {
//set things like a reference to the table. //set things like a reference to the table.
AIRTIME.showbuilder.init(oTable); 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;
});
}); });