diff --git a/airtime_mvc/application/controllers/LocaleController.php b/airtime_mvc/application/controllers/LocaleController.php index dbf33d813..4c08f14b7 100644 --- a/airtime_mvc/application/controllers/LocaleController.php +++ b/airtime_mvc/application/controllers/LocaleController.php @@ -298,6 +298,10 @@ class LocaleController extends Zend_Controller_Action //status/status.js "in use" => _("in use"), "Disk" => _("Disk"), + //serverbrowse/serverbrowse.js + "Look in" => _("Look in"), + "Cancel" => _("Cancel"), + "Open" => _("Open"), //user/user.js "Admin" => _("Admin"), "DJ" => _("DJ"), diff --git a/airtime_mvc/application/forms/SmartBlockCriteria.php b/airtime_mvc/application/forms/SmartBlockCriteria.php index d4763f998..f47117b63 100644 --- a/airtime_mvc/application/forms/SmartBlockCriteria.php +++ b/airtime_mvc/application/forms/SmartBlockCriteria.php @@ -309,7 +309,7 @@ class Application_Form_SmartBlockCriteria extends Zend_Form_SubForm { $data = Application_Model_Block::organizeSmartPlyalistCriteria($params['data']); // add elelments that needs to be added - // set multioption for modifier according to creiteria_field + // set multioption for modifier according to criteria_field $modRowMap = array(); foreach ($data['criteria'] as $critKey=>$d) { $count = 1; diff --git a/airtime_mvc/application/forms/helpers/ValidationTypes.php b/airtime_mvc/application/forms/helpers/ValidationTypes.php index 8a097087b..23aebba4b 100644 --- a/airtime_mvc/application/forms/helpers/ValidationTypes.php +++ b/airtime_mvc/application/forms/helpers/ValidationTypes.php @@ -40,12 +40,12 @@ Class Application_Form_Helper_ValidationTypes { public static function overrideRegexValidator($p_pattern, $p_msg) { $validator = new Zend_Validate_Regex($p_pattern); - + $validator->setMessage( $p_msg, Zend_Validate_Regex::NOT_MATCH ); - + return $validator; } @@ -54,17 +54,17 @@ Class Application_Form_Helper_ValidationTypes { $validator = new Zend_Validate_StringLength(); $validator->setMin($p_min); $validator->setMax($p_max); - + $validator->setMessage( _("'%value%' is less than %min% characters long"), Zend_Validate_StringLength::TOO_SHORT ); - + $validator->setMessage( _("'%value%' is less than %max% characters long"), Zend_Validate_StringLength::TOO_LONG ); - + return $validator; } diff --git a/airtime_mvc/public/js/serverbrowse/AIRTIME_DEV_README b/airtime_mvc/public/js/serverbrowse/AIRTIME_DEV_README new file mode 100644 index 000000000..2597624a7 --- /dev/null +++ b/airtime_mvc/public/js/serverbrowse/AIRTIME_DEV_README @@ -0,0 +1,34 @@ +Before you overwrite serverbrowser.js, note that we have changed a few lines +in this file. + +Running a diff between the original serverbrowser.js and our modified one: + +denise@denise-DX4860:~/airtime/airtime_mvc/public/js/serverbrowse$ diff -u serverbrowser_orig.js serverbrowser.js +--- serverbrowser_orig.js 2012-11-28 11:42:43.250237696 -0500 ++++ serverbrowser.js 2012-11-28 11:44:57.738242930 -0500 +@@ -65,14 +65,14 @@ + modal: true, + buttons: [ + { +- text: "Cancel", ++ text: $.i18n._("Cancel"), + "class": "btn", + click: function() { + browserDlg.dialog("close"); + } + }, + { +- text: "Open", ++ text: $.i18n._("Open"), + "class": "btn", + click: function() { + doneOk(); +@@ -123,7 +123,7 @@ + function() { $(this).removeClass('ui-state-hover'); } + ); + +- var enterLabel = $('').text('Look in: ').appendTo(enterButton.clone(false).appendTo(enterPathDiv)); ++ var enterLabel = $('').text($.i18n._('Look in')+': ').appendTo(enterButton.clone(false).appendTo(enterPathDiv)); + + var enterText = $('').keypress(function(e) { + if (e.keyCode == '13') { diff --git a/airtime_mvc/public/js/serverbrowse/serverbrowser.js b/airtime_mvc/public/js/serverbrowse/serverbrowser.js index 867e70795..5e6bb41e1 100644 --- a/airtime_mvc/public/js/serverbrowse/serverbrowser.js +++ b/airtime_mvc/public/js/serverbrowse/serverbrowser.js @@ -65,14 +65,14 @@ modal: true, buttons: [ { - text: "Cancel", + text: $.i18n._("Cancel"), "class": "btn", click: function() { browserDlg.dialog("close"); } }, { - text: "Open", + text: $.i18n._("Open"), "class": "btn", click: function() { doneOk(); @@ -123,7 +123,7 @@ function() { $(this).removeClass('ui-state-hover'); } ); - var enterLabel = $('').text('Look in: ').appendTo(enterButton.clone(false).appendTo(enterPathDiv)); + var enterLabel = $('').text($.i18n._('Look in')+': ').appendTo(enterButton.clone(false).appendTo(enterPathDiv)); var enterText = $('').keypress(function(e) { if (e.keyCode == '13') { diff --git a/airtime_mvc/public/js/serverbrowse/serverbrowser_orig.js b/airtime_mvc/public/js/serverbrowse/serverbrowser_orig.js new file mode 100644 index 000000000..867e70795 --- /dev/null +++ b/airtime_mvc/public/js/serverbrowse/serverbrowser_orig.js @@ -0,0 +1,378 @@ +/* + author: ApmeM (artem.votincev@gmail.com) + date: 9-June-2010 + version: 1.4 + download: http://code.google.com/p/jq-serverbrowse/ +*/ + +(function($) { + $.fn.serverBrowser = function(settings) { + this.each(function() { + + var config = { +// Event function +// Appear when user click 'Ok' button, or doubleclick on file + onSelect: function(file) { + alert('You select: ' + file); + }, + onLoad: function() { + return config.basePath; + }, + multiselect: false, +// Image parameters +// System images (loading.gif, unknown.png, folder.png and images from knownPaths) will be referenced to systemImageUrl +// if systemImageUrl is empty or not specified - imageUrl will be taken +// All other images (like images for extension) will be taken from imageUrl + imageUrl: 'img/', + systemImageUrl: '', + showUpInList: false, +// Path properties +// Base path, that links should start from. +// If opened path is not under this path, alert will be shown and nothing will be opened +// Path separator, that will be used to split specified paths and join paths to a string + basePath: 'C:', + separatorPath: '/', +// Paths, that will be displayed on the left side of the dialog +// This is a link to specified paths on the server + useKnownPaths: true, + knownPaths: [{text:'Desktop', image:'desktop.png', path:'C:/Users/All Users/Desktop'}, + {text:'Documents', image:'documents.png', path:'C:/Users/All Users/Documents'}], +// Images for known extension (like 'png', 'exe', 'zip'), that will be displayed with its real names +// Images, that is not in this list will be referenced to 'unknown.png' image +// If list is empty - all images is known. + knownExt: [], +// Server path to this plugin handler + handlerUrl: 'browserDlg.txt', +// JQuery-ui dialog settings + title: 'Browse', + width: 300, + height: 300, + position: ['center', 'top'], + +// Administrative parameters used to +// help programmer or system administrator + requestMethod: 'POST', + }; + + if (settings) $.extend(config, settings); +// Required configuration elements +// We need to set some configuration elements without user +// For example there should be 2 buttons on the bottom, +// And dialog should be opened after button is pressed, not when it created +// Also we need to know about dialog resizing + $.extend(config, { + autoOpen: false, + modal: true, + buttons: [ + { + text: "Cancel", + "class": "btn", + click: function() { + browserDlg.dialog("close"); + } + }, + { + text: "Open", + "class": "btn", + click: function() { + doneOk(); + } + } + ], + resize: function(event, ui) { + recalculateSize(event, ui); + }, + }); + + function systemImageUrl() + { + if (config.systemImageUrl.length == 0) { + return config.imageUrl; + } else{ + return config.systemImageUrl; + } + } + + var privateConfig = { +// This stack array will store history navigation data +// When user open new directory, old directory will be added to this list +// If user want, he will be able to move back by this history + browserHistory: [], + +// This array contains all currently selected items +// When user select element, it will add associated path into this array +// When user deselect element - associated path will be removed +// Exception: if 'config.multiselect' is false, only one element will be stored in this array. + selectedItems: [], + } + +// Main dialog div +// It will be converted into jQuery-ui dialog box using my configuration parameters +// It contains 3 divs + var browserDlg = $('
').css({'overflow': 'hidden'}).appendTo(document.body); + browserDlg.dialog(config); + +// First div on the top +// It contains textbox field and buttons +// User can enter any paths he want to open in this textbox and press enter +// There is 3 buttons on the panel: + var enterPathDiv = $('
').addClass('ui-widget-content').appendTo(browserDlg).css({'height': '30px', 'width': '100%', 'padding-top': '7px'}); + + var enterButton = $('
').css({'float': 'left', 'vertical-align': 'middle', 'margin-left': '6px'}).addClass('ui-corner-all').hover( + function() { $(this).addClass('ui-state-hover'); }, + function() { $(this).removeClass('ui-state-hover'); } + ); + + var enterLabel = $('').text('Look in: ').appendTo(enterButton.clone(false).appendTo(enterPathDiv)); + + var enterText = $('').keypress(function(e) { + if (e.keyCode == '13') { + e.preventDefault(); + loadPath(enterText.val()); + } + }).appendTo(enterButton.clone(false).appendTo(enterPathDiv)); + + +// Back button. +// When user click on it, 2 last elements of the history pop from the list, and reload second of them. + var enterBack = $('
').addClass('ui-corner-all ui-icon ui-icon-circle-arrow-w').click(function(){ + privateConfig.browserHistory.pop(); // Remove current element. It is not required now. + var backPath = config.basePath; + if(privateConfig.browserHistory.length > 0){ + backPath = privateConfig.browserHistory.pop(); + } + loadPath(backPath); + }).appendTo(enterButton.clone(true).appendTo(enterPathDiv)); + +// Level Up Button +// When user click on it, last element of the history will be taken, and '..' will be applied to the end of the array. + var enterUp = $('
').addClass('ui-corner-all ui-icon ui-icon-arrowreturnthick-1-n').click(function(){ + backPath = privateConfig.browserHistory[privateConfig.browserHistory.length - 1]; + if(backPath != config.basePath){ + loadPath(backPath + config.separatorPath + '..'); + } + }).appendTo(enterButton.clone(true).appendTo(enterPathDiv)); + +// Second div is on the left +// It contains images and texts for pre-defined paths +// User just click on them and it will open pre-defined path + var knownPathDiv = $('
').addClass('ui-widget-content').css({'text-align':'center', 'overflow': 'auto', 'float': 'left', 'width': '100px'}); + if(config.useKnownPaths){ + knownPathDiv.appendTo(browserDlg); + $.each(config.knownPaths, function(index, path) { + var knownDiv = $('
').css({'margin':'10px'}).hover( + function() { $(this).addClass('ui-state-hover'); }, + function() { $(this).removeClass('ui-state-hover'); } + ).click(function() { + loadPath(path.path); + }).appendTo(knownPathDiv); + + $('').attr({ src: systemImageUrl() + config.separatorPath + path.image }).css({ width: '32px', margin: '5px 10px 5px 5px' }).appendTo(knownDiv); + $('
').appendTo(knownDiv); + $('').text(path.text).appendTo(knownDiv); + }); + } + +// Third div is everywhere :) +// It show files and folders in the current path +// User can click on path to select or deselect it +// Doubleclick on path will open it +// Also doubleclick on file will select this file and close dialog + var browserPathDiv = $('
').addClass('ui-widget-content').css({'float': 'right', 'overflow': 'auto'}).appendTo(browserDlg); + +// Now everything is done +// When user will be ready - he just click on the area you select for this plugin and dialog will appear + $(this).click(function() { + privateConfig.browserHistory = []; + var startpath = removeBackPath(config.onLoad()); + + startpath = startpath.split(config.separatorPath); + startpath.pop(); + startpath = startpath.join(config.separatorPath); + + if(!checkBasePath(startpath)){ + startpath = config.basePath; + } + loadPath(startpath); + browserDlg.dialog('open'); + recalculateSize(); + }); + +// Function check if specified path is a child path of a 'config.basePath' +// If it is not - user should see message, that path invalid, or path should be changed to valid. + function checkBasePath(path){ + if(config.basePath == '') + return true; + var confPath = config.basePath.split(config.separatorPath); + var curPath = path.split(config.separatorPath); + if(confPath.length > curPath.length) + return false; + var result = true; + $.each(confPath, function(index, partConfPath) { + if(partConfPath != curPath[index]){ + result = false; + } + }); + return result; + } + +// Function remove '..' parts of the path +// Process depend on config.separatorPath option +// On the server side you need to check / or \ separators + function removeBackPath(path){ + var confPath = config.basePath.split(config.separatorPath); + var curPath = path.split(config.separatorPath); + var newcurPath = []; + $.each(curPath, function(index, partCurPath) { + if(partCurPath == ".."){ + newcurPath.pop(); + }else{ + newcurPath.push(partCurPath); + } + }); + return newcurPath.join(config.separatorPath); + } + +// This function will be called when user click 'Open' +// It check if any path is selected, and call config.onSelect function with path list + function doneOk(){ + var newCurPath = []; + $.each(privateConfig.selectedItems, function(index, item) { + newCurPath.push($.data(item, 'path')); + }); + if(newCurPath.length == 0) { + newCurPath.push(privateConfig.browserHistory.pop()); + } + + if(config.multiselect) + config.onSelect(newCurPath); + else { + if(newCurPath.length == 1) { + config.onSelect(newCurPath[0]); + } else if(newCurPath.length > 1){ + alert('Plugin work incorrectly. If error repeat, please add issue into http://code.google.com/p/jq-serverbrowse/issues/list with steps to reproduce.'); + return; + } + } + browserDlg.dialog("close"); + } + +// Function recalculate and set new width and height for left and right div elements +// height have '-2' because of the borders +// width have '-4' because of a border an 2 pixels space between divs + function recalculateSize(event, ui){ + knownPathDiv.css({'height' : browserDlg.height() - enterPathDiv.outerHeight(true) - 2}); + browserPathDiv.css({'height' : browserDlg.height() - enterPathDiv.outerHeight(true) - 2, + 'width' : browserDlg.width() - knownPathDiv.outerWidth(true) - 4}); + } + +// Function adds new element into browserPathDiv element depends on file parameters +// If file.isError is set, error message will be displayed instead of clickable area +// Clickable div contain image from extension and text from file parameter + function addElement(file){ + var itemDiv = $('
').css({ margin: '2px' }).appendTo(browserPathDiv); + if(file.isError) + { + itemDiv.addClass('ui-state-error ui-corner-all').css({padding: '0pt 0.7em'}); + var p = $('

').appendTo(itemDiv); + $('').addClass('ui-icon ui-icon-alert').css({'float': 'left', 'margin-right': '0.3em'}).appendTo(p); + $('').text(file.name).appendTo(p); + }else + { + var fullPath = file.path + config.separatorPath + file.name; + itemDiv.hover( + function() { $(this).addClass('ui-state-hover'); }, + function() { $(this).removeClass('ui-state-hover'); } + ); + var itemImage = $('').css({ width: '16px', margin: '0 5px 0 0' }).appendTo(itemDiv); + var itemText = $('').text(file.name).appendTo(itemDiv); + if (file.isFolder) + itemImage.attr({ src: systemImageUrl() + 'folder.png' }); + else { + ext = file.name.split('.').pop(); + var res = ''; + if (ext == '' || ext == file.name || (config.knownExt.length > 0 && $.inArray(ext, config.knownExt) < 0)) + itemImage.attr({ src: systemImageUrl() + 'unknown.png' }); + else + itemImage.attr({ src: config.imageUrl + ext + '.png' }); + } + $.data(itemDiv, 'path', fullPath); + itemDiv.unbind('click').bind('click', function(e) { + if(!$(this).hasClass('ui-state-active')) { + if(!config.multiselect && privateConfig.selectedItems.length > 0) { + $(privateConfig.selectedItems[0]).click(); + } + privateConfig.selectedItems.push(itemDiv); + }else{ + var newCurPath = []; + $.each(privateConfig.selectedItems, function(index, item) { + if($.data(item, 'path') != fullPath) + newCurPath.push(item); + }); + privateConfig.selectedItems = newCurPath; + } + $(this).toggleClass('ui-state-active'); + }); + + itemDiv.unbind('dblclick').bind('dblclick', function(e) { + if (file.isFolder){ + loadPath(fullPath); + } else { + privateConfig.selectedItems = [itemDiv]; + doneOk(); + } + }); + } + } + +// Main plugin function +// When user enter path manually, select it from pre-defined path, or doubleclick in browser this function will call +// It send a request on the server to retrieve child directories and files of the specified path +// If path is not under 'config.basePath', alert will be shown and nothing will be opened + function loadPath(path) { + privateConfig.selectedItems = []; + + // First we need to remove all '..' parts of the path + path = removeBackPath(path); + + // Then we need to check, if path based on 'config.basePath' + if(!checkBasePath(path)) { + alert('Path should be based from ' + config.basePath); + return; + } + + // Then we can put this path into history + privateConfig.browserHistory.push(path); + + // Show it to user + enterText.val(path); + + // And load + $.ajax({ + url: config.handlerUrl, + type: config.requestMethod, + data: { + action: 'browse', + path: path, + time: new Date().getTime() + }, + beforeSend: function() { + browserPathDiv.empty().css({ 'text-align': 'center' }); + $('').attr({ src: systemImageUrl() + 'loading.gif' }).css({ width: '32px' }).appendTo(browserPathDiv); + }, + success: function(files) { + browserPathDiv.empty().css({ 'text-align': 'left' }); + if(path != config.basePath && config.showUpInList){ + addElement({name: '..', isFolder: true, isError: false, path: path}); + } + $.each(files, function(index, file) { + addElement($.extend(file, {path: path})); + }); + }, + dataType: 'json' + }); + } + }); + return this; + }; +})(jQuery);