diff --git a/airtime_mvc/application/controllers/PreferenceController.php b/airtime_mvc/application/controllers/PreferenceController.php index d1d134218..cc9080e53 100644 --- a/airtime_mvc/application/controllers/PreferenceController.php +++ b/airtime_mvc/application/controllers/PreferenceController.php @@ -13,6 +13,7 @@ class PreferenceController extends Zend_Controller_Action ->addActionContext('remove-watch-directory', 'json') ->addActionContext('is-import-in-progress', 'json') ->addActionContext('change-stream-setting', 'json') + ->addActionContext('get-liquidsoap-status', 'json') ->initContext(); } @@ -284,6 +285,20 @@ class PreferenceController extends Zend_Controller_Action } die(json_encode($res)); } + + public function getLiquidsoapStatusAction(){ + $out = array(); + $num_of_stream = intval(Application_Model_Preference::GetNumOfStreams()); + for($i=1; $i<=$num_of_stream; $i++){ + $status = Application_Model_StreamSetting::getLiquidsoapError($i); + $status = $status == ''?"OK":$status; + if(!Application_Model_StreamSetting::getStreamEnabled($i)){ + $status = "N/A"; + } + $out[] = array("id"=>$i, "status"=>$status); + } + die(json_encode($out)); + } } diff --git a/airtime_mvc/application/forms/StreamSettingSubForm.php b/airtime_mvc/application/forms/StreamSettingSubForm.php index c8398d806..70e28a7ba 100644 --- a/airtime_mvc/application/forms/StreamSettingSubForm.php +++ b/airtime_mvc/application/forms/StreamSettingSubForm.php @@ -154,9 +154,11 @@ class Application_Form_StreamSettingSubForm extends Zend_Form_SubForm{ $user->setAttrib("disabled", "disabled"); } $this->addElement($user); - + + $liquidsopa_error_msg = "Getting infomation from the server.."; + $this->setDecorators(array( - array('ViewScript', array('viewScript' => 'form/stream-setting-form.phtml', "stream_number"=>$stream_number)) + array('ViewScript', array('viewScript' => 'form/stream-setting-form.phtml', "stream_number"=>$stream_number, "liquidsoap_error_msg"=>$liquidsopa_error_msg)) )); } diff --git a/airtime_mvc/application/models/StreamSetting.php b/airtime_mvc/application/models/StreamSetting.php index 7d5c20cdd..aeedeb0c7 100644 --- a/airtime_mvc/application/models/StreamSetting.php +++ b/airtime_mvc/application/models/StreamSetting.php @@ -99,9 +99,25 @@ class Application_Model_StreamSetting { $keyname = "s".$stream_id."_liquidsoap_error"; $sql = "SELECT value FROM cc_stream_setting" - ." WHERE keyname = '$key'"; + ." WHERE keyname = '$keyname'"; $result = $CC_DBC->GetOne($sql); return $result; } + + public static function getStreamEnabled($stream_id){ + global $CC_DBC; + + $keyname = "s".$stream_id."_ouput"; + $sql = "SELECT value FROM cc_stream_setting" + ." WHERE keyname = '$keyname'"; + $result = $CC_DBC->GetOne($sql); + if($result == 'disabled'){ + $result = false; + }else{ + $result = true; + } + + return $result; + } } diff --git a/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml b/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml index 66c12949b..07f8bf9bc 100644 --- a/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml +++ b/airtime_mvc/application/views/scripts/form/stream-setting-form.phtml @@ -5,6 +5,12 @@
stream_number != '1'?'style="display: none;':''?> id="-config">
+
+ +
+
+ liquidsoap_error_msg?> +
diff --git a/airtime_mvc/public/css/playlist_builder.css b/airtime_mvc/public/css/playlist_builder.css index 7aef3be75..2d02f4676 100644 --- a/airtime_mvc/public/css/playlist_builder.css +++ b/airtime_mvc/public/css/playlist_builder.css @@ -2,6 +2,7 @@ width: 40%; min-height: 475px; padding: 8px; + padding-bottom: 0px; font-size: 16px; } @@ -39,10 +40,11 @@ #spl_sortable { list-style: none; padding:0; - height: 300px; - overflow: auto; + padding-bottom:50px; width:100%; + min-height: 320px; margin-top:0; + margin-bottom:0; } #side_playlist li { diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 98a185f18..6408e5c97 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -111,46 +111,13 @@ function dtRowCallback( nRow, aData, iDisplayIndex, iDisplayIndexFull ) { // insert id on lenth field $('td:eq(4)', nRow).attr("id", "length"); - - $('td:gt(0)', nRow).qtip({ - content: { - text: "Loading...", - title: { - text: aData[1] - }, - ajax: { - url: "/Library/get-file-meta-data", - type: "post", - data: ({format: "html", id : id, type: type}), - success: function(data, status){ - this.set('content.text', data) - } - } - }, - position: { - adjust: { - resize: true, - method: "flip flip" - }, - at: "right center", - my: "left top", - viewport: $(window) - }, - style: { - width: 570, - classes: "ui-tooltip-dark" - }, - show: { - delay: 700 - } - } - ); return nRow; } function dtDrawCallback() { addLibraryItemEvents(); + addMetadataQtip(); } function addProgressIcon(id) { @@ -254,6 +221,60 @@ function addQtipToSCIcons(){ }); } +function addMetadataQtip(){ + var tableRow = $('#library_display tbody tr'); + tableRow.each(function(){ + var title = $(this).find('td:eq(0)').html() + var info = $(this).attr("id") + info = info.split("_"); + var id = info[1]; + var type = info[0]; + $(this).qtip({ + content: { + text: "Loading...", + title: { + text: title + }, + ajax: { + url: "/Library/get-file-meta-data", + type: "post", + data: ({format: "html", id : id, type: type}), + success: function(data, status){ + this.set('content.text', data) + } + } + }, + position: { + target: 'event', + adjust: { + resize: true, + method: "flip flip" + }, + at: "right center", + my: "left top", + viewport: $(window) + }, + style: { + width: 570, + classes: "ui-tooltip-dark" + }, + show: 'mousedown', + events: { + show: function(event, api) { + // Only show the tooltip if it was a right-click + if(event.originalEvent.button !== 2) { + event.preventDefault(); + } + } + } + }) + }) + + tableRow.bind('contextmenu', function(e){ + return false; + }) +} + $(document).ready(function() { $('.tabs').tabs(); diff --git a/airtime_mvc/public/js/airtime/preferences/streamsetting.js b/airtime_mvc/public/js/airtime/preferences/streamsetting.js index fab7d5b13..a97df38ca 100644 --- a/airtime_mvc/public/js/airtime/preferences/streamsetting.js +++ b/airtime_mvc/public/js/airtime/preferences/streamsetting.js @@ -69,6 +69,29 @@ function showForIcecast(ele){ div.find("select[id$=data-type]").find("option[value='ogg']").attr("disabled",""); } +function checkLiquidsoapStatus(){ + var url = '/Preference/get-liquidsoap-status/format/json'; + var id = $(this).attr("id"); + $.post(url, function(json){ + var json_obj = jQuery.parseJSON(json); + for(var i=0;i y) ? 1 : 0)); @@ -612,6 +615,8 @@ "string-desc": function ( a, b ) { + if ( typeof a != 'string' ) { a = ''; } + if ( typeof b != 'string' ) { b = ''; } var x = a.toLowerCase(); var y = b.toLowerCase(); return ((x < y) ? 1 : ((x > y) ? -1 : 0)); @@ -646,7 +651,7 @@ if ( isNaN(x) || x==="" ) { - x = Date.parse( "01/01/1970 00:00:00" ); + x = Date.parse( "01/01/1970 00:00:00" ); } if ( isNaN(y) || y==="" ) { @@ -663,7 +668,7 @@ if ( isNaN(x) || x==="" ) { - x = Date.parse( "01/01/1970 00:00:00" ); + x = Date.parse( "01/01/1970 00:00:00" ); } if ( isNaN(y) || y==="" ) { @@ -711,15 +716,19 @@ * Function: - * Purpose: Check to see if a string is numeric * Returns: string:'numeric' or null - * Inputs: string:sText - string to check + * Inputs: mixed:sText - string to check */ function ( sData ) { /* Allow zero length strings as a number */ - if ( sData.length === 0 ) + if ( typeof sData == 'number' ) { return 'numeric'; } + else if ( typeof sData != 'string' ) + { + return null; + } var sValidFirstChars = "0123456789-"; var sValidChars = "0123456789."; @@ -765,7 +774,7 @@ function ( sData ) { var iParse = Date.parse(sData); - if ( (iParse !== null && !isNaN(iParse)) || sData.length === 0 ) + if ( (iParse !== null && !isNaN(iParse)) || (typeof sData == 'string' && sData.length === 0) ) { return 'date'; } @@ -780,7 +789,7 @@ */ function ( sData ) { - if ( sData.indexOf('<') != -1 && sData.indexOf('>') != -1 ) + if ( typeof sData == 'string' && sData.indexOf('<') != -1 && sData.indexOf('>') != -1 ) { return 'html'; } @@ -840,7 +849,7 @@ * Function: dataTable * Purpose: DataTables information * Returns: - - * Inputs: object:oInit - initalisation options for the table + * Inputs: object:oInit - initialisation options for the table */ $.fn.dataTable = function( oInit ) { @@ -914,7 +923,8 @@ "bProcessing": false, "bSortClasses": true, "bStateSave": false, - "bServerSide": false + "bServerSide": false, + "bDeferRender": false }; /* @@ -959,10 +969,12 @@ "sLengthMenu": "Show _MENU_ entries", "sZeroRecords": "No matching records found", "sEmptyTable": "No data available in table", + "sLoadingRecords": "Loading...", "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", "sInfoEmpty": "Showing 0 to 0 of 0 entries", "sInfoFiltered": "(filtered from _MAX_ total entries)", "sInfoPostFix": "", + "sInfoThousands": ",", "sSearch": "Search:", "sUrl": "", "oPaginate": { @@ -1007,6 +1019,20 @@ * Scope: jQuery.dataTable.classSettings */ this.aoColumns = []; + + /* + * Variable: aoHeader + * Purpose: Store information about the table's header + * Scope: jQuery.dataTable.classSettings + */ + this.aoHeader = []; + + /* + * Variable: aoFooter + * Purpose: Store information about the table's footer + * Scope: jQuery.dataTable.classSettings + */ + this.aoFooter = []; /* * Variable: iNextId @@ -1059,22 +1085,22 @@ this.aaSortingFixed = null; /* - * Variable: asStripClasses + * Variable: asStripeClasses * Purpose: Classes to use for the striping of a table * Scope: jQuery.dataTable.classSettings */ - this.asStripClasses = []; + this.asStripeClasses = []; /* - * Variable: asDestoryStrips - * Purpose: If restoring a table - we should restore it's striping classes as well + * Variable: asDestroyStripes + * Purpose: If restoring a table - we should restore its striping classes as well * Scope: jQuery.dataTable.classSettings */ - this.asDestoryStrips = []; + this.asDestroyStripes = []; /* * Variable: sDestroyWidth - * Purpose: If restoring a table - we should restore it's width + * Purpose: If restoring a table - we should restore its width * Scope: jQuery.dataTable.classSettings */ this.sDestroyWidth = 0; @@ -1110,9 +1136,17 @@ */ this.aoDrawCallback = []; + /* + * Variable: fnPreDrawCallback + * Purpose: Callback function for just before the table is redrawn. A return of false + * will be used to cancel the draw. + * Scope: jQuery.dataTable.classSettings + */ + this.fnPreDrawCallback = null; + /* * Variable: fnInitComplete - * Purpose: Callback function for when the table has been initalised + * Purpose: Callback function for when the table has been initialised * Scope: jQuery.dataTable.classSettings */ this.fnInitComplete = null; @@ -1159,6 +1193,14 @@ */ this.nTableWrapper = null; + /* + * Variable: bDeferLoading + * Purpose: Indicate if when using server-side processing the loading of data + * should be deferred until the second draw + * Scope: jQuery.dataTable.classSettings + */ + this.bDeferLoading = false; + /* * Variable: bInitialised * Purpose: Indicate if all required information has been read in @@ -1262,6 +1304,15 @@ */ this.sAjaxSource = null; + /* + * Variable: sAjaxDataProp + * Purpose: Property from a given object from which to read the table data from. This can + * be an empty string (when not server-side processing), in which case it is + * assumed an an array is given directly. + * Scope: jQuery.dataTable.classSettings + */ + this.sAjaxDataProp = 'aaData'; + /* * Variable: bAjaxDataGet * Purpose: Note if draw should be blocked while getting data @@ -1269,16 +1320,27 @@ */ this.bAjaxDataGet = true; + /* + * Variable: jqXHR + * Purpose: The last jQuery XHR object that was used for server-side data gathering. + * This can be used for working with the XHR information in one of the callbacks + * Scope: jQuery.dataTable.classSettings + */ + this.jqXHR = null; + /* * Variable: fnServerData * Purpose: Function to get the server-side data - can be overruled by the developer * Scope: jQuery.dataTable.classSettings */ - this.fnServerData = function ( url, data, callback ) { - $.ajax( { + this.fnServerData = function ( url, data, callback, settings ) { + settings.jqXHR = $.ajax( { "url": url, "data": data, - "success": callback, + "success": function (json) { + $(settings.oInstance).trigger('xhr', settings); + callback( json ); + }, "dataType": "json", "cache": false, "error": function (xhr, error, thrown) { @@ -1290,6 +1352,17 @@ } ); }; + /* + * Variable: aoServerParams + * Purpose: Functions which are called prior to sending an Ajax request so extra parameters + * can easily be sent to the server + * Scope: jQuery.dataTable.classSettings + * Notes: Each array element is an object with the following parameters: + * function:fn - function to call + * string:sName - name callback - useful for knowing where it came from (plugin etc) + */ + this.aoServerParams = []; + /* * Variable: fnFormatNumber * Purpose: Format numbers for display @@ -1310,7 +1383,7 @@ { if ( i%3 === 0 && i !== 0 ) { - out = ','+out; + out = this.oLanguage.sInfoThousands+out; } out = a[iLen-i-1]+out; } @@ -1380,7 +1453,7 @@ this.bJUI = false; /* - * Variable: bJUI + * Variable: oClasses * Purpose: Should we add the markup needed for jQuery UI theming? * Scope: jQuery.dataTable.classSettings */ @@ -1394,12 +1467,28 @@ this.bFiltered = false; this.bSorted = false; + /* + * Variable: bSortCellsTop + * Purpose: Indicate that if multiple rows are in the header and there is more than one + * unique cell per column, if the top one (true) or bottom one (false) should + * be used for sorting / title by DataTables + * Scope: jQuery.dataTable.classSettings + */ + this.bSortCellsTop = false; + /* * Variable: oInit * Purpose: Initialisation object that is used for the table * Scope: jQuery.dataTable.classSettings */ this.oInit = null; + + /* + * Variable: aoDestroyCallback + * Purpose: Destroy callback functions + * Scope: jQuery.dataTable.classSettings + */ + this.aoDestroyCallback = []; } /* @@ -1680,10 +1769,10 @@ * Purpose: Open a display row (append a row after the row in question) * Returns: node:nNewRow - the row opened * Inputs: node:nTr - the table row to 'open' - * string:sHtml - the HTML to put into the row + * string|node|jQuery:mHtml - the HTML to put into the row * string:sClass - class to give the new TD cell */ - this.fnOpen = function( nTr, sHtml, sClass ) + this.fnOpen = function( nTr, mHtml, sClass ) { /* Find settings from table node */ var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); @@ -1696,8 +1785,16 @@ nNewRow.appendChild( nNewCell ); nNewCell.className = sClass; nNewCell.colSpan = _fnVisbleColumns( oSettings ); - nNewCell.innerHTML = sHtml; - + + if( typeof mHtml.jquery != 'undefined' || typeof mHtml == "object" ) + { + nNewCell.appendChild( mHtml ); + } + else + { + nNewCell.innerHTML = mHtml; + } + /* If the nTr isn't on the page at the moment - then we don't insert at the moment */ var nTrs = $('tr', oSettings.nTBody); if ( $.inArray(nTr, nTrs) != -1 ) @@ -1745,16 +1842,15 @@ * Function: fnGetData * Purpose: Return an array with the data which is used to make up the table * Returns: array array string: 2d data array ([row][column]) or array string: 1d data array - * or - * array string (if iRow specified) + * or string if both row and column are given * Inputs: mixed:mRow - optional - if not present, then the full 2D array for the table * if given then: - * int: - return 1D array for aoData entry of this index - * node(TR): - return 1D array for this TR element - * Inputs: int:iRow - optional - if present then the array returned will be the data for - * the row with the index 'iRow' + * int: - return data object for aoData entry of this index + * node(TR): - return data object for this TR element + * int:iCol - optional - the column that you want the data of. This will take into + * account mDataProp and return the value DataTables uses for this column */ - this.fnGetData = function( mRow ) + this.fnGetData = function( mRow, iCol ) { var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); @@ -1762,7 +1858,13 @@ { var iRow = (typeof mRow == 'object') ? _fnNodeToDataIndex(oSettings, mRow) : mRow; - return oSettings.aoData[iRow]._aData; + + if ( typeof iCol != 'undefined' ) + { + return _fnGetCellData( oSettings, iRow, iCol, '' ); + } + return (typeof oSettings.aoData[iRow] != 'undefined') ? + oSettings.aoData[iRow]._aData : null; } return _fnGetDataMaster( oSettings ); }; @@ -1782,7 +1884,7 @@ if ( typeof iRow != 'undefined' ) { - return oSettings.aoData[iRow].nTr; + return (typeof oSettings.aoData[iRow] != 'undefined') ? oSettings.aoData[iRow].nTr : null; } return _fnGetTrNodes( oSettings ); }; @@ -1792,34 +1894,28 @@ * Purpose: Get the array indexes of a particular cell from it's DOM element * Returns: int: - row index, or array[ int, int, int ]: - row index, column index (visible) * and column index including hidden columns - * Inputs: node:nNode - this can either be a TR or a TD in the table, the return is + * Inputs: node:nNode - this can either be a TR, TD or TH in the table's body, the return is * dependent on this input */ this.fnGetPosition = function( nNode ) { var oSettings = _fnSettingsFromNode( this[_oExt.iApiIndex] ); - var i; + var sNodeName = nNode.nodeName.toUpperCase(); - if ( nNode.nodeName.toUpperCase() == "TR" ) + if ( sNodeName == "TR" ) { return _fnNodeToDataIndex(oSettings, nNode); } - else if ( nNode.nodeName.toUpperCase() == "TD" ) + else if ( sNodeName == "TD" || sNodeName == "TH" ) { var iDataIndex = _fnNodeToDataIndex(oSettings, nNode.parentNode); - var iCorrector = 0; - for ( var j=0 ; jtr', oSettings.nTHead)[0]; - var nTrFoot = $('>tr', oSettings.nTFoot)[0]; - var anTheadTh = []; - var anTfootTh = []; - for ( i=0 ; i= _fnVisbleColumns( oSettings ) ) + bAppend = (iInsert >= _fnVisbleColumns( oSettings )); + + /* Which coloumn should we be inserting before? */ + if ( !bAppend ) { - nTrHead.appendChild( anTheadTh[iCol] ); - anTrs = $('>tr', oSettings.nTHead); - for ( i=1, iLen=anTrs.length ; itr', oSettings.nTFoot); - for ( i=1, iLen=anTrs.length ; itr', oSettings.nTHead); - for ( i=1, iLen=anTrs.length ; itr', oSettings.nTFoot); - for ( i=1, iLen=anTrs.length ; itd:eq('+iBefore+')', - oSettings.aoData[i].nTr)[0] ); + oSettings.aoData[i].nTr.appendChild( + oSettings.aoData[i]._anHidden[iCol] + ); + } + else + { + oSettings.aoData[i].nTr.insertBefore( + oSettings.aoData[i]._anHidden[iCol], + _fnGetTdNodes( oSettings, i )[iBefore] ); + } } } - - oSettings.aoColumns[iCol].bVisible = true; } else { /* Remove a column from display */ - nTrHead.removeChild( anTheadTh[iCol] ); - for ( i=0, iLen=oSettings.aoColumns[iCol].anThExtra.length ; i