From d1e734104605c29a5724e313e455e755ab87e385 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Wed, 22 Aug 2012 16:08:46 -0400 Subject: [PATCH 01/10] cc-3936: Refactored php code to upload files --- airtime_mvc/application/models/StoredFile.php | 89 +++++++++---------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index 642212fc5..9be0e51b0 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -853,58 +853,55 @@ class Application_Model_StoredFile { $audio_file = $p_targetDir . DIRECTORY_SEPARATOR . $tempname; Logging::info('copyFileToStor: moving file '.$audio_file); - $md5 = md5_file($audio_file); - $duplicate = Application_Model_StoredFile::RecallByMd5($md5, true); - $result = null; - if ($duplicate) { - if (file_exists($duplicate->getFilePath())) { - $duplicateName = $duplicate->getMetadataValue('MDATA_KEY_TITLE'); - $result = array( "code" => 106, "message" => "An identical audioclip named '$duplicateName' already exists on the server."); + $storDir = Application_Model_MusicDir::getStorDir(); + $stor = $storDir->getDirectory(); + // check if "organize" dir exists and if not create one + if (!file_exists($stor."/organize")) { + if (!mkdir($stor."/organize", 0777)) { + return array( + "code" => 109, + "message" => "Failed to create 'organize' directory."); } } - if (!isset($result)) {//The file has no duplicate, so proceed to copy. - $storDir = Application_Model_MusicDir::getStorDir(); - $stor = $storDir->getDirectory(); - // check if "organize" dir exists and if not create one - if (!file_exists($stor."/organize")) { - if (!mkdir($stor."/organize", 0777)) { - $result = array("code" => 109, "message" => "Failed to create 'organize' directory."); - - return $result; - } - } - - if (chmod($audio_file, 0644) === false) { - Logging::info("Warning: couldn't change permissions of $audio_file to 0644"); - } - - //check to see if there is enough space in $stor to continue. - if (self::isEnoughDiskSpaceToCopy($stor, $audio_file)) { - $audio_stor = Application_Common_OsPath::join($stor, "organize", $fileName); - - if (self::liquidsoapFilePlayabilityTest($audio_file)) { - - Logging::info("copyFileToStor: moving file $audio_file to $audio_stor"); - - //Martin K.: changed to rename: Much less load + quicker since this is an atomic operation - if (@rename($audio_file, $audio_stor) === false) { - #something went wrong likely there wasn't enough space in the audio_stor to move the file too. - #warn the user that the file wasn't uploaded and they should check if there is enough disk space. - unlink($audio_file);//remove the file after failed rename - $result = array("code" => 108, "message" => "The file was not uploaded, this error can occur if the computer hard drive does not have enough disk space or the stor directory does not have correct write permissions."); - } - } else { - $result = array("code" => 110, "message" => "This file appears to be corrupted and will not be added to media library."); - } - - } else { - $result = array("code" => 107, "message" => "The file was not uploaded, there is ".$freeSpace."MB of disk space left and the file you are uploading has a size of ".$fileSize."MB."); - } + if (chmod($audio_file, 0644) === false) { + Logging::info("Warning: couldn't change permissions of $audio_file to 0644"); } - return $result; + // Check if we have enough space before copying + if(!self::isEnoughDiskSpaceToCopy($stor, $audio_file)) { + $freeSpace = disk_free_space($stor); + return array("code" => 107, + "message" => "The file was not uploaded, there is + ".$freeSpace."MB of disk space left and the file you are + uploading has a size of ".$fileSize."MB."); + } + + // Check if liquidsoap can play this file + if(!self::liquidsoapFilePlayabilityTest($audio_file)) { + return array( + "code" => 110, + "message" => "This file appears to be corrupted and will not + be added to media library."); + } + + // Did all the checks for realz, now trying to copy + $audio_stor = Application_Common_OsPath::join($stor, "organize", $fileName); + Logging::info("copyFileToStor: moving file $audio_file to $audio_stor"); + //Martin K.: changed to rename: Much less load + quicker since this is an atomic operation + if (@rename($audio_file, $audio_stor) === false) { + #something went wrong likely there wasn't enough space in the audio_stor to move the file too. + #warn the user that the file wasn't uploaded and they should check if there is enough disk space. + unlink($audio_file);//remove the file after failed rename + return array( + "code" => 108, + "message" => " + The file was not uploaded, this error can occur if the computer + hard drive does not have enough disk space or the stor + directory does not have correct write permissions. "); + } + return null; } /* From f47e32d9de58ff232790f966a1feed0e1d9fef9a Mon Sep 17 00:00:00 2001 From: denise Date: Wed, 22 Aug 2012 16:29:17 -0400 Subject: [PATCH 02/10] CC-4266: Smart Block: Bit rate with 'is in the range' modifier -> 2nd range value doesn't get multiplied by 1000 -fixed --- airtime_mvc/application/models/Block.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/airtime_mvc/application/models/Block.php b/airtime_mvc/application/models/Block.php index a7b39869e..2b88568fa 100644 --- a/airtime_mvc/application/models/Block.php +++ b/airtime_mvc/application/models/Block.php @@ -1166,6 +1166,9 @@ EOT; // multiply 1000 because we store only number value // e.g 192kps is stored as 192000 $spCriteriaValue = $criteria['value']*1000; + if (isset($criteria['extra'])) { + $criteria['extra'] *= 1000; + } } else { $spCriteriaValue = addslashes($criteria['value']); } From c7cf687e66dc11baedeef1c299aa415df7f24d2e Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Wed, 22 Aug 2012 17:01:39 -0400 Subject: [PATCH 03/10] MM2: Imporoved some logging and error checking. --- python_apps/media-monitor2/media/monitor/eventcontractor.py | 3 +++ python_apps/media-monitor2/media/monitor/metadata.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/python_apps/media-monitor2/media/monitor/eventcontractor.py b/python_apps/media-monitor2/media/monitor/eventcontractor.py index 9eb181bf4..089b30a44 100644 --- a/python_apps/media-monitor2/media/monitor/eventcontractor.py +++ b/python_apps/media-monitor2/media/monitor/eventcontractor.py @@ -57,4 +57,7 @@ class EventContractor(Loggable): def __unregister(self, evt): try: del self.store[evt.path] + except KeyError as e: + self.logger.info("Contractor failed to unrecord event: '%s'" \ + % evt.path) except Exception as e: self.unexpected_exception(e) diff --git a/python_apps/media-monitor2/media/monitor/metadata.py b/python_apps/media-monitor2/media/monitor/metadata.py index 5f222c4b4..f718b7b2e 100644 --- a/python_apps/media-monitor2/media/monitor/metadata.py +++ b/python_apps/media-monitor2/media/monitor/metadata.py @@ -154,6 +154,11 @@ class Metadata(Loggable): try : full_mutagen = mutagen.File(fpath, easy=True) except Exception : raise BadSongFile(fpath) self.path = fpath + if not os.path.exists(self.path): + self.logger.info("Attempting to read metadata of file \ + that does not exist. Setting metadata to {}") + self.__metadata = {} + return # TODO : Simplify the way all of these rules are handled right not it's # extremely unclear and needs to be refactored. self.__metadata = Metadata.airtime_dict(full_mutagen) From 5d40bd851d702f1d7bb15c0c35e9b40501e3b5b6 Mon Sep 17 00:00:00 2001 From: Rudi Grinberg Date: Wed, 22 Aug 2012 17:25:19 -0400 Subject: [PATCH 04/10] added upgrade.sql for 2.2.0 upgrade --- install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql diff --git a/install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql b/install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql new file mode 100644 index 000000000..b4fffb97f --- /dev/null +++ b/install_minimal/upgrades/airtime-2.2.0/data/upgrade.sql @@ -0,0 +1,2 @@ +DELETE FROM cc_pref WHERE keystr = 'system_version'; +INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '2.2.0'); From 38173bd3a2a13828ca43911fdd505ea3de723614 Mon Sep 17 00:00:00 2001 From: denise Date: Thu, 23 Aug 2012 10:19:00 -0400 Subject: [PATCH 05/10] - small fix --- airtime_mvc/application/controllers/LibraryController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php index e647f4b30..32db3e06b 100644 --- a/airtime_mvc/application/controllers/LibraryController.php +++ b/airtime_mvc/application/controllers/LibraryController.php @@ -56,6 +56,7 @@ class LibraryController extends Zend_Controller_Action $obj_sess = new Zend_Session_Namespace(UI_PLAYLISTCONTROLLER_OBJ_SESSNAME); if (isset($obj_sess->id)) { + $objInfo = Application_Model_Library::getObjInfo($obj_sess->type); Logging::info($obj_sess->id); Logging::info($obj_sess->type); $objInfo = Application_Model_Library::getObjInfo($obj_sess->type); From de0aeee59493ae0efd94158fc3fbb5edb526f698 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 23 Aug 2012 10:41:40 -0400 Subject: [PATCH 06/10] CC-4254: Library: advanced search on library - initial commit --- .../controllers/PlaylistController.php | 1 + airtime_mvc/application/models/Datatables.php | 62 ++ airtime_mvc/application/models/Preference.php | 4 + .../views/scripts/library/library.phtml | 5 +- .../public/js/airtime/library/library.js | 115 ++- .../plugin/dataTables.columnFilter.js | 733 ++++++++++++++++++ 6 files changed, 900 insertions(+), 20 deletions(-) create mode 100644 airtime_mvc/public/js/datatables/plugin/dataTables.columnFilter.js diff --git a/airtime_mvc/application/controllers/PlaylistController.php b/airtime_mvc/application/controllers/PlaylistController.php index c3d2bfa6e..baa8f2d33 100644 --- a/airtime_mvc/application/controllers/PlaylistController.php +++ b/airtime_mvc/application/controllers/PlaylistController.php @@ -176,6 +176,7 @@ class PlaylistController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColVis.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.columnFilter.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/utilities/utilities.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); diff --git a/airtime_mvc/application/models/Datatables.php b/airtime_mvc/application/models/Datatables.php index 66737b7d9..69e9b7f31 100644 --- a/airtime_mvc/application/models/Datatables.php +++ b/airtime_mvc/application/models/Datatables.php @@ -2,12 +2,74 @@ class Application_Model_Datatables { + private static function buildWhereClauseForAdvancedSearch($dbname2searchTerm) + { + $where = array(); + foreach ($dbname2searchTerm as $dbname=>$term) { + $isRange = false; + if (strstr($term, '~')) { + $info = explode('~', $term); + $input1 = isset($info[0])?$info[0]:null; + $input2 = isset($info[1])?$info[1]:null; + $isRange = true; + } else { + $input1 = $term; + } + + if ($isRange) { + $sub = array(); + if ($input1 != null) { + $sub[] = $dbname." >= '".$input1."'"; + } + if ($input2 != null) { + $sub[] = $dbname." <= '".$input2."'"; + } + if (!empty($sub)) { + $where[] = "(".implode(' AND ', $sub).")"; + } + } else { + if (trim($input1) !== "") { + $where[] = $dbname." ILIKE "."'%".$input1."%'"; + } + } + } + return implode(" AND ", $where); + } /* * query used to return data for a paginated/searchable datatable. */ public static function findEntries($con, $displayColumns, $fromTable, $data, $dataProp = "aaData") { + $librarySetting = Application_Model_Preference::getCurrentLibraryTableSetting(); + + // map that maps original column position to db name + $current2dbname = array(); + // array of search terms + $orig2searchTerm= array(); + foreach ($data as $key=>$d) { + if (strstr($key, "mDataProp_")) { + list($dump, $index) = explode("_", $key); + $current2dbname[$index] = $d; + } else if (strstr($key, "sSearch_")) { + list($dump, $index) = explode("_", $key); + $orig2searchTerm[$index] = $d; + } + } + // map that maps current column position to original position + $current2orig = $librarySetting['ColReorder']; + + // map that maps dbname to searchTerm + $dbname2searchTerm = array(); + foreach ($current2dbname as $currentPos=>$dbname) { + $dbname2searchTerm[$dbname] = $orig2searchTerm[$current2orig[$currentPos]]; + } + $where = array(); + + $advancedWhere = self::buildWhereClauseForAdvancedSearch($dbname2searchTerm); + if ($advancedWhere != "") { + $where[] = $advancedWhere; + } if ($data["sSearch"] !== "") { $searchTerms = explode(" ", $data["sSearch"]); diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php index df3b1fdde..9d98b2134 100644 --- a/airtime_mvc/application/models/Preference.php +++ b/airtime_mvc/application/models/Preference.php @@ -1099,4 +1099,8 @@ class Application_Model_Preference return true; } } + + public static function getCurrentLibraryTableSetting(){ + return unserialize(self::getValue("library_datatable")); + } } diff --git a/airtime_mvc/application/views/scripts/library/library.phtml b/airtime_mvc/application/views/scripts/library/library.phtml index 9bdb1c387..e0d889f76 100644 --- a/airtime_mvc/application/views/scripts/library/library.phtml +++ b/airtime_mvc/application/views/scripts/library/library.phtml @@ -1,2 +1,5 @@ -
\ No newline at end of file + + +
\ No newline at end of file diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index 653d11997..e29976099 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -8,6 +8,35 @@ var AIRTIME = (function(AIRTIME) { chosenItems = {}, visibleChosenItems = {}; + var criteriaTypes = { + 0 : "", + "album_title" : "s", + "artist_name" : "s", + "bit_rate" : "n", + "bpm" : "n", + "comments" : "s", + "composer" : "s", + "conductor" : "s", + "utime" : "n", + "mtime" : "n", + "lptime" : "n", + "disc_number" : "n", + "genre" : "s", + "isrc_number" : "s", + "label" : "s", + "language" : "s", + "length" : "n", + "lyricist" : "s", + "mood" : "s", + "name" : "s", + "orchestra" : "s", + "rating" : "n", + "sample_rate" : "n", + "track_title" : "s", + "track_num" : "n", + "year" : "n" + }; + if (AIRTIME.library === undefined) { AIRTIME.library = {}; } @@ -303,11 +332,57 @@ var AIRTIME = (function(AIRTIME) { $el.removeClass("ui-state-hover"); } }); - + + var colReorderMap = new Array(); + $libTable = $libContent.find("table"); var tableHeight = $libContent.height() - 130; + function setColumnFilter(){ + var aoCols = oTable.fnSettings().aoColumns; + var colsForAdvancedSearch = new Array(); + var advanceSearchDiv = $("div#advanced_search"); + advanceSearchDiv.empty(); + $.each(aoCols, function(i,ele){ + if (ele.bSearchable) { + var currentColId = ele._ColReorder_iOrigCol; + if (ele.bVisible) { + advanceSearchDiv.append("
"+ele.sTitle+" :
"); + } else { + advanceSearchDiv.append(""); + } + if (criteriaTypes[ele.mDataProp] == "s") { + var obj = { sSelector: "#"+ele.mDataProp } + } else { + var obj = { sSelector: "#"+ele.mDataProp, type: "number-range" } + } + colsForAdvancedSearch.push(obj); + } else { + colsForAdvancedSearch.push(null); + } + }); + + oTable.columnFilter({ + aoColumns: colsForAdvancedSearch, + bUseColVis: true, + sPlaceHolder: "head:before" + } + ); + } + + function setFilterElement(iColumn, bVisible){ + var actualId = colReorderMap[iColumn]; + var selector = "div#advanced_search_col_"+actualId; + if (bVisible) { + $(selector).show(); + } else { + $(selector).hide(); + } + } + + var currentColOrder = new Array(); + oTable = $libTable.dataTable( { //put hidden columns at the top to insure they can never be visible on the table through column reordering. @@ -323,19 +398,19 @@ var AIRTIME = (function(AIRTIME) { /* Length */ {"sTitle": "Length", "mDataProp": "length", "sClass": "library_length", "sWidth": "80px"}, /* Upload Time */ {"sTitle": "Uploaded", "mDataProp": "utime", "sClass": "library_upload_time", "sWidth": "125px"}, /* Last Modified */ {"sTitle": "Last Modified", "mDataProp": "mtime", "bVisible": false, "sClass": "library_modified_time", "sWidth": "125px"}, - /* Track Number */ {"sTitle": "Track", "mDataProp": "track_number", "bSearchable": false, "bVisible": false, "sClass": "library_track", "sWidth": "65px"}, - /* Mood */ {"sTitle": "Mood", "mDataProp": "mood", "bSearchable": false, "bVisible": false, "sClass": "library_mood", "sWidth": "70px"}, - /* BPM */ {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm", "sWidth": "50px"}, - /* Composer */ {"sTitle": "Composer", "mDataProp": "composer", "bSearchable": false, "bVisible": false, "sClass": "library_composer", "sWidth": "150px"}, - /* Website */ {"sTitle": "Website", "mDataProp": "info_url", "bSearchable": false, "bVisible": false, "sClass": "library_url", "sWidth": "150px"}, - /* Bit Rate */ {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bSearchable": false, "bVisible": false, "sClass": "library_bitrate", "sWidth": "80px"}, - /* Sample Rate */ {"sTitle": "Sample", "mDataProp": "sample_rate", "bSearchable": false, "bVisible": false, "sClass": "library_sr", "sWidth": "80px"}, - /* ISRC Number */ {"sTitle": "ISRC", "mDataProp": "isrc_number", "bSearchable": false, "bVisible": false, "sClass": "library_isrc", "sWidth": "150px"}, - /* Encoded */ {"sTitle": "Encoded", "mDataProp": "encoded_by", "bSearchable": false, "bVisible": false, "sClass": "library_encoded", "sWidth": "150px"}, - /* Label */ {"sTitle": "Label", "mDataProp": "label", "bSearchable": false, "bVisible": false, "sClass": "library_label", "sWidth": "125px"}, - /* Copyright */ {"sTitle": "Copyright", "mDataProp": "copyright", "bSearchable": false, "bVisible": false, "sClass": "library_copyright", "sWidth": "125px"}, - /* Mime */ {"sTitle": "Mime", "mDataProp": "mime", "bSearchable": false, "bVisible": false, "sClass": "library_mime", "sWidth": "80px"}, - /* Language */ {"sTitle": "Language", "mDataProp": "language", "bSearchable": false, "bVisible": false, "sClass": "library_language", "sWidth": "125px"} + /* Track Number */ {"sTitle": "Track", "mDataProp": "track_number", "bVisible": false, "sClass": "library_track", "sWidth": "65px"}, + /* Mood */ {"sTitle": "Mood", "mDataProp": "mood", "bVisible": false, "sClass": "library_mood", "sWidth": "70px"}, + /* BPM */ {"sTitle": "BPM", "mDataProp": "bpm", "bVisible": false, "sClass": "library_bpm", "sWidth": "50px"}, + /* Composer */ {"sTitle": "Composer", "mDataProp": "composer", "bVisible": false, "sClass": "library_composer", "sWidth": "150px"}, + /* Website */ {"sTitle": "Website", "mDataProp": "info_url", "bVisible": false, "sClass": "library_url", "sWidth": "150px"}, + /* Bit Rate */ {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bVisible": false, "sClass": "library_bitrate", "sWidth": "80px"}, + /* Sample Rate */ {"sTitle": "Sample", "mDataProp": "sample_rate", "bVisible": false, "sClass": "library_sr", "sWidth": "80px"}, + /* ISRC Number */ {"sTitle": "ISRC", "mDataProp": "isrc_number", "bVisible": false, "sClass": "library_isrc", "sWidth": "150px"}, + /* Encoded */ {"sTitle": "Encoded", "mDataProp": "encoded_by", "bVisible": false, "sClass": "library_encoded", "sWidth": "150px"}, + /* Label */ {"sTitle": "Label", "mDataProp": "label", "bVisible": false, "sClass": "library_label", "sWidth": "125px"}, + /* Copyright */ {"sTitle": "Copyright", "mDataProp": "copyright", "bVisible": false, "sClass": "library_copyright", "sWidth": "125px"}, + /* Mime */ {"sTitle": "Mime", "mDataProp": "mime", "bVisible": false, "sClass": "library_mime", "sWidth": "80px"}, + /* Language */ {"sTitle": "Language", "mDataProp": "language", "bVisible": false, "sClass": "library_language", "sWidth": "125px"} ], "bProcessing": true, @@ -350,15 +425,15 @@ var AIRTIME = (function(AIRTIME) { delete oData.aoSearchCols; }, "fnStateSave": function (oSettings, oData) { - localStorage.setItem('datatables-library', JSON.stringify(oData)); - $.ajax({ url: "/usersettings/set-library-datatable", type: "POST", data: {settings : oData, format: "json"}, dataType: "json" }); + + colReorderMap = oData.ColReorder; }, "fnStateLoad": function fnLibStateLoad(oSettings) { var settings = localStorage.getItem('datatables-library'); @@ -371,7 +446,7 @@ var AIRTIME = (function(AIRTIME) { var i, length, a = oData.abVisCols; - + //putting serialized data back into the correct js type to make //sure everything works properly. for (i = 0, length = a.length; i < length; i++) { @@ -386,7 +461,7 @@ var AIRTIME = (function(AIRTIME) { a[i] = parseInt(a[i], 10); } } - + oData.iEnd = parseInt(oData.iEnd, 10); oData.iLength = parseInt(oData.iLength, 10); oData.iStart = parseInt(oData.iStart, 10); @@ -526,7 +601,8 @@ var AIRTIME = (function(AIRTIME) { "oColVis": { "sAlign": "right", "aiExclude": [0, 1, 2], - "sSize": "css" + "sSize": "css", + "fnStateChange": setFilterElement }, "oColReorder": { @@ -534,6 +610,7 @@ var AIRTIME = (function(AIRTIME) { } }); + setColumnFilter(); oTable.fnSetFilteringDelay(350); $libContent.find(".dataTables_scrolling").css("max-height", tableHeight); diff --git a/airtime_mvc/public/js/datatables/plugin/dataTables.columnFilter.js b/airtime_mvc/public/js/datatables/plugin/dataTables.columnFilter.js new file mode 100644 index 000000000..4f36c9a71 --- /dev/null +++ b/airtime_mvc/public/js/datatables/plugin/dataTables.columnFilter.js @@ -0,0 +1,733 @@ +/* +* File: jquery.dataTables.columnFilter.js +* Version: 1.4.8. +* Author: Jovan Popovic +* +* Copyright 2011-2012 Jovan Popovic, all rights reserved. +* +* This source file is free software, under either the GPL v2 license or a +* BSD style license, as supplied with this software. +* +* This source file is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. +* +* Parameters:" +* @sPlaceHolder String Place where inline filtering function should be placed ("tfoot", "thead:before", "thead:after"). Default is "tfoot" +* @sRangeSeparator String Separator that will be used when range values are sent to the server-side. Default value is "~". +* @sRangeFormat string Default format of the From ... to ... range inputs. Default is From {from} to {to} +* @aoColumns Array Array of the filter settings that will be applied on the columns +*/ +(function ($) { + + + $.fn.columnFilter = function (options) { + + var asInitVals, i, label, th; + + //var sTableId = "table"; + var sRangeFormat = "From {from} to {to}"; + //Array of the functions that will override sSearch_ parameters + var afnSearch_ = new Array(); + var aiCustomSearch_Indexes = new Array(); + + var oFunctionTimeout = null; + + var fnOnFiltered = function () { }; + + function _fnGetColumnValues(oSettings, iColumn, bUnique, bFiltered, bIgnoreEmpty) { + /// + ///Return values in the column + /// + ///DataTables settings + ///Id of the column + ///Return only distinct values + ///Return values only from the filtered rows + ///Ignore empty cells + + // check that we have a column id + if (typeof iColumn == "undefined") return new Array(); + + // by default we only wany unique data + if (typeof bUnique == "undefined") bUnique = true; + + // by default we do want to only look at filtered data + if (typeof bFiltered == "undefined") bFiltered = true; + + // by default we do not wany to include empty values + if (typeof bIgnoreEmpty == "undefined") bIgnoreEmpty = true; + + // list of rows which we're going to loop through + var aiRows; + + // use only filtered rows + if (bFiltered == true) aiRows = oSettings.aiDisplay; + // use all rows + else aiRows = oSettings.aiDisplayMaster; // all row numbers + + // set up data array + var asResultData = new Array(); + + for (var i = 0, c = aiRows.length; i < c; i++) { + iRow = aiRows[i]; + var aData = oTable.fnGetData(iRow); + var sValue = aData[iColumn]; + + // ignore empty values? + if (bIgnoreEmpty == true && sValue.length == 0) continue; + + // ignore unique values? + else if (bUnique == true && jQuery.inArray(sValue, asResultData) > -1) continue; + + // else push the value onto the result data array + else asResultData.push(sValue); + } + + return asResultData.sort(); + } + + function _fnColumnIndex(iColumnIndex) { + if (properties.bUseColVis) + return iColumnIndex; + else + return oTable.fnSettings().oApi._fnVisibleToColumnIndex(oTable.fnSettings(), iColumnIndex); + //return iColumnIndex; + //return oTable.fnSettings().oApi._fnColumnIndexToVisible(oTable.fnSettings(), iColumnIndex); + } + + function fnCreateInput(oTable, regex, smart, bIsNumber, iFilterLength, iMaxLenght) { + var sCSSClass = "text_filter"; + if (bIsNumber) + sCSSClass = "number_filter"; + + label = label.replace(/(^\s*)|(\s*$)/g, ""); + var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch; + var search_init = 'search_init '; + var inputvalue = label; + if (currentFilter != '' && currentFilter != '^') { + if (bIsNumber && currentFilter.charAt(0) == '^') + inputvalue = currentFilter.substr(1); //ignore trailing ^ + else + inputvalue = currentFilter; + search_init = ''; + } + + var input = $(''); + if (iMaxLenght != undefined && iMaxLenght != -1) { + input.attr('maxlength', iMaxLenght); + } + th.html(input); + if (bIsNumber) + th.wrapInner(''); + else + th.wrapInner(''); + + asInitVals[i] = label; + var index = i; + + if (bIsNumber && !oTable.fnSettings().oFeatures.bServerSide) { + input.keyup(function () { + /* Filter on the column all numbers that starts with the entered value */ + oTable.fnFilter('^' + this.value, _fnColumnIndex(index), true, false); //Issue 37 + fnOnFiltered(); + }); + } else { + input.keyup(function () { + if (oTable.fnSettings().oFeatures.bServerSide && iFilterLength != 0) { + //If filter length is set in the server-side processing mode + //Check has the user entered at least iFilterLength new characters + + var currentFilter = oTable.fnSettings().aoPreSearchCols[index].sSearch; + var iLastFilterLength = $(this).data("dt-iLastFilterLength"); + if (typeof iLastFilterLength == "undefined") + iLastFilterLength = 0; + var iCurrentFilterLength = this.value.length; + if (Math.abs(iCurrentFilterLength - iLastFilterLength) < iFilterLength + //&& currentFilter.length == 0 //Why this? + ) { + //Cancel the filtering + return; + } + else { + //Remember the current filter length + $(this).data("dt-iLastFilterLength", iCurrentFilterLength); + } + } + /* Filter on the column (the index) of this element */ + oTable.fnFilter(this.value, _fnColumnIndex(index), regex, smart); //Issue 37 + fnOnFiltered(); + }); + } + + input.focus(function () { + if ($(this).hasClass("search_init")) { + $(this).removeClass("search_init"); + this.value = ""; + } + }); + input.blur(function () { + if (this.value == "") { + $(this).addClass("search_init"); + this.value = asInitVals[index]; + } + }); + } + + function fnCreateRangeInput(oTable) { + + //var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch; + th.html(_fnRangeLabelPart(0)); + var sFromId = oTable.attr("id") + '_range_from_' + i; + var from = $(''); + th.append(from); + th.append(_fnRangeLabelPart(1)); + var sToId = oTable.attr("id") + '_range_to_' + i; + var to = $(''); + th.append(to); + th.append(_fnRangeLabelPart(2)); + th.wrapInner(''); + var index = i; + aiCustomSearch_Indexes.push(i); + + + + //------------start range filtering function + + + /* Custom filtering function which will filter data in column four between two values + * Author: Allan Jardine, Modified by Jovan Popovic + */ + //$.fn.dataTableExt.afnFiltering.push( + oTable.dataTableExt.afnFiltering.push( + function (oSettings, aData, iDataIndex) { + if (oTable.attr("id") != oSettings.sTableId) + return true; + // Try to handle missing nodes more gracefully + if (document.getElementById(sFromId) == null) + return true; + var iMin = document.getElementById(sFromId).value * 1; + var iMax = document.getElementById(sToId).value * 1; + var iValue = aData[_fnColumnIndex(index)] == "-" ? 0 : aData[_fnColumnIndex(index)] * 1; + if (iMin == "" && iMax == "") { + return true; + } + else if (iMin == "" && iValue <= iMax) { + return true; + } + else if (iMin <= iValue && "" == iMax) { + return true; + } + else if (iMin <= iValue && iValue <= iMax) { + return true; + } + return false; + } + ); + //------------end range filtering function + + + + $('#' + sFromId + ',#' + sToId, th).keyup(function () { + + var iMin = document.getElementById(sFromId).value * 1; + var iMax = document.getElementById(sToId).value * 1; + if (iMin != 0 && iMax != 0 && iMin > iMax) + return; + + oTable.fnDraw(); + fnOnFiltered(); + }); + + + } + + + function fnCreateDateRangeInput(oTable) { + th.html(_fnRangeLabelPart(0)); + var sFromId = oTable.attr("id") + '_range_from_' + i; + var from = $(''); + from.datepicker(); + th.append(from); + th.append(_fnRangeLabelPart(1)); + var sToId = oTable.attr("id") + '_range_to_' + i; + var to = $(''); + th.append(to); + th.append(_fnRangeLabelPart(2)); + th.wrapInner(''); + to.datepicker(); + var index = i; + aiCustomSearch_Indexes.push(i); + + + //------------start date range filtering function + + //$.fn.dataTableExt.afnFiltering.push( + oTable.dataTableExt.afnFiltering.push( + function (oSettings, aData, iDataIndex) { + if (oTable.attr("id") != oSettings.sTableId) + return true; + + var dStartDate = from.datepicker("getDate"); + + var dEndDate = to.datepicker("getDate"); + + if (dStartDate == null && dEndDate == null) { + return true; + } + + var dCellDate = null; + try { + if (aData[_fnColumnIndex(index)] == null || aData[_fnColumnIndex(index)] == "") + return false; + dCellDate = $.datepicker.parseDate($.datepicker.regional[""].dateFormat, aData[_fnColumnIndex(index)]); + } catch (ex) { + return false; + } + if (dCellDate == null) + return false; + + + if (dStartDate == null && dCellDate <= dEndDate) { + return true; + } + else if (dStartDate <= dCellDate && dEndDate == null) { + return true; + } + else if (dStartDate <= dCellDate && dCellDate <= dEndDate) { + return true; + } + return false; + } + ); + //------------end date range filtering function + + $('#' + sFromId + ',#' + sToId, th).change(function () { + oTable.fnDraw(); + fnOnFiltered(); + }); + + + } + + function fnCreateColumnSelect(oTable, aData, iColumn, nTh, sLabel, bRegex) { + if (aData == null) + aData = _fnGetColumnValues(oTable.fnSettings(), iColumn, true, false, true); + var index = iColumn; + var currentFilter = oTable.fnSettings().aoPreSearchCols[i].sSearch; + + var r = ''); + nTh.html(select); + nTh.wrapInner(''); + select.change(function () { + //var val = $(this).val(); + if ($(this).val() != "") { + $(this).removeClass("search_init"); + } else { + $(this).addClass("search_init"); + } + if (bRegex) + oTable.fnFilter($(this).val(), iColumn, bRegex); //Issue 41 + else + oTable.fnFilter(unescape($(this).val()), iColumn); //Issue 25 + fnOnFiltered(); + }); + } + + function fnCreateSelect(oTable, aData, bRegex) { + var oSettings = oTable.fnSettings(); + if (aData == null && oSettings.sAjaxSource != "" && !oSettings.oFeatures.bServerSide) { + // Add a function to the draw callback, which will check for the Ajax data having + // been loaded. Use a closure for the individual column elements that are used to + // built the column filter, since 'i' and 'th' (etc) are locally "global". + oSettings.aoDrawCallback.push({ + "fn": (function (iColumn, nTh, sLabel) { + return function () { + // Only rebuild the select on the second draw - i.e. when the Ajax + // data has been loaded. + if (oSettings.iDraw == 2 && oSettings.sAjaxSource != null && oSettings.sAjaxSource != "" && !oSettings.oFeatures.bServerSide) { + return fnCreateColumnSelect(oTable, null, _fnColumnIndex(iColumn), nTh, sLabel, bRegex); //Issue 37 + } + }; + })(i, th, label), + "sName": "column_filter_" + i + }); + } + // Regardless of the Ajax state, build the select on first pass + fnCreateColumnSelect(oTable, aData, _fnColumnIndex(i), th, label, bRegex); //Issue 37 + + } + + function fnCreateCheckbox(oTable, aData) { + + if (aData == null) + aData = _fnGetColumnValues(oTable.fnSettings(), i, true, true, true); + var index = i; + + var r = '', j, iLen = aData.length; + + //clean the string + var localLabel = label.replace('%', 'Perc').replace("&", "AND").replace("$", "DOL").replace("£", "STERL").replace("@", "AT").replace(/\s/g, "_"); + localLabel = localLabel.replace(/[^a-zA-Z 0-9]+/g, ''); + //clean the string + + //button label override + if (properties.sFilterButtonText != null || properties.sFilterButtonText != undefined) { + labelBtn = properties.sFilterButtonText; + } else { + labelBtn = label; + } + + var relativeDivWidthToggleSize = 10; + var numRow = 12; //numero di checkbox per colonna + var numCol = Math.floor(iLen / numRow); + if (iLen % numRow > 0) { + numCol = numCol + 1; + }; + + //count how many column should be generated and split the div size + var divWidth = 100 / numCol - 2; + + var divWidthToggle = relativeDivWidthToggleSize * numCol; + + if (numCol == 1) { + divWidth = 20; + } + + var divRowDef = '
'; + var divClose = '
'; + + var uniqueId = oTable.attr("id") + localLabel; + var buttonId = "chkBtnOpen" + uniqueId; + var checkToggleDiv = uniqueId + "-flt-toggle"; + r += ''; //filter button witch open dialog + r += '
'; //dialog div + //r+= '
'; //reset button and its div + r += divRowDef; + + for (j = 0; j < iLen; j++) { + + //if last check close div + if (j % numRow == 0 && j != 0) { + r += divClose + divRowDef; + } + + //check button + r += '' + aData[j] + '
'; + + var checkbox = $(r); + th.html(checkbox); + th.wrapInner(''); + //on every checkbox selection + checkbox.change(function () { + + var search = ''; + var or = '|'; //var for select checks in 'or' into the regex + var resSize = $('input:checkbox[name="' + localLabel + '"]:checked').size(); + $('input:checkbox[name="' + localLabel + '"]:checked').each(function (index) { + + //search = search + ' ' + $(this).val(); + //concatenation for selected checks in or + if ((index == 0 && resSize == 1) + || (index != 0 && index == resSize - 1)) { + or = ''; + } + //trim + search = search.replace(/^\s+|\s+$/g, ""); + search = search + $(this).val() + or; + or = '|'; + + }); + + for (var jj = 0; jj < iLen; jj++) { + if (search != "") { + $('#' + aData[jj]).removeClass("search_init"); + } else { + $('#' + aData[jj]).addClass("search_init"); + } + } + + //execute search + oTable.fnFilter(search, index, true, false); + fnOnFiltered(); + }); + } + + //filter button + $('#' + buttonId).button(); + //dialog + $('#' + checkToggleDiv).dialog({ + //height: 140, + autoOpen: false, + //show: "blind", + hide: "blind", + buttons: [{ + text: "Reset", + click: function () { + //$('#'+buttonId).removeClass("filter_selected"); //LM remove border if filter selected + $('input:checkbox[name="' + localLabel + '"]:checked').each(function (index3) { + $(this).attr('checked', false); + $(this).addClass("search_init"); + }); + oTable.fnFilter('', index, true, false); + fnOnFiltered(); + return false; + } + }, + { + text: "Close", + click: function () { $(this).dialog("close"); } + } + ] + }); + + + $('#' + buttonId).click(function () { + + $('#' + checkToggleDiv).dialog('open'); + var target = $(this); + $('#' + checkToggleDiv).dialog("widget").position({ my: 'top', + at: 'bottom', + of: target + }); + + return false; + }); + + var fnOnFilteredCurrent = fnOnFiltered; + + fnOnFiltered = function () { + var target = $('#' + buttonId); + $('#' + checkToggleDiv).dialog("widget").position({ my: 'top', + at: 'bottom', + of: target + }); + fnOnFilteredCurrent(); + }; + //reset + /* + $('#'+buttonId+"Reset").button(); + $('#'+buttonId+"Reset").click(function(){ + $('#'+buttonId).removeClass("filter_selected"); //LM remove border if filter selected + $('input:checkbox[name="'+localLabel+'"]:checked').each(function(index3) { + $(this).attr('checked', false); + $(this).addClass("search_init"); + }); + oTable.fnFilter('', index, true, false); + return false; + }); + */ + } + + + + + function _fnRangeLabelPart(iPlace) { + switch (iPlace) { + case 0: + return sRangeFormat.substring(0, sRangeFormat.indexOf("{from}")); + case 1: + return sRangeFormat.substring(sRangeFormat.indexOf("{from}") + 6, sRangeFormat.indexOf("{to}")); + default: + return sRangeFormat.substring(sRangeFormat.indexOf("{to}") + 4); + } + } + + + + + oTable = this; + + var defaults = { + sPlaceHolder: "foot", + sRangeSeparator: "~", + iFilteringDelay: 500, + aoColumns: null, + sRangeFormat: "From {from} to {to}" + }; + + properties = $.extend(defaults, options); + + return this.each(function () { + + if (!oTable.fnSettings().oFeatures.bFilter) + return; + asInitVals = new Array(); + + aoFilterCells = oTable.fnSettings().aoFooter[0]; + + var oHost = oTable.fnSettings().nTFoot; //Before fix for ColVis + var sFilterRow = "tr"; //Before fix for ColVis + + if (properties.sPlaceHolder == "head:after") { + var tr = $("tr:first", oTable.fnSettings().nTHead).detach(); + //tr.appendTo($(oTable.fnSettings().nTHead)); + if (oTable.fnSettings().bSortCellsTop) { + tr.prependTo($(oTable.fnSettings().nTHead)); + //tr.appendTo($("thead", oTable)); + aoFilterCells = oTable.fnSettings().aoHeader[1]; + } + else { + tr.appendTo($(oTable.fnSettings().nTHead)); + //tr.prependTo($("thead", oTable)); + aoFilterCells = oTable.fnSettings().aoHeader[0]; + } + + sFilterRow = "tr:last"; + oHost = oTable.fnSettings().nTHead; + + } else if (properties.sPlaceHolder == "head:before") { + + if (oTable.fnSettings().bSortCellsTop) { + var tr = $("tr:first", oTable.fnSettings().nTHead).detach(); + tr.appendTo($(oTable.fnSettings().nTHead)); + aoFilterCells = oTable.fnSettings().aoHeader[1]; + } else { + aoFilterCells = oTable.fnSettings().aoHeader[0]; + } + /*else { + //tr.prependTo($("thead", oTable)); + sFilterRow = "tr:first"; + }*/ + + sFilterRow = "tr:first"; + + oHost = oTable.fnSettings().nTHead; + + + } + + //$(sFilterRow + " th", oHost).each(function (index) {//bug with ColVis + $(aoFilterCells).each(function (index) {//fix for ColVis + i = index; + var aoColumn = { type: "text", + bRegex: false, + bSmart: true, + iMaxLenght: -1, + iFilterLength: 0 + }; + if (properties.aoColumns != null) { + if (properties.aoColumns.length < i || properties.aoColumns[i] == null) + return; + aoColumn = properties.aoColumns[i]; + } + //label = $(this).text(); //Before fix for ColVis + label = $($(this)[0].cell).text(); //Fix for ColVis + if (aoColumn.sSelector == null) { + //th = $($(this)[0]);//Before fix for ColVis + th = $($(this)[0].cell); //Fix for ColVis + } + else { + th = $(aoColumn.sSelector); + if (th.length == 0) + th = $($(this)[0].cell); + } + + if (aoColumn != null) { + if (aoColumn.sRangeFormat != null) + sRangeFormat = aoColumn.sRangeFormat; + else + sRangeFormat = properties.sRangeFormat; + switch (aoColumn.type) { + case "null": + break; + case "number": + fnCreateInput(oTable, true, false, true, aoColumn.iFilterLength, aoColumn.iMaxLenght); + break; + case "select": + if (aoColumn.bRegex != true) + aoColumn.bRegex = false; + fnCreateSelect(oTable, aoColumn.values, aoColumn.bRegex); + break; + case "number-range": + fnCreateRangeInput(oTable); + break; + case "date-range": + fnCreateDateRangeInput(oTable); + break; + case "checkbox": + fnCreateCheckbox(oTable, aoColumn.values); + break; + case "text": + default: + bRegex = (aoColumn.bRegex == null ? false : aoColumn.bRegex); + bSmart = (aoColumn.bSmart == null ? false : aoColumn.bSmart); + fnCreateInput(oTable, bRegex, bSmart, false, aoColumn.iFilterLength, aoColumn.iMaxLenght); + break; + + } + } + }); + + for (j = 0; j < aiCustomSearch_Indexes.length; j++) { + //var index = aiCustomSearch_Indexes[j]; + var fnSearch_ = function () { + var id = oTable.attr("id"); + return $("#" + id + "_range_from_" + aiCustomSearch_Indexes[j]).val() + properties.sRangeSeparator + $("#" + id + "_range_to_" + aiCustomSearch_Indexes[j]).val() + } + afnSearch_.push(fnSearch_); + } + + if (oTable.fnSettings().oFeatures.bServerSide) { + + var fnServerDataOriginal = oTable.fnSettings().fnServerData; + + oTable.fnSettings().fnServerData = function (sSource, aoData, fnCallback) { + + for (j = 0; j < aiCustomSearch_Indexes.length; j++) { + var index = aiCustomSearch_Indexes[j]; + + for (k = 0; k < aoData.length; k++) { + if (aoData[k].name == "sSearch_" + index) + aoData[k].value = afnSearch_[j](); + } + } + aoData.push({ "name": "sRangeSeparator", "value": properties.sRangeSeparator }); + + if (fnServerDataOriginal != null) { + try { + fnServerDataOriginal(sSource, aoData, fnCallback, oTable.fnSettings()); //TODO: See Issue 18 + } catch (ex) { + fnServerDataOriginal(sSource, aoData, fnCallback); + } + } + else { + $.getJSON(sSource, aoData, function (json) { + fnCallback(json) + }); + } + }; + + } + + }); + + }; + + + + +})(jQuery); \ No newline at end of file From a4c579f36e4cd862b7647ca53e9e884ca0c6816b Mon Sep 17 00:00:00 2001 From: James Date: Thu, 23 Aug 2012 10:59:34 -0400 Subject: [PATCH 07/10] CC-4254: Library: advanced search on library - bug fix --- airtime_mvc/application/controllers/LibraryController.php | 1 + airtime_mvc/application/controllers/ScheduleController.php | 1 + airtime_mvc/application/controllers/ShowbuilderController.php | 1 + airtime_mvc/public/js/airtime/library/library.js | 4 ++-- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php index 32db3e06b..d1b48f371 100644 --- a/airtime_mvc/application/controllers/LibraryController.php +++ b/airtime_mvc/application/controllers/LibraryController.php @@ -37,6 +37,7 @@ class LibraryController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColVis.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.columnFilter.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/utilities/utilities.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php index 5494d6d0b..755781e45 100644 --- a/airtime_mvc/application/controllers/ScheduleController.php +++ b/airtime_mvc/application/controllers/ScheduleController.php @@ -74,6 +74,7 @@ class ScheduleController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.TableTools.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.columnFilter.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($this->view->baseUrl('/js/airtime/library/events/library_showbuilder.js?'.$CC_CONFIG['airtime_version']),'text/javascript'); diff --git a/airtime_mvc/application/controllers/ShowbuilderController.php b/airtime_mvc/application/controllers/ShowbuilderController.php index 666a06c2f..6f02a1467 100644 --- a/airtime_mvc/application/controllers/ShowbuilderController.php +++ b/airtime_mvc/application/controllers/ShowbuilderController.php @@ -50,6 +50,7 @@ class ShowbuilderController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColVis.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); + $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.columnFilter.js?'.$CC_CONFIG['airtime_version'], 'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/blockui/jquery.blockUI.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript'); diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index e29976099..baed0bd82 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -339,7 +339,7 @@ var AIRTIME = (function(AIRTIME) { var tableHeight = $libContent.height() - 130; - function setColumnFilter(){ + function setColumnFilter(oTable){ var aoCols = oTable.fnSettings().aoColumns; var colsForAdvancedSearch = new Array(); var advanceSearchDiv = $("div#advanced_search"); @@ -610,7 +610,7 @@ var AIRTIME = (function(AIRTIME) { } }); - setColumnFilter(); + setColumnFilter(oTable); oTable.fnSetFilteringDelay(350); $libContent.find(".dataTables_scrolling").css("max-height", tableHeight); From a94039a0c613630bfa94efde4ee2257813bfeb03 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 23 Aug 2012 11:32:37 -0400 Subject: [PATCH 08/10] CC-4268: Playlist: No "preview" button if a track of the playlist does not exist any more - fixed --- airtime_mvc/application/views/scripts/playlist/update.phtml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/airtime_mvc/application/views/scripts/playlist/update.phtml b/airtime_mvc/application/views/scripts/playlist/update.phtml index a66b9f94a..dc95ac2da 100644 --- a/airtime_mvc/application/views/scripts/playlist/update.phtml +++ b/airtime_mvc/application/views/scripts/playlist/update.phtml @@ -24,6 +24,10 @@ if ($item['type'] == 2) {
">
+ +
+ +
From bf69f2334381d259efcabb039b6b91da0b12d624 Mon Sep 17 00:00:00 2001 From: denise Date: Thu, 23 Aug 2012 11:53:44 -0400 Subject: [PATCH 09/10] CC-4249: Playlist: Playlist's length doesn't update if it contains a smart playlist which the length is changed -fixed --- airtime_mvc/application/models/Block.php | 9 +++++++++ airtime_mvc/application/models/Playlist.php | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/airtime_mvc/application/models/Block.php b/airtime_mvc/application/models/Block.php index 2b88568fa..d7de15ca7 100644 --- a/airtime_mvc/application/models/Block.php +++ b/airtime_mvc/application/models/Block.php @@ -451,6 +451,10 @@ EOT; $this->block->save($this->con); $this->con->commit(); + + //check if block is in any playlists and update the playlist's length + Application_Model_Playlist::updatePlaylistsLengthWithBlock($this->id, $this->getLength()); + } catch (Exception $e) { $this->con->rollback(); throw $e; @@ -562,6 +566,11 @@ EOT; $this->block->save($this->con); $this->con->commit(); + + + //check if block is in any playlists and update the playlist's length + Application_Model_Playlist::updatePlaylistsLengthWithBlock($this->id, $this->getLength()); + } catch (Exception $e) { $this->con->rollback(); throw $e; diff --git a/airtime_mvc/application/models/Playlist.php b/airtime_mvc/application/models/Playlist.php index 5d716b107..17fc1907f 100644 --- a/airtime_mvc/application/models/Playlist.php +++ b/airtime_mvc/application/models/Playlist.php @@ -913,6 +913,26 @@ SQL; { CcPlaylistcontentsQuery::create()->findByDbPlaylistId($this->id)->delete(); } + + /** + * After items get deleted/added from/to a block + * this function updates the length of playlists + * that contain that block + */ + public static function updatePlaylistsLengthWithBlock($p_blockId, $p_blockLength) { + $playlists = CcPlaylistQuery::create()->find(); + foreach ($playlists as $pl) { + $contents = CcPlaylistcontentsQuery::create()->findByDbPlaylistId($pl->getDbId()); + //check if the block is in each playlist + foreach ($contents as $item) { + if ($item->getDbBlockId() == $p_blockId) { + $item->setDbCliplength($p_blockLength); + $con = Propel::getConnection(CcPlaylistPeer::DATABASE_NAME); + $item->save($con); + } + } + } + } } // class Playlist From 9dfbd90e0f28e0b4c2a1bacbbd62de4bd7baa9a0 Mon Sep 17 00:00:00 2001 From: denise Date: Thu, 23 Aug 2012 13:50:46 -0400 Subject: [PATCH 10/10] CC-4270: Media Library: Sample Rate is broken to 2 lines in context view -fixed --- .../views/scripts/library/get-file-metadata.ajax.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/views/scripts/library/get-file-metadata.ajax.phtml b/airtime_mvc/application/views/scripts/library/get-file-metadata.ajax.phtml index 7e4770332..4a7baecad 100644 --- a/airtime_mvc/application/views/scripts/library/get-file-metadata.ajax.phtml +++ b/airtime_mvc/application/views/scripts/library/get-file-metadata.ajax.phtml @@ -5,7 +5,7 @@ Album:md["MDATA_KEY_SOURCE"]);?> Track:md["MDATA_KEY_TRACKNUMBER"]);?> Length:md["MDATA_KEY_DURATION"]);?> -Sample Rate:md["MDATA_KEY_SAMPLERATE"]);?> +Sample Rate:md["MDATA_KEY_SAMPLERATE"]);?> Bit Rate:md["MDATA_KEY_BITRATE"]);?> Mood:md["MDATA_KEY_MOOD"]);?> Genre:md["MDATA_KEY_GENRE"]);?>