diff --git a/ChangeLog b/ChangeLog index 633d481373..e6c1bc4605 100644 --- a/ChangeLog +++ b/ChangeLog @@ -43,6 +43,7 @@ phpMyAdmin - ChangeLog + AJAX for table Operations copy table - bug #3380946 [export] no uid Query result export (Suhosin limit) + Grid editing in browse mode (replaces row inline edit) ++ Zoom-search in table Search 3.4.5.0 (not yet released) - bug #3375325 [interface] Page list in navigation frame looks odd diff --git a/config.sample.inc.php b/config.sample.inc.php index edffd03345..778785e1f9 100644 --- a/config.sample.inc.php +++ b/config.sample.inc.php @@ -14,7 +14,7 @@ * This is needed for cookie based authentication to encrypt password in * cookie */ -$cfg['blowfish_secret'] = ''; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */ +$cfg['blowfish_secret'] = 'a8b7c6d'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */ /* * Servers configuration diff --git a/db_structure.php b/db_structure.php index d76bcca9d9..9c2e045755 100644 --- a/db_structure.php +++ b/db_structure.php @@ -57,6 +57,7 @@ require_once './libraries/db_structure.lib.php'; $titles = PMA_buildActionTitles(); // 1. No tables + if ($num_tables == 0) { echo '

' . __('No tables found in database') . '

' . "\n"; diff --git a/js/canvg/canvg.js b/js/canvg/canvg.js index 19a47a7adf..dbc5a9dd01 100644 --- a/js/canvg/canvg.js +++ b/js/canvg/canvg.js @@ -12,6 +12,7 @@ if(!window.console) { window.console.dir = function(str) {}; } +// <3 IE if(!Array.indexOf){ Array.prototype.indexOf = function(obj){ for(var i=0; i ignore mouse events // ignoreAnimation: true => ignore animations @@ -74,11 +75,7 @@ if(!Array.indexOf){ svg.opts = opts; var ctx = target.getContext('2d'); - if (typeof(s.documentElement) != 'undefined') { - // load from xml doc - svg.loadXmlDoc(ctx, s); - } - else if (s.substr(0,1) == '<') { + if (s.substr(0,1) == '<') { // load from xml string svg.loadXml(ctx, s); } @@ -92,7 +89,6 @@ if(!Array.indexOf){ var svg = { }; svg.FRAMERATE = 30; - svg.MAX_VIRTUAL_PIXELS = 30000; // globals svg.init = function(ctx) { @@ -103,7 +99,6 @@ if(!Array.indexOf){ svg.ctx = ctx; svg.ViewPort = new (function () { this.viewPorts = []; - this.Clear = function() { this.viewPorts = []; } this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); } this.RemoveCurrent = function() { this.viewPorts.pop(); } this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; } @@ -556,7 +551,7 @@ if(!Array.indexOf){ } } - var data = svg.trim(svg.compressSpaces(v)).split(/\s(?=[a-z])/); + var data = v.split(/\s(?=[a-z])/); for (var i=0; i0 && text[i-1]!=' ' && i0 && text[i-1]!=' ' && (i == text.length-1 || text[i+1]==' ')) arabicForm = 'initial'; - if (typeof(font.glyphs[c]) != 'undefined') { - glyph = font.glyphs[c][arabicForm]; - if (glyph == null && font.glyphs[c].type == 'glyph') glyph = font.glyphs[c]; - } - } - else { - glyph = font.glyphs[c]; - } - if (glyph == null) glyph = font.missingGlyph; - return glyph; - } - this.renderChildren = function(ctx) { - var customFont = this.parent.style('font-family').Definition.getDefinition(); - if (customFont != null) { - var fontSize = this.parent.style('font-size').numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize); - var fontStyle = this.parent.style('font-style').valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle); - var text = this.getText(); - if (customFont.isRTL) text = text.split("").reverse().join(""); - - if (this.parent.style('text-anchor').value == 'middle') { - this.x = this.x - this.measureText(ctx) / 2.0; - } - - var dx = svg.ToNumberArray(this.parent.attribute('dx').value); - for (var i=0; i 0 ? node.childNodes[0].nodeValue : // element - node.text; + // TEXT ELEMENT + this.text = node.nodeType == 3 ? node.nodeValue : node.childNodes[0].nodeValue; this.getText = function() { return this.text; } @@ -2095,9 +1931,8 @@ if(!Array.indexOf){ this.base = svg.Element.ElementBase; this.base(node); - // text, or spaces then CDATA - var css = node.childNodes[0].nodeValue + (node.childNodes.length > 1 ? node.childNodes[1].nodeValue : ''); - css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ''); // remove comments + var css = node.childNodes[0].nodeValue; + css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(\/\/.*)/gm, ''); // remove comments css = svg.compressSpaces(css); // replace whitespace var cssDefs = css.split('}'); for (var i=0; i 0) { - var urlStart = srcs[s].indexOf('url'); - var urlEnd = srcs[s].indexOf(')', urlStart); - var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6); - var doc = svg.parseXml(svg.ajax(url)); - var fonts = doc.getElementsByTagName('font'); - for (var f=0; f + * @link http://www.phpied.com/rgb-color-parser-in-javascript/ + * @license Use it if you like it + */ +function RGBColor(color_string) +{ + this.ok = false; + + // strip any leading # + if (color_string.charAt(0) == '#') { // remove # if any + color_string = color_string.substr(1,6); + } + + color_string = color_string.replace(/ /g,''); + color_string = color_string.toLowerCase(); + + // before getting into regexps, try simple matches + // and overwrite the input + var simple_colors = { + aliceblue: 'f0f8ff', + antiquewhite: 'faebd7', + aqua: '00ffff', + aquamarine: '7fffd4', + azure: 'f0ffff', + beige: 'f5f5dc', + bisque: 'ffe4c4', + black: '000000', + blanchedalmond: 'ffebcd', + blue: '0000ff', + blueviolet: '8a2be2', + brown: 'a52a2a', + burlywood: 'deb887', + cadetblue: '5f9ea0', + chartreuse: '7fff00', + chocolate: 'd2691e', + coral: 'ff7f50', + cornflowerblue: '6495ed', + cornsilk: 'fff8dc', + crimson: 'dc143c', + cyan: '00ffff', + darkblue: '00008b', + darkcyan: '008b8b', + darkgoldenrod: 'b8860b', + darkgray: 'a9a9a9', + darkgreen: '006400', + darkkhaki: 'bdb76b', + darkmagenta: '8b008b', + darkolivegreen: '556b2f', + darkorange: 'ff8c00', + darkorchid: '9932cc', + darkred: '8b0000', + darksalmon: 'e9967a', + darkseagreen: '8fbc8f', + darkslateblue: '483d8b', + darkslategray: '2f4f4f', + darkturquoise: '00ced1', + darkviolet: '9400d3', + deeppink: 'ff1493', + deepskyblue: '00bfff', + dimgray: '696969', + dodgerblue: '1e90ff', + feldspar: 'd19275', + firebrick: 'b22222', + floralwhite: 'fffaf0', + forestgreen: '228b22', + fuchsia: 'ff00ff', + gainsboro: 'dcdcdc', + ghostwhite: 'f8f8ff', + gold: 'ffd700', + goldenrod: 'daa520', + gray: '808080', + green: '008000', + greenyellow: 'adff2f', + honeydew: 'f0fff0', + hotpink: 'ff69b4', + indianred : 'cd5c5c', + indigo : '4b0082', + ivory: 'fffff0', + khaki: 'f0e68c', + lavender: 'e6e6fa', + lavenderblush: 'fff0f5', + lawngreen: '7cfc00', + lemonchiffon: 'fffacd', + lightblue: 'add8e6', + lightcoral: 'f08080', + lightcyan: 'e0ffff', + lightgoldenrodyellow: 'fafad2', + lightgrey: 'd3d3d3', + lightgreen: '90ee90', + lightpink: 'ffb6c1', + lightsalmon: 'ffa07a', + lightseagreen: '20b2aa', + lightskyblue: '87cefa', + lightslateblue: '8470ff', + lightslategray: '778899', + lightsteelblue: 'b0c4de', + lightyellow: 'ffffe0', + lime: '00ff00', + limegreen: '32cd32', + linen: 'faf0e6', + magenta: 'ff00ff', + maroon: '800000', + mediumaquamarine: '66cdaa', + mediumblue: '0000cd', + mediumorchid: 'ba55d3', + mediumpurple: '9370d8', + mediumseagreen: '3cb371', + mediumslateblue: '7b68ee', + mediumspringgreen: '00fa9a', + mediumturquoise: '48d1cc', + mediumvioletred: 'c71585', + midnightblue: '191970', + mintcream: 'f5fffa', + mistyrose: 'ffe4e1', + moccasin: 'ffe4b5', + navajowhite: 'ffdead', + navy: '000080', + oldlace: 'fdf5e6', + olive: '808000', + olivedrab: '6b8e23', + orange: 'ffa500', + orangered: 'ff4500', + orchid: 'da70d6', + palegoldenrod: 'eee8aa', + palegreen: '98fb98', + paleturquoise: 'afeeee', + palevioletred: 'd87093', + papayawhip: 'ffefd5', + peachpuff: 'ffdab9', + peru: 'cd853f', + pink: 'ffc0cb', + plum: 'dda0dd', + powderblue: 'b0e0e6', + purple: '800080', + red: 'ff0000', + rosybrown: 'bc8f8f', + royalblue: '4169e1', + saddlebrown: '8b4513', + salmon: 'fa8072', + sandybrown: 'f4a460', + seagreen: '2e8b57', + seashell: 'fff5ee', + sienna: 'a0522d', + silver: 'c0c0c0', + skyblue: '87ceeb', + slateblue: '6a5acd', + slategray: '708090', + snow: 'fffafa', + springgreen: '00ff7f', + steelblue: '4682b4', + tan: 'd2b48c', + teal: '008080', + thistle: 'd8bfd8', + tomato: 'ff6347', + turquoise: '40e0d0', + violet: 'ee82ee', + violetred: 'd02090', + wheat: 'f5deb3', + white: 'ffffff', + whitesmoke: 'f5f5f5', + yellow: 'ffff00', + yellowgreen: '9acd32' + }; + for (var key in simple_colors) { + if (color_string == key) { + color_string = simple_colors[key]; + } + } + // emd of simple type-in colors + + // array of color definition objects + var color_defs = [ + { + re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, + example: ['rgb(123, 234, 45)', 'rgb(255,234,245)'], + process: function (bits){ + return [ + parseInt(bits[1]), + parseInt(bits[2]), + parseInt(bits[3]) + ]; + } + }, + { + re: /^(\w{2})(\w{2})(\w{2})$/, + example: ['#00ff00', '336699'], + process: function (bits){ + return [ + parseInt(bits[1], 16), + parseInt(bits[2], 16), + parseInt(bits[3], 16) + ]; + } + }, + { + re: /^(\w{1})(\w{1})(\w{1})$/, + example: ['#fb0', 'f0f'], + process: function (bits){ + return [ + parseInt(bits[1] + bits[1], 16), + parseInt(bits[2] + bits[2], 16), + parseInt(bits[3] + bits[3], 16) + ]; + } + } + ]; + + // search through the definitions to find a match + for (var i = 0; i < color_defs.length; i++) { + var re = color_defs[i].re; + var processor = color_defs[i].process; + var bits = re.exec(color_string); + if (bits) { + channels = processor(bits); + this.r = channels[0]; + this.g = channels[1]; + this.b = channels[2]; + this.ok = true; + } + + } + + // validate/cleanup values + this.r = (this.r < 0 || isNaN(this.r)) ? 0 : ((this.r > 255) ? 255 : this.r); + this.g = (this.g < 0 || isNaN(this.g)) ? 0 : ((this.g > 255) ? 255 : this.g); + this.b = (this.b < 0 || isNaN(this.b)) ? 0 : ((this.b > 255) ? 255 : this.b); + + // some getters + this.toRGB = function () { + return 'rgb(' + this.r + ', ' + this.g + ', ' + this.b + ')'; + } + this.toHex = function () { + var r = this.r.toString(16); + var g = this.g.toString(16); + var b = this.b.toString(16); + if (r.length == 1) r = '0' + r; + if (g.length == 1) g = '0' + g; + if (b.length == 1) b = '0' + b; + return '#' + r + g + b; + } + + // help + this.getHelpXML = function () { + + var examples = new Array(); + // add regexps + for (var i = 0; i < color_defs.length; i++) { + var example = color_defs[i].example; + for (var j = 0; j < example.length; j++) { + examples[examples.length] = example[j]; + } + } + // add type-in colors + for (var sc in simple_colors) { + examples[examples.length] = sc; + } + + var xml = document.createElement('ul'); + xml.setAttribute('id', 'rgbcolor-examples'); + for (var i = 0; i < examples.length; i++) { + try { + var list_item = document.createElement('li'); + var list_color = new RGBColor(examples[i]); + var example_div = document.createElement('div'); + example_div.style.cssText = + 'margin: 3px; ' + + 'border: 1px solid black; ' + + 'background:' + list_color.toHex() + '; ' + + 'color:' + list_color.toHex() + ; + example_div.appendChild(document.createTextNode('test')); + var list_item_value = document.createTextNode( + ' ' + examples[i] + ' -> ' + list_color.toRGB() + ' -> ' + list_color.toHex() + ); + list_item.appendChild(example_div); + list_item.appendChild(list_item_value); + xml.appendChild(list_item); + + } catch(e){} + } + return xml; + + } + +} + diff --git a/js/date.js b/js/date.js new file mode 100644 index 0000000000..9a923408ad --- /dev/null +++ b/js/date.js @@ -0,0 +1,335 @@ +// =================================================================== +// Author: Matt Kruse +// WWW: http://www.mattkruse.com/ +// +// NOTICE: You may use this code for any purpose, commercial or +// private, without any further permission from the author. You may +// remove this notice from your final code if you wish, however it is +// appreciated by the author if at least my web site address is kept. +// +// You may *NOT* re-distribute this code in any way except through its +// use. That means, you can include it in your product, or your web +// site, or any other form where the code is actually being used. You +// may not put the plain javascript up on your site for download or +// include it in your javascript libraries for download. +// If you wish to share this code with others, please just point them +// to the URL instead. +// Please DO NOT link directly to my .js files from your site. Copy +// the files to your server and use them there. Thank you. +// =================================================================== + +// HISTORY +// ------------------------------------------------------------------ +// May 17, 2003: Fixed bug in parseDate() for dates <1970 +// March 11, 2003: Added parseDate() function +// March 11, 2003: Added "NNN" formatting option. Doesn't match up +// perfectly with SimpleDateFormat formats, but +// backwards-compatability was required. + +// ------------------------------------------------------------------ +// These functions use the same 'format' strings as the +// java.text.SimpleDateFormat class, with minor exceptions. +// The format string consists of the following abbreviations: +// +// Field | Full Form | Short Form +// -------------+--------------------+----------------------- +// Year | yyyy (4 digits) | yy (2 digits), y (2 or 4 digits) +// Month | MMM (name or abbr.)| MM (2 digits), M (1 or 2 digits) +// | NNN (abbr.) | +// Day of Month | dd (2 digits) | d (1 or 2 digits) +// Day of Week | EE (name) | E (abbr) +// Hour (1-12) | hh (2 digits) | h (1 or 2 digits) +// Hour (0-23) | HH (2 digits) | H (1 or 2 digits) +// Hour (0-11) | KK (2 digits) | K (1 or 2 digits) +// Hour (1-24) | kk (2 digits) | k (1 or 2 digits) +// Minute | mm (2 digits) | m (1 or 2 digits) +// Second | ss (2 digits) | s (1 or 2 digits) +// AM/PM | a | +// +// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm! +// Examples: +// "MMM d, y" matches: January 01, 2000 +// Dec 1, 1900 +// Nov 20, 00 +// "M/d/yy" matches: 01/20/00 +// 9/2/00 +// "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM" +// ------------------------------------------------------------------ + +var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); +var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat'); +function LZ(x) {return(x<0||x>9?"":"0")+x} + +// ------------------------------------------------------------------ +// isDate ( date_string, format_string ) +// Returns true if date string matches format of format string and +// is a valid date. Else returns false. +// It is recommended that you trim whitespace around the value before +// passing it to this function, as whitespace is NOT ignored! +// ------------------------------------------------------------------ +function isDate(val,format) { + var date=getDateFromFormat(val,format); + if (date==0) { return false; } + return true; + } + +// ------------------------------------------------------------------- +// compareDates(date1,date1format,date2,date2format) +// Compare two date strings to see which is greater. +// Returns: +// 1 if date1 is greater than date2 +// 0 if date2 is greater than date1 of if they are the same +// -1 if either of the dates is in an invalid format +// ------------------------------------------------------------------- +function compareDates(date1,dateformat1,date2,dateformat2) { + var d1=getDateFromFormat(date1,dateformat1); + var d2=getDateFromFormat(date2,dateformat2); + if (d1==0 || d2==0) { + return -1; + } + else if (d1 > d2) { + return 1; + } + return 0; + } + +// ------------------------------------------------------------------ +// formatDate (date_object, format) +// Returns a date in the output format specified. +// The format string uses the same abbreviations as in getDateFromFormat() +// ------------------------------------------------------------------ +function formatDate(date,format) { + format=format+""; + var result=""; + var i_format=0; + var c=""; + var token=""; + var y=date.getYear()+""; + var M=date.getMonth()+1; + var d=date.getDate(); + var E=date.getDay(); + var H=date.getHours(); + var m=date.getMinutes(); + var s=date.getSeconds(); + var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k; + // Convert real date parts into formatted versions + var value=new Object(); + if (y.length < 4) {y=""+(y-0+1900);} + value["y"]=""+y; + value["yyyy"]=y; + value["yy"]=y.substring(2,4); + value["M"]=M; + value["MM"]=LZ(M); + value["MMM"]=MONTH_NAMES[M-1]; + value["NNN"]=MONTH_NAMES[M+11]; + value["d"]=d; + value["dd"]=LZ(d); + value["E"]=DAY_NAMES[E+7]; + value["EE"]=DAY_NAMES[E]; + value["H"]=H; + value["HH"]=LZ(H); + if (H==0){value["h"]=12;} + else if (H>12){value["h"]=H-12;} + else {value["h"]=H;} + value["hh"]=LZ(value["h"]); + if (H>11){value["K"]=H-12;} else {value["K"]=H;} + value["k"]=H+1; + value["KK"]=LZ(value["K"]); + value["kk"]=LZ(value["k"]); + if (H > 11) { value["a"]="PM"; } + else { value["a"]="AM"; } + value["m"]=m; + value["mm"]=LZ(m); + value["s"]=s; + value["ss"]=LZ(s); + while (i_format < format.length) { + c=format.charAt(i_format); + token=""; + while ((format.charAt(i_format)==c) && (i_format < format.length)) { + token += format.charAt(i_format++); + } + if (value[token] != null) { result=result + value[token]; } + else { result=result + token; } + } + return result; + } + +// ------------------------------------------------------------------ +// Utility functions for parsing in getDateFromFormat() +// ------------------------------------------------------------------ +function _isInteger(val) { + var digits="1234567890"; + for (var i=0; i < val.length; i++) { + if (digits.indexOf(val.charAt(i))==-1) { return false; } + } + return true; + } +function _getInt(str,i,minlength,maxlength) { + for (var x=maxlength; x>=minlength; x--) { + var token=str.substring(i,i+x); + if (token.length < minlength) { return null; } + if (_isInteger(token)) { return token; } + } + return null; + } + +// ------------------------------------------------------------------ +// getDateFromFormat( date_string , format_string ) +// +// This function takes a date string and a format string. It matches +// If the date string matches the format string, it returns the +// getTime() of the date. If it does not match, it returns 0. +// ------------------------------------------------------------------ +function getDateFromFormat(val,format) { + val=val+""; + format=format+""; + var i_val=0; + var i_format=0; + var c=""; + var token=""; + var token2=""; + var x,y; + var now=new Date(); + var year=now.getYear(); + var month=now.getMonth()+1; + var date=1; + var hh=now.getHours(); + var mm=now.getMinutes(); + var ss=now.getSeconds(); + var ampm=""; + + while (i_format < format.length) { + // Get next token from format string + c=format.charAt(i_format); + token=""; + while ((format.charAt(i_format)==c) && (i_format < format.length)) { + token += format.charAt(i_format++); + } + // Extract contents of value based on format token + if (token=="yyyy" || token=="yy" || token=="y") { + if (token=="yyyy") { x=4;y=4; } + if (token=="yy") { x=2;y=2; } + if (token=="y") { x=2;y=4; } + year=_getInt(val,i_val,x,y); + if (year==null) { return 0; } + i_val += year.length; + if (year.length==2) { + if (year > 70) { year=1900+(year-0); } + else { year=2000+(year-0); } + } + } + else if (token=="MMM"||token=="NNN"){ + month=0; + for (var i=0; i11)) { + month=i+1; + if (month>12) { month -= 12; } + i_val += month_name.length; + break; + } + } + } + if ((month < 1)||(month>12)){return 0;} + } + else if (token=="EE"||token=="E"){ + for (var i=0; i12)){return 0;} + i_val+=month.length;} + else if (token=="dd"||token=="d") { + date=_getInt(val,i_val,token.length,2); + if(date==null||(date<1)||(date>31)){return 0;} + i_val+=date.length;} + else if (token=="hh"||token=="h") { + hh=_getInt(val,i_val,token.length,2); + if(hh==null||(hh<1)||(hh>12)){return 0;} + i_val+=hh.length;} + else if (token=="HH"||token=="H") { + hh=_getInt(val,i_val,token.length,2); + if(hh==null||(hh<0)||(hh>23)){return 0;} + i_val+=hh.length;} + else if (token=="KK"||token=="K") { + hh=_getInt(val,i_val,token.length,2); + if(hh==null||(hh<0)||(hh>11)){return 0;} + i_val+=hh.length;} + else if (token=="kk"||token=="k") { + hh=_getInt(val,i_val,token.length,2); + if(hh==null||(hh<1)||(hh>24)){return 0;} + i_val+=hh.length;hh--;} + else if (token=="mm"||token=="m") { + mm=_getInt(val,i_val,token.length,2); + if(mm==null||(mm<0)||(mm>59)){return 0;} + i_val+=mm.length;} + else if (token=="ss"||token=="s") { + ss=_getInt(val,i_val,token.length,2); + if(ss==null||(ss<0)||(ss>59)){return 0;} + i_val+=ss.length;} + else if (token=="a") { + if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";} + else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";} + else {return 0;} + i_val+=2;} + else { + if (val.substring(i_val,i_val+token.length)!=token) {return 0;} + else {i_val+=token.length;} + } + } + // If there are any trailing characters left in the value, it doesn't match + if (i_val != val.length) { return 0; } + // Is date valid for month? + if (month==2) { + // Check for leap year + if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year + if (date > 29){ return 0; } + } + else { if (date > 28) { return 0; } } + } + if ((month==4)||(month==6)||(month==9)||(month==11)) { + if (date > 30) { return 0; } + } + // Correct hours value + if (hh<12 && ampm=="PM") { hh=hh-0+12; } + else if (hh>11 && ampm=="AM") { hh-=12; } + var newdate=new Date(year,month-1,date,hh,mm,ss); + return newdate.getTime(); + } + +// ------------------------------------------------------------------ +// parseDate( date_string [, prefer_euro_format] ) +// +// This function takes a date string and tries to match it to a +// number of possible date formats to get the value. It will try to +// match against the following international formats, in this order: +// y-M-d MMM d, y MMM d,y y-MMM-d d-MMM-y MMM d +// M/d/y M-d-y M.d.y MMM-d M/d M-d +// d/M/y d-M-y d.M.y d-MMM d/M d-M +// A second argument may be passed to instruct the method to search +// for formats like d/M/y (european format) before M/d/y (American). +// Returns a Date object or null if no patterns match. +// ------------------------------------------------------------------ +function parseDate(val) { + var preferEuro=(arguments.length==2)?arguments[1]:false; + generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d'); + monthFirst=new Array('M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d'); + dateFirst =new Array('d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M'); + var checkList=new Array('generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst'); + var d=null; + for (var i=0; i* Hovering over a point will show its label.
* Drag and select an area in the plot to zoom into it.
* Click reset zoom link to come back to original state.
* Click a data point to view and possibly edit the data row.
* The plot can be resized by dragging it along the bottom right corner.
* Strings are converted into integer for plotting'); +$js_messages['strInputNull'] = __('Select two columns'); +$js_messages['strSameInputs'] = __('Select two different columns'); + /* For tbl_change.js */ $js_messages['strIgnore'] = __('Ignore'); diff --git a/js/tbl_select.js b/js/tbl_select.js index 3bc1137c5d..af2242ad5e 100644 --- a/js/tbl_select.js +++ b/js/tbl_select.js @@ -66,7 +66,7 @@ $(document).ready(function() { if (typeof response == 'string') { // found results $("#sqlqueryresults").html(response); - $("#sqlqueryresults").trigger('makegrid'); + $("#sqlqueryresults").trigger('appendAnchor'); $('#tbl_search_form') // work around for bug #3168569 - Issue on toggling the "Hide search criteria" in chrome. .slideToggle() diff --git a/js/tbl_zoom_plot.js b/js/tbl_zoom_plot.js new file mode 100644 index 0000000000..b9c0d75364 --- /dev/null +++ b/js/tbl_zoom_plot.js @@ -0,0 +1,605 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + ** @fileoverview JavaScript functions used on tbl_select.php + ** + ** @requires jQuery + ** @requires js/functions.js + **/ + + +/** + ** Display Help/Info + **/ +function displayHelp() { + var msgbox = PMA_ajaxShowMessage(PMA_messages['strDisplayHelp'],10000); + msgbox.click(function() { + PMA_ajaxRemoveMessage(msgbox); + }); +} + +/** + ** Extend the array object for max function + ** @param array + **/ +Array.max = function (array) { + return Math.max.apply( Math, array ); +} + +/** + ** Extend the array object for min function + ** @param array + **/ +Array.min = function (array) { + return Math.min.apply( Math, array ); +} + +/** + ** Checks if a string contains only numeric value + ** @param n: String (to be checked) + **/ +function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} + +/** + ** Checks if an object is empty + ** @param n: Object (to be checked) + **/ +function isEmpty(obj) { + var name; + for (name in obj) { + return false; + } + return true; +} + +/** + ** Converts a timestamp into the format of its field type + ** @param val Integer Timestamp + ** @param type String Field type(datetime/timestamp/time/date) + **/ +function getDate(val,type) { + if(type.toString().search(/datetime/i) != -1 || type.toString().search(/timestamp/i) != -1) { + return Highcharts.dateFormat('%Y-%m-%e %H:%M:%S', val) + } + else if(type.toString().search(/time/i) != -1) { + return Highcharts.dateFormat('%H:%M:%S', val + 19800000) + } + else if (type.toString().search(/date/i) != -1) { + return Highcharts.dateFormat('%Y-%m-%e', val) + } +} + +/** + ** Converts a date/time into timestamp + ** @param val String Date + ** @param type Sring Field type(datetime/timestamp/time/date) + **/ +function getTimeStamp(val,type) { + if(type.toString().search(/datetime/i) != -1 || type.toString().search(/timestamp/i) != -1) { + return getDateFromFormat(val,'yyyy-MM-dd HH:mm:ss', val) + } + else if(type.toString().search(/time/i) != -1) { + return getDateFromFormat('1970-01-01 ' + val,'yyyy-MM-dd HH:mm:ss') + } + else if (type.toString().search(/date/i) != -1) { + return getDateFromFormat(val,'yyyy-MM-dd') + } +} + +/** + ** Classifies the field type into numeric,timeseries or text + ** @param field: field type (as in database structure) + **/ +function getType(field) { + if(field.toString().search(/int/i) != -1 || field.toString().search(/decimal/i) != -1 || field.toString().search(/year/i) != -1) + return 'numeric'; + else if(field.toString().search(/time/i) != -1 || field.toString().search(/date/i) != -1) + return 'time'; + else + return 'text'; +} +/** + ** Converts a categorical array into numeric array + ** @param array categorical values array + **/ +function getCord(arr) { + var newCord = new Array(); + var original = $.extend(true,[],arr); + var arr = jQuery.unique(arr).sort(); + $.each(original, function(index,value) { + newCord.push(jQuery.inArray(value,arr)); + }); + return [newCord,arr,original]; +} + +/** + ** Scrolls the view to the display section + **/ +function scrollToChart() { + var x = $('#dataDisplay').offset().top - 100; // 100 provides buffer in viewport + $('html,body').animate({scrollTop: x}, 500); +} + +$(document).ready(function() { + + /** + ** Set a parameter for all Ajax queries made on this page. Don't let the + ** web server serve cached pages + **/ + $.ajaxSetup({ + cache: 'false' + }); + + var cursorMode = ($("input[name='mode']:checked").val() == 'edit') ? 'crosshair' : 'pointer'; + var currentChart = null; + var currentData = null; + var xLabel = $('#tableid_0').val(); + var yLabel = $('#tableid_1').val(); + var xType = $('#types_0').val(); + var yType = $('#types_1').val(); + var dataLabel = $('#dataLabel').val(); + + // Get query result + var data = jQuery.parseJSON($('#querydata').html()); + + /** + ** Input form submit on field change + **/ + $('#tableid_0').change(function() { + $('#zoom_search_form').submit(); + }) + + $('#tableid_1').change(function() { + $('#zoom_search_form').submit(); + }) + + $('#tableid_2').change(function() { + $('#zoom_search_form').submit(); + }) + + $('#tableid_3').change(function() { + $('#zoom_search_form').submit(); + }) + + /** + * Input form validation + **/ + $('#inputFormSubmitId').click(function() { + if ($('#tableid_0').get(0).selectedIndex == 0 || $('#tableid_1').get(0).selectedIndex == 0) + PMA_ajaxShowMessage(PMA_messages['strInputNull']); + else if (xLabel == yLabel) + PMA_ajaxShowMessage(PMA_messages['strSameInputs']); + }); + + /** + ** Prepare a div containing a link, otherwise it's incorrectly displayed + ** after a couple of clicks + **/ + $('
') + .insertAfter('#zoom_search_form') + // don't show it until we have results on-screen + .hide(); + + $('#togglesearchformlink') + .html(PMA_messages['strShowSearchCriteria']) + .bind('click', function() { + var $link = $(this); + $('#zoom_search_form').slideToggle(); + if ($link.text() == PMA_messages['strHideSearchCriteria']) { + $link.text(PMA_messages['strShowSearchCriteria']); + } else { + $link.text(PMA_messages['strHideSearchCriteria']); + } + // avoid default click action + return false; + }); + + /** + ** Set dialog properties for the data display form + **/ + $("#dataDisplay").dialog({ + autoOpen: false, + title: 'Data point content', + modal: false, //false otherwise other dialogues like timepicker may not function properly + height: $('#dataDisplay').height() + 80, + width: $('#dataDisplay').width() + 80 + }); + + /* + * Handle submit of zoom_display_form + */ + + $("#submitForm").click(function(event) { + + //Prevent default submission of form + event.preventDefault(); + + //Find changed values by comparing form values with selectedRow Object + var newValues = new Array();//Stores the values changed from original + var it = 4; + var xChange = false; + var yChange = false; + for (key in selectedRow) { + if (key != 'where_clause'){ + var oldVal = selectedRow[key]; + var newVal = ($('#fields_null_id_' + it).attr('checked')) ? null : $('#fieldID_' + it).val(); + if (oldVal != newVal){ + selectedRow[key] = newVal; + newValues[key] = newVal; + if(key == xLabel) { + xChange = true; + data[currentData][xLabel] = newVal; + } + else if(key == yLabel) { + yChange = true; + data[currentData][yLabel] = newVal; + } + } + } + it++ + }//End data update + + //Update the chart series and replot + if (xChange || yChange) { + var newSeries = new Array(); + newSeries[0] = new Object(); + newSeries[0].marker = { + symbol: 'circle' + }; + //Logic similar to plot generation, replot only if xAxis changes or yAxis changes. Code includes a lot of checks so as to replot only when necessary + if(xChange) { + xCord[currentData] = selectedRow[xLabel]; + if(xType == 'numeric') { + currentChart.series[0].data[currentData].update({ x : selectedRow[xLabel] }); + currentChart.xAxis[0].setExtremes(Array.min(xCord) - 6,Array.max(xCord) + 6); + } + else if(xType == 'time') { + currentChart.series[0].data[currentData].update({ x : getTimeStamp(selectedRow[xLabel],$('#types_0').val())}); + } + else { + var tempX = getCord(xCord); + var tempY = getCord(yCord); + var i = 0; + newSeries[0].data = new Array(); + xCord = tempX[2]; + yCord = tempY[2]; + + $.each(data,function(key,value) { + if(yType != 'text') + newSeries[0].data.push({ name: value[dataLabel], x: tempX[0][i], y: value[yLabel], marker: {fillColor: colorCodes[i % 8]} , id: i } ); + else + newSeries[0].data.push({ name: value[dataLabel], x: tempX[0][i], y: tempY[0][i], marker: {fillColor: colorCodes[i % 8]} , id: i } ); + i++; + }); + currentSettings.xAxis.labels = { formatter : function() { + if(tempX[1][this.value] && tempX[1][this.value].length > 10) + return tempX[1][this.value].substring(0,10) + else + return tempX[1][this.value]; + } + } + currentSettings.series = newSeries; + currentChart = PMA_createChart(currentSettings); + } + + } + if(yChange) { + + yCord[currentData] = selectedRow[yLabel]; + if(yType == 'numeric') { + currentChart.series[0].data[currentData].update({ y : selectedRow[yLabel] }); + currentChart.yAxis[0].setExtremes(Array.min(yCord) - 6,Array.max(yCord) + 6); + } + else if(yType =='time') { + currentChart.series[0].data[currentData].update({ y : getTimeStamp(selectedRow[yLabel],$('#types_1').val())}); + } + else { + var tempX = getCord(xCord); + var tempY = getCord(yCord); + var i = 0; + newSeries[0].data = new Array(); + xCord = tempX[2]; + yCord = tempY[2]; + + $.each(data,function(key,value) { + if(xType != 'text' ) + newSeries[0].data.push({ name: value[dataLabel], x: value[xLabel], y: tempY[0][i], marker: {fillColor: colorCodes[i % 8]} , id: i } ); + else + newSeries[0].data.push({ name: value[dataLabel], x: tempX[0][i], y: tempY[0][i], marker: {fillColor: colorCodes[i % 8]} , id: i } ); + i++; + }); + currentSettings.yAxis.labels = { formatter : function() { + if(tempY[1][this.value] && tempY[1][this.value].length > 10) + return tempY[1][this.value].substring(0,10) + else + return tempY[1][this.value]; + } + } + currentSettings.series = newSeries; + currentChart = PMA_createChart(currentSettings); + } + } + currentChart.series[0].data[currentData].select(); + } + //End plot update + + //Generate SQL query for update + if (!isEmpty(newValues)) { + var sql_query = 'UPDATE `' + window.parent.table + '` SET '; + for (key in newValues) { + if(key != 'where_clause') { + sql_query += '`' + key + '`=' ; + var value = newValues[key]; + if(!isNumeric(value) && value != null) + sql_query += '\'' + value + '\' ,'; + else + sql_query += value + ' ,'; + } + } + sql_query = sql_query.substring(0, sql_query.length - 1); + sql_query += ' WHERE ' + PMA_urldecode(data[currentData]['where_clause']); + + //Post SQL query to sql.php + $.post('sql.php', { + 'token' : window.parent.token, + 'db' : window.parent.db, + 'ajax_request' : true, + 'sql_query' : sql_query, + 'inline_edit' : false + }, function(data) { + if(data.success == true) { + $('#sqlqueryresults').html(data.sql_query); + $("#sqlqueryresults").trigger('appendAnchor'); + } + else + PMA_ajaxShowMessage(data.error); + })//End $.post + }//End database update + $("#dataDisplay").dialog("close"); + });//End submit handler + + /* + * Generate plot using Highcharts + */ + + if (data != null) { + $('#zoom_search_form') + .slideToggle() + .hide(); + $('#togglesearchformlink') + .text(PMA_messages['strShowSearchCriteria']) + $('#togglesearchformdiv').show(); + var selectedRow; + var columnNames = new Array(); + var colorCodes = ['#FF0000','#00FFFF','#0000FF','#0000A0','#FF0080','#800080','#FFFF00','#00FF00','#FF00FF']; + var series = new Array(); + var xCord = new Array(); + var yCord = new Array(); + var xCat = new Array(); + var yCat = new Array(); + var tempX, tempY; + var it = 0; + + // Set the basic plot settings + var currentSettings = { + chart: { + renderTo: 'querychart', + type: 'scatter', + zoomType: 'xy', + width:$('#resizer').width() -3, + height:$('#resizer').height()-20 + }, + credits: { + enabled: false + }, + exporting: { enabled: false }, + label: { text: $('#dataLabel').val() }, + plotOptions: { + series: { + allowPointSelect: true, + cursor: 'pointer', + showInLegend: false, + dataLabels: { + enabled: false, + }, + point: { + events: { + click: function() { + var id = this.id; + var fid = 4; + currentData = id; + // Make AJAX request to tbl_zoom_select.php for getting the complete row info + var post_params = { + 'ajax_request' : true, + 'get_data_row' : true, + 'db' : window.parent.db, + 'table' : window.parent.table, + 'where_clause' : data[id]['where_clause'], + 'token' : window.parent.token, + } + $.post('tbl_zoom_select.php', post_params, function(data) { + // Row is contained in data.row_info, now fill the displayResultForm with row values + for ( key in data.row_info) { + if (data.row_info[key] == null) + $('#fields_null_id_' + fid).attr('checked', true); + else + $('#fieldID_' + fid).val(data.row_info[key]); + fid++; + } + selectedRow = new Object(); + selectedRow = data.row_info; + }); + + $("#dataDisplay").dialog("open"); + }, + } + } + } + }, + tooltip: { + formatter: function() { + return this.point.name; + } + }, + title: { text: 'Query Results' }, + xAxis: { + title: { text: $('#tableid_0').val() }, + }, + yAxis: { + min: null, + title: { text: $('#tableid_1').val() }, + }, + } + + $('#resizer').resizable({ + resize: function() { + currentChart.setSize( + this.offsetWidth -3, + this.offsetHeight -20, + false + ); + } + }); + + // Classify types as either numeric,time,text + xType = getType(xType); + yType = getType(yType); + + //Set the axis type based on the field + currentSettings.xAxis.type = (xType == 'time') ? 'datetime' : 'linear'; + currentSettings.yAxis.type = (yType == 'time') ? 'datetime' : 'linear'; + + // Formulate series data for plot + series[0] = new Object(); + series[0].data = new Array(); + series[0].marker = { + symbol: 'circle' + }; + if (xType != 'text' && yType != 'text') { + $.each(data,function(key,value) { + var xVal = (xType == 'numeric') ? value[xLabel] : getTimeStamp(value[xLabel],$('#types_0').val()); + var yVal = (yType == 'numeric') ? value[yLabel] : getTimeStamp(value[yLabel],$('#types_1').val()); + series[0].data.push({ name: value[dataLabel], x: xVal, y: yVal, marker: {fillColor: colorCodes[it % 8]} , id: it } ); + xCord.push(value[xLabel]); + yCord.push(value[yLabel]); + it++; + }); + if(xType == 'numeric') { + currentSettings.xAxis.max = Array.max(xCord) + 6 + currentSettings.xAxis.min = Array.min(xCord) - 6 + } + else { + currentSettings.xAxis.labels = { formatter : function() { + return getDate(this.value, $('#types_0').val()); + }} + } + if(yType == 'numeric') { + currentSettings.yAxis.max = Array.max(yCord) + 6 + currentSettings.yAxis.min = Array.min(yCord) - 6 + } + else { + currentSettings.yAxis.labels = { formatter : function() { + return getDate(this.value, $('#types_1').val()); + }} + } + + } + + else if (xType =='text' && yType !='text') { + $.each(data,function(key,value) { + xCord.push(value[xLabel]); + yCord.push(value[yLabel]); + }); + + tempX = getCord(xCord); + $.each(data,function(key,value) { + var yVal = (yType == 'numeric') ? value[yLabel] : getTimeStamp(value[yLabel],$('#types_1').val()); + series[0].data.push({ name: value[dataLabel], x: tempX[0][it], y: yVal, marker: {fillColor: colorCodes[it % 8]} , id: it } ); + it++; + }); + + currentSettings.xAxis.labels = { formatter : function() { + if(tempX[1][this.value] && tempX[1][this.value].length > 10) + return tempX[1][this.value].substring(0,10) + else + return tempX[1][this.value]; + } + } + if(yType == 'numeric') { + currentSettings.yAxis.max = Array.max(yCord) + 6 + currentSettings.yAxis.min = Array.min(yCord) - 6 + } + else { + currentSettings.yAxis.labels = { formatter : function() { + return getDate(this.value, $('#types_1').val()); + }} + } + xCord = tempX[2]; + } + + else if (xType !='text' && yType =='text') { + $.each(data,function(key,value) { + xCord.push(value[xLabel]); + yCord.push(value[yLabel]); + }); + tempY = getCord(yCord); + $.each(data,function(key,value) { + var xVal = (xType == 'numeric') ? value[xLabel] : getTimeStamp(value[xLabel],$('#types_0').val()); + series[0].data.push({ name: value[dataLabel], y: tempY[0][it], x: xVal, marker: {fillColor: colorCodes[it % 8]} , id: it } ); + it++; + }); + if(xType == 'numeric') { + currentSettings.xAxis.max = Array.max(xCord) + 6 + currentSettings.xAxis.min = Array.min(xCord) - 6 + } + else { + currentSettings.xAxis.labels = { formatter : function() { + return getDate(this.value, $('#types_0').val()); + }} + } + currentSettings.yAxis.labels = { formatter : function() { + if(tempY[1][this.value] && tempY[1][this.value].length > 10) + return tempY[1][this.value].substring(0,10) + else + return tempY[1][this.value]; + } + } + yCord = tempY[2]; + } + + else if (xType =='text' && yType =='text') { + $.each(data,function(key,value) { + xCord.push(value[xLabel]); + yCord.push(value[yLabel]); + }); + tempX = getCord(xCord); + tempY = getCord(yCord); + $.each(data,function(key,value) { + series[0].data.push({ name: value[dataLabel], x: tempX[0][it], y: tempY[0][it], marker: {fillColor: colorCodes[it % 8]} , id: it } ); + it++; + }); + currentSettings.xAxis.labels = { formatter : function() { + if(tempX[1][this.value] && tempX[1][this.value].length > 10) + return tempX[1][this.value].substring(0,10) + else + return tempX[1][this.value]; + } + } + currentSettings.yAxis.labels = { formatter : function() { + if(tempY[1][this.value] && tempY[1][this.value].length > 10) + return tempY[1][this.value].substring(0,10) + else + return tempY[1][this.value]; + } + } + xCord = tempX[2]; + yCord = tempY[2]; + + } + + currentSettings.series = series; + currentChart = PMA_createChart(currentSettings); + scrollToChart(); + } +}); diff --git a/libraries/common.inc.php b/libraries/common.inc.php index c806923591..d40cd7b30f 100644 --- a/libraries/common.inc.php +++ b/libraries/common.inc.php @@ -413,6 +413,7 @@ $goto_whitelist = array( 'tbl_replace.php', 'tbl_row_action.php', 'tbl_select.php', + 'tbl_zoom_select.php', //'themes.php', 'transformation_overview.php', 'transformation_wrapper.php', diff --git a/libraries/config.default.php b/libraries/config.default.php index efb0cbb358..d8367e5839 100644 --- a/libraries/config.default.php +++ b/libraries/config.default.php @@ -1137,7 +1137,6 @@ $cfg['DefaultTabDatabase'] = 'db_structure.php'; */ $cfg['DefaultTabTable'] = 'sql.php'; - /******************************************************************************* * Export defaults */ @@ -1845,7 +1844,6 @@ $cfg['Export']['xml_export_contents'] = true; */ $cfg['Export']['yaml_structure_or_data'] = 'data'; - /******************************************************************************* * Import defaults */ @@ -2443,7 +2441,6 @@ $cfg['UserprefsDisallow'] = array(); */ $cfg['UserprefsDeveloperTab'] = false; - /******************************************************************************* * Window title settings */ @@ -2554,9 +2551,7 @@ $cfg['DefaultQueryDatabase'] = ''; /******************************************************************************* * SQL Query box settings * These are the links display in all of the SQL Query boxes - */ - -/** + * * @global array $cfg['SQLQuery'] */ $cfg['SQLQuery'] = array(); @@ -2628,7 +2623,7 @@ $cfg['SaveDir'] = ''; $cfg['TempDir'] = ''; -/******************************************************************************* +/** * Misc. settings */ @@ -2673,12 +2668,7 @@ $cfg['LinkLengthLimit'] = 1000; */ $cfg['DisableMultiTableMaintenance'] = false; - /******************************************************************************* - * SQL Parser - */ - -/** * SQL Parser Settings * * @global array $cfg['SQP'] @@ -2708,10 +2698,6 @@ $cfg['SQP']['fmtIndUnit'] = 'em'; /******************************************************************************* - * SQL Validator - */ - -/** * If you wish to use the SQL Validator service, you should be aware of the * following: * All SQL statements are stored anonymously for statistical purposes. @@ -2746,10 +2732,6 @@ $cfg['SQLValidator']['password'] = ''; /******************************************************************************* * Developers ONLY! - */ - -/** - * Debugging settings * * @global array $cfg['DBG'] */ @@ -2768,50 +2750,328 @@ $cfg['DBG']['sql'] = false; */ /** - * Column types - * - * Fill in this array to overwrite data from data_*.inc.php files + * Column types; + * VARCHAR, TINYINT, TEXT and DATE are listed first, based on estimated popularity * * @global array $cfg['ColumnTypes'] */ -$cfg['ColumnTypes'] = array(); +$cfg['ColumnTypes'] = array( + // most used + 'INT', + 'VARCHAR', + 'TEXT', + 'DATE', + + // numeric + 'NUMERIC' => array( + 'TINYINT', + 'SMALLINT', + 'MEDIUMINT', + 'INT', + 'BIGINT', + '-', + 'DECIMAL', + 'FLOAT', + 'DOUBLE', + 'REAL', + '-', + 'BIT', + 'BOOLEAN', + 'SERIAL', + ), + + + // Date/Time + 'DATE and TIME' => array( + 'DATE', + 'DATETIME', + 'TIMESTAMP', + 'TIME', + 'YEAR', + ), + + // Text + 'STRING' => array( + 'CHAR', + 'VARCHAR', + '-', + 'TINYTEXT', + 'TEXT', + 'MEDIUMTEXT', + 'LONGTEXT', + '-', + 'BINARY', + 'VARBINARY', + '-', + 'TINYBLOB', + 'MEDIUMBLOB', + 'BLOB', + 'LONGBLOB', + '-', + 'ENUM', + 'SET', + ), + + 'SPATIAL' => array( + 'GEOMETRY', + 'POINT', + 'LINESTRING', + 'POLYGON', + 'MULTIPOINT', + 'MULTILINESTRING', + 'MULTIPOLYGON', + 'GEOMETRYCOLLECTION', + ), +); /** * Attributes * - * Fill in this array to overwrite data from data_*.inc.php files - * * @global array $cfg['AttributeTypes'] */ -$cfg['AttributeTypes'] = array(); +$cfg['AttributeTypes'] = array( + '', + 'BINARY', + 'UNSIGNED', + 'UNSIGNED ZEROFILL', + 'on update CURRENT_TIMESTAMP', +); + if ($cfg['ShowFunctionFields']) { /** * Available functions * - * Fill in this array to overwrite data from data_*.inc.php files - * * @global array $cfg['Functions'] */ - $cfg['Functions'] = array(); + $cfg['Functions'] = array( + 'ABS', + 'ACOS', + 'ASCII', + 'ASIN', + 'ATAN', + 'BIN', + 'BIT_COUNT', + 'BIT_LENGTH', + 'CEILING', + 'CHAR', + 'CHAR_LENGTH', + 'COMPRESS', + 'COS', + 'COT', + 'CRC32', + 'CURDATE', + 'CURRENT_USER', + 'CURTIME', + 'DATE', + 'DAYNAME', + 'DEGREES', + 'DES_DECRYPT', + 'DES_ENCRYPT', + 'ENCRYPT', + 'EXP', + 'FLOOR', + 'FROM_DAYS', + 'FROM_UNIXTIME', + 'HEX', + 'INET_ATON', + 'INET_NTOA', + 'LENGTH', + 'LN', + 'LOG', + 'LOG10', + 'LOG2', + 'LOWER', + 'MD5', + 'NOW', + 'OCT', + 'OLD_PASSWORD', + 'ORD', + 'PASSWORD', + 'RADIANS', + 'RAND', + 'REVERSE', + 'ROUND', + 'SEC_TO_TIME', + 'SHA1', + 'SOUNDEX', + 'SPACE', + 'SQRT', + 'STDDEV_POP', + 'STDDEV_SAMP', + 'TAN', + 'TIMESTAMP', + 'TIME_TO_SEC', + 'UNCOMPRESS', + 'UNHEX', + 'UNIX_TIMESTAMP', + 'UPPER', + 'USER', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'UUID', + 'VAR_POP', + 'VAR_SAMP', + 'YEAR', + ); /** * Which column types will be mapped to which Group? * - * Fill in this array to overwrite data from data_*.inc.php files - * * @global array $cfg['RestrictColumnTypes'] */ - $cfg['RestrictColumnTypes'] = array(); + $cfg['RestrictColumnTypes'] = array( + 'TINYINT' => 'FUNC_NUMBER', + 'SMALLINT' => 'FUNC_NUMBER', + 'MEDIUMINT' => 'FUNC_NUMBER', + 'INT' => 'FUNC_NUMBER', + 'BIGINT' => 'FUNC_NUMBER', + 'DECIMAL' => 'FUNC_NUMBER', + 'FLOAT' => 'FUNC_NUMBER', + 'DOUBLE' => 'FUNC_NUMBER', + 'REAL' => 'FUNC_NUMBER', + 'BIT' => 'FUNC_NUMBER', + 'BOOLEAN' => 'FUNC_NUMBER', + 'SERIAL' => 'FUNC_NUMBER', + + 'DATE' => 'FUNC_DATE', + 'DATETIME' => 'FUNC_DATE', + 'TIMESTAMP' => 'FUNC_DATE', + 'TIME' => 'FUNC_DATE', + 'YEAR' => 'FUNC_DATE', + + 'CHAR' => 'FUNC_CHAR', + 'VARCHAR' => 'FUNC_CHAR', + 'TINYTEXT' => 'FUNC_CHAR', + 'TEXT' => 'FUNC_CHAR', + 'MEDIUMTEXT' => 'FUNC_CHAR', + 'LONGTEXT' => 'FUNC_CHAR', + 'BINARY' => 'FUNC_CHAR', + 'VARBINARY' => 'FUNC_CHAR', + 'TINYBLOB' => 'FUNC_CHAR', + 'MEDIUMBLOB' => 'FUNC_CHAR', + 'BLOB' => 'FUNC_CHAR', + 'LONGBLOB' => 'FUNC_CHAR', + 'ENUM' => '', + 'SET' => '', + + 'GEOMETRY' => 'FUNC_SPATIAL', + 'POINT' => 'FUNC_SPATIAL', + 'LINESTRING' => 'FUNC_SPATIAL', + 'POLYGON' => 'FUNC_SPATIAL', + 'MULTIPOINT' => 'FUNC_SPATIAL', + 'MULTILINESTRING' => 'FUNC_SPATIAL', + 'MULTIPOLYGON' => 'FUNC_SPATIAL', + 'GEOMETRYCOLLECTION' => 'FUNC_SPATIAL', + + ); /** * Map above defined groups to any function * - * Fill in this array to overwrite data from data_*.inc.php files - * * @global array $cfg['RestrictFunctions'] */ - $cfg['RestrictFunctions'] = array(); + $cfg['RestrictFunctions'] = array( + 'FUNC_CHAR' => array( + 'BIN', + 'CHAR', + 'CURRENT_USER', + 'COMPRESS', + 'DAYNAME', + 'DES_DECRYPT', + 'DES_ENCRYPT', + 'ENCRYPT', + 'HEX', + 'INET_NTOA', + 'LOWER', + 'MD5', + 'OLD_PASSWORD', + 'PASSWORD', + 'REVERSE', + 'SHA1', + 'SOUNDEX', + 'SPACE', + 'UNCOMPRESS', + 'UNHEX', + 'UPPER', + 'USER', + 'UUID', + ), + + 'FUNC_DATE' => array( + 'CURDATE', + 'CURTIME', + 'DATE', + 'FROM_DAYS', + 'FROM_UNIXTIME', + 'NOW', + 'SEC_TO_TIME', + 'TIMESTAMP', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'YEAR', + ), + + 'FUNC_NUMBER' => array( + 'ABS', + 'ACOS', + 'ASCII', + 'ASIN', + 'ATAN', + 'BIT_LENGTH', + 'BIT_COUNT', + 'CEILING', + 'CHAR_LENGTH', + 'COS', + 'COT', + 'CRC32', + 'DEGREES', + 'EXP', + 'FLOOR', + 'INET_ATON', + 'LENGTH', + 'LN', + 'LOG', + 'LOG2', + 'LOG10', + 'OCT', + 'ORD', + 'RADIANS', + 'RAND', + 'ROUND', + 'SQRT', + 'STDDEV_POP', + 'STDDEV_SAMP', + 'TAN', + 'TIME_TO_SEC', + 'UNIX_TIMESTAMP', + 'VAR_POP', + 'VAR_SAMP', + ), + + 'FUNC_SPATIAL' => array( + 'GeomFromText', + 'GeomFromWKB', + + 'GeomCollFromText', + 'LineFromText', + 'MLineFromText', + 'PointFromText', + 'MPointFromText', + 'PolyFromText', + 'MPolyFromText', + + 'GeomCollFromWKB', + 'LineFromWKB', + 'MLineFromWKB', + 'PointFromWKB', + 'MPointFromWKB', + 'PolyFromWKB', + 'MPolyFromWKB', + ), + ); /** * Default functions for above defined groups @@ -2825,6 +3085,97 @@ if ($cfg['ShowFunctionFields']) { 'first_timestamp' => 'NOW', 'pk_char36' => 'UUID', ); -} + + +} // end if + +/** + * Search operators + * + * @global array $cfg['NumOperators'] + */ +$cfg['NumOperators'] = array( + '=', + '>', + '>=', + '<', + '<=', + '!=', + 'LIKE', + 'NOT LIKE', + 'IN (...)', + 'NOT IN (...)', + 'BETWEEN', + 'NOT BETWEEN', +); + +/** + * Search operators + * + * @global array $cfg['TextOperators'] + */ +$cfg['TextOperators'] = array( + 'LIKE', + 'LIKE %...%', + 'NOT LIKE', + '=', + '!=', + 'REGEXP', + 'REGEXP ^...$', + 'NOT REGEXP', + "= ''", + "!= ''", + 'IN (...)', + 'NOT IN (...)', + 'BETWEEN', + 'NOT BETWEEN', +); + +/** + * Search operators + * + * @global array $cfg['EnumOperators'] + */ +$cfg['EnumOperators'] = array( + '=', + '!=', +); + +/** + * Search operators + * + * @global array $cfg['SetOperators'] + */ +$cfg['SetOperators'] = array( + 'IN', + 'NOT IN', +); + +/** + * Search operators + * + * @global array $cfg['NullOperators'] + */ +$cfg['NullOperators'] = array( + 'IS NULL', + 'IS NOT NULL', +); + +/** + * Search operators + * + * @global array $cfg['UnaryOperators'] + */ +$cfg['UnaryOperators'] = array( + 'IS NULL' => 1, + 'IS NOT NULL' => 1, + "= ''" => 1, + "!= ''" => 1 +); + +/** + * Max rows retreived for zoom search + */ +$cfg['maxRowPlotLimit'] = 500; ?> diff --git a/libraries/svg_plot/pma_scatter_plot.php b/libraries/svg_plot/pma_scatter_plot.php new file mode 100644 index 0000000000..10c0aea343 --- /dev/null +++ b/libraries/svg_plot/pma_scatter_plot.php @@ -0,0 +1,267 @@ + array( + '#BCE02E', + '#E0642E', + '#E0D62E', + '#2E97E0', + '#B02EE0', + '#E02E75', + '#5CE02E', + '#E0B02E', + '#000000', + '#0022E0', + '#726CB1', + '#481A36', + '#BAC658', + '#127224', + '#825119', + '#238C74', + '#4C489B', + '#87C9BF', + ), + // Plot background color. + 'bgColor' => '#84AD83', + + // The width of the plot. + 'width' => 520, + + // The height of the plot. + 'height' => 325, + + // Default X Axis label. If empty, label will be taken from the data. + 'xLabel' => '', + + // Default Y Axis label. If empty, label will be taken from the data. + 'yLabel' => '', + + // Data point label. If empty, label will be taken from the data. + 'dataLabel' => '', + + ); + + /** + * @var array Options that the user has specified. + */ + private $_userSpecifiedSettings = null; + + /** + * Returns the settings array + * + * @return the settings array. + */ + public function getSettings() + { + return $this->_settings; + } + + /** + * Returns the data array + * + * @return the data array. + */ + public function getData() + { + return $this->_data; + } + + /** + * Constructor. Stores user specified options. + * + * @param array $data Data for the visualization + * @param array $options Users specified options + */ + public function __construct($data, $options) + { + $this->_userSpecifiedSettings = $options; + $this->_data = $data; + } + + /** + * All the variable initialization, options handling has to be done here. + */ + protected function init() + { + $this->_handleOptions(); + } + + /** + * A function which handles passed parameters. Useful if desired + * chart needs to be a little bit different from the default one. + */ + private function _handleOptions() + { + $this->_dataPoints = array(); + if (! is_null($this->_userSpecifiedSettings)) { + foreach (array_keys($this->_userSpecifiedSettings) as $key){ + $this->_settings[$key] = $this->_userSpecifiedSettings[$key]; + } + } + if ($this->_settings['dataLabel'] == '') { + $labels = array_keys($this->_data[0]); + $this->_settings['dataLabel'] = $labels[0]; + } + } + + /** + * Generate the visualization in SVG format. + * + * @return the generated image resource + */ + private function _svg() + { + $this->init(); + + $output = '' . "\n"; + $output .= ''; + $output .= ''; + $output .= ' + + + '; + $output .= ' + '; + $output .= $this->_settings['yLabel']; + $output .= ' + '; + + $output .= ' + '; + $output .= $this->_settings['xLabel']; + $output .= ' + '; + + + $scale_data = $this->_scaleDataSet($this->_data, $this->_settings['xLabel'], $this->_settings['yLabel']); + $output .= $this->_prepareDataSet($this->_data, 0, $scale_data, $this->_settings['dataLabel']); + + $output .= ''; + $output .= ''; + + return $output; + } + + /** + * Get the visualization as a SVG. + * + * @return the visualization as a SVG + */ + public function asSVG() + { + $output = $this->_svg(); + return $output; + } + + /** + * Calculates the scale, horizontal and vertical offset that should be used. + * + * @param array $data Row data + * + * @return an array containing the scale, x and y offsets + */ + private function _scaleDataSet($data,$xField,$yField) + { + + // Currently assuming only numeric fields are selected + $coordinates = array(); + foreach ($data as $row) { + $coordinates[0][] = $row[$xField]; + $coordinates[1][] = $row[$yField]; + } + for ($i = 0 ; $i < 2 ; $i++) { + + $maxC = ($i == 0) ? 500 : 320; + + if( !is_numeric($coordinates[$i][0])) { + $uniqueC = array_unique($coordinates[$i]); + $countC = count(array_unique($coordinates[$i])); + $map = $tmp = array(); + foreach ($uniqueC as $uc) { + $tmp[] = $uc; + } + for ($j = 0 ; $j < $countC ; $j++) { + $map[$tmp[$j]] = 20 + $j * $maxC / $countC; + } + for($j = 0 ; $j < count($coordinates[$i]) ; $j++) { + $coordinates[$i][$j] = $map[$coordinates[$i][$j]]; + } + + } + elseif (is_numeric($coordinates[$i][0])) { + + $maxC = max($coordinates[$i]); + for($j = 0 ; $j < count($coordinates[$i]) ; $j++) { + + if ($i == 0) + $coordinates[$i][$j] = 20 + 500 * $coordinates[$i][$j] / $maxC; + else + $coordinates[$i][$j] = 20 + 320 * (1 - $coordinates[$i][$j] / $maxC); + } + } + } + return $coordinates; + } + + /** + * Prepares and return the dataset as needed by the visualization. + * + * @param array $data Raw data + * @param int $color_number Start index to the color array + * @param array $scale_data Data related to scaling + * @param string $label Label for the data points + * @param image $results Image object in the case of png + * + * @return the formatted array of data. + */ + private function _prepareDataSet($data, $color_number, $scale_data, $label) + { + $result = ''; + // loop through the rows + for($i = 0 ; $i < count($data) ; $i++) { + + $index = $color_number % sizeof($this->_settings['colors']); + + $data_element = new PMA_SVG_Data_Point($scale_data[0][$i],$scale_data[1][$i],$data[$i][$label],$data[$i]); + + $options = array('color' => $this->_settings['colors'][$index], 'id' => $i); + $this->_dataPoints[] = $data_element; + + $result .= $data_element->prepareRowAsSVG($options); + $color_number++; + } + + + return $result; + } +} +?> + diff --git a/libraries/svg_plot/pma_svg_data_element.php b/libraries/svg_plot/pma_svg_data_element.php new file mode 100644 index 0000000000..97f7734572 --- /dev/null +++ b/libraries/svg_plot/pma_svg_data_element.php @@ -0,0 +1,50 @@ +label = $label; + $this->dataRow = $dataRow; + } + + /** + * Handles the generation of each Data Row/Element as a SVG element + * @return the code related to a row in the GIS dataset + */ + public abstract function prepareRowAsSVG($options); + + public function getLabel() + { + return $this->label; + } + + public function setLabel($label) + { + $this->label = $label; + } + + public function getDataRow() + { + return $this->dataRow; + } + + public function setDataRow($dataRow) + { + $this->dataRow = $dataRow; + } +} +?> diff --git a/libraries/svg_plot/pma_svg_data_point.php b/libraries/svg_plot/pma_svg_data_point.php new file mode 100644 index 0000000000..7d01b2b566 --- /dev/null +++ b/libraries/svg_plot/pma_svg_data_point.php @@ -0,0 +1,86 @@ +cx = $cx; + $this->cy = $cy; + + } + + public function prepareRowAsSVG($options) + { + return $this->prepareSvg($options); + } + + /** + * Prepares and returns the code related to a row in the query result as SVG. + * + * @param array $options Array containing options related to properties of the point + * + * @return the code related to a row in the query result. + */ + + protected function prepareSvg($options) + { + $point_options = array( + 'name' => $this->label . '_' .$options['id'], + 'id' => $this->label . 'id' . '_' . $options['id'], + 'class' => 'point', + 'fill' => 'white', + 'stroke' => $options['color'], + 'stroke-width'=> 2, + ); + + $row = ' $val) { + $row .= ' ' . $option . '="' . trim($val) . '"'; + } + $row .= '/>'; + + return $row; + } + + public function getCx() + { + return $this->cx; + } + + public function setCx($cx) + { + $this->cx = $cx; + } + + public function getCy() + { + return $this->cy; + } + + public function setCy($cy) + { + $this->cy = $cy; + } +} +?> diff --git a/libraries/tbl_select.lib.php b/libraries/tbl_select.lib.php new file mode 100644 index 0000000000..6384f60b16 --- /dev/null +++ b/libraries/tbl_select.lib.php @@ -0,0 +1,390 @@ +'; + + if ($propertiesIconic === 'both') { + $str .= __('Browse foreign values'); + return $str; + } + } else { + return __('Browse foreign values'); + } +} + + /** + * PMA_tbl_getFields() gets all the fields of a table along with their types,collations and whether null or not. + * + * @uses PMA_DBI_query() + * @uses PMA_backquote() + * @uses PMA_DBI_num_rows() + * @uses PMA_DBI_fetch_assoc() + * @uses PMA_DBI_free_result() + * @uses preg_replace() + * @uses str_replace() + * @uses strncasecmp() + * @uses empty() + * + * @param $db Selected database + * @param $table Selected table + * + * @return array($fields_list,$fields_type,$fields_collation,$fields_null) Array containing the field list, field types, collations and null constatint + * + */ + +function PMA_tbl_getFields($table,$db) { + + // Gets the list and number of fields + + $result = PMA_DBI_query('SHOW FULL FIELDS FROM ' . PMA_backquote($table) . ' FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); + $fields_cnt = PMA_DBI_num_rows($result); + $fields_list = $fields_null = $fields_type = $fields_collation = array(); + while ($row = PMA_DBI_fetch_assoc($result)) { + $fields_list[] = $row['Field']; + $type = $row['Type']; + // reformat mysql query output + if (strncasecmp($type, 'set', 3) == 0 + || strncasecmp($type, 'enum', 4) == 0) { + $type = str_replace(',', ', ', $type); + } else { + + // strip the "BINARY" attribute, except if we find "BINARY(" because + // this would be a BINARY or VARBINARY field type + if (!preg_match('@BINARY[\(]@i', $type)) { + $type = preg_replace('@BINARY@i', '', $type); + } + $type = preg_replace('@ZEROFILL@i', '', $type); + $type = preg_replace('@UNSIGNED@i', '', $type); + + $type = strtolower($type); + } + if (empty($type)) { + $type = ' '; + } + $fields_null[] = $row['Null']; + $fields_type[] = $type; + $fields_collation[] = !empty($row['Collation']) && $row['Collation'] != 'NULL' + ? $row['Collation'] + : ''; + } // end while + PMA_DBI_free_result($result); + unset($result, $type); + + return array($fields_list,$fields_type,$fields_collation,$fields_null); + +} + +/* PMA_tbl_setTableHeader() sets the table header for displaying a table in query-by-example format + * + * @return HTML content, the tags and content for table header + * + */ + +function PMA_tbl_setTableHeader(){ + +return ' + ' . __('Column') . ' + ' . __('Type') . ' + ' . __('Collation') . ' + ' . __('Operator') . ' + ' . __('Value') . ' + + '; + + +} + +/* PMA_tbl_getSubTabs() returns an array with necessary configrations to create sub-tabs(Table Search and Zoom Search) in the table_select page + * + * @return array $subtabs Array containing configuration (icon,text,link,id,args) of sub-tabs for Table Search and Zoom search + * + */ + +function PMA_tbl_getSubTabs(){ + + $subtabs = array(); + + $subtabs['search']['icon'] = 'b_search.png'; + $subtabs['search']['text'] = __('Table Search'); + $subtabs['search']['link'] = 'tbl_select.php'; + $subtabs['search']['id'] = 'tbl_search_id'; + $subtabs['search']['args']['pos'] = 0; + + $subtabs['zoom']['icon'] = 'b_props.png'; + $subtabs['zoom']['link'] = 'tbl_zoom_select.php'; + $subtabs['zoom']['text'] = __('Zoom Search'); + $subtabs['zoom']['id'] = 'zoom_search_id'; + + return $subtabs; + +} + + +/* PMA_tbl_getForeignFields_Values() creates the HTML content for: 1) Browsing foreign data for a field. 2) Creating elements for search criteria input on fields. + * + * @uses PMA_foreignDropdown + * @uses PMA_generate_common_url + * @uses isset() + * @uses is_array() + * @uses in_array() + * @uses urlencode() + * @uses str_replace() + * @uses stbstr() + * + * @param $foreigners Array of foreign keys + * @param $foreignData Foreign keys data + * @param $field Column name + * @param $tbl_fields_type Column type + * @param $i Column index + * @param $db Selected database + * @param $table Selected table + * @param $titles Selected title + * @param $foreignMaxLimit Max limit of displaying foreign elements + * @param $fields Array of search criteria inputs + * + * @return string $str HTML content for viewing foreing data and elements for search criteria input. + * + */ + +function PMA_getForeignFields_Values($foreigners, $foreignData, $field, $tbl_fields_type, $i, $db, $table,$titles,$foreignMaxLimit, $fields){ + + $str = ''; + + if ($foreigners && isset($foreigners[$field]) && is_array($foreignData['disp_row'])) { + // f o r e i g n k e y s + $str .= ' ' . "\n"; + } + elseif ($foreignData['foreign_link'] == true) { + if(isset($fields[$i]) && is_string($fields[$i])){ + $str .= '' ; + } + else{ + $str .= '' ; + } + ?> + '; + // ' . str_replace("'", "\'", $titles['Browse']) . ''; + // ]] + $str .= ''; + } + elseif (strncasecmp($tbl_fields_type[$i], 'enum', 4) == 0) { + // e n u m s + $enum_value=explode(', ', str_replace("'", '', substr($tbl_fields_type[$i], 5, -1))); + $cnt_enum_value = count($enum_value); + $str .= '' . "\n"; + } + else { + // o t h e r c a s e s + $the_class = 'textfield'; + $type = $tbl_fields_type[$i]; + if ($type == 'date') { + $the_class .= ' datefield'; + } elseif ($type == 'datetime' || substr($type, 0, 9) == 'timestamp') { + $the_class .= ' datetimefield'; + } + if(isset($fields[$i]) && is_string($fields[$i])){ + $str .= ' ' . "\n"; + } + else{ + $str .= ' ' . "\n"; + } + }; + return $str; + +} + + +/* PMA_tbl_search_getWhereClause() Return the where clause for query generation based on the inputs provided. + * + * @uses PMA_backquote + * @uses PMA_sqlAddslashes + * @uses preg_match + * @uses isset() + * @uses in_array() + * @uses str_replace() + * @uses strpos() + * @uses explode() + * @uses trim() + * + * @param $fields Search criteria input + * @param $names Name of the field(column) on which search criteria is submitted + * @param $types Type of the field + * @param $collations Field collation + * @param $func_type Search fucntion/operator + * @param $unaryFlag Whether operator unary or not + * + * @return string $str HTML content for viewing foreing data and elements for search criteria input. + * + */ + +function PMA_tbl_search_getWhereClause($fields, $names, $types, $collations, $func_type, $unaryFlag){ + + + $w = ''; + if($unaryFlag){ + $fields = ''; + $w = PMA_backquote($names) . ' ' . $func_type; + + } elseif (strncasecmp($types, 'enum', 4) == 0) { + if (!empty($fields)) { + if (! is_array($fields)) { + $fields = explode(',', $fields); + } + $enum_selected_count = count($fields); + if ($func_type == '=' && $enum_selected_count > 1) { + $func_type = 'IN'; + $parens_open = '('; + $parens_close = ')'; + + } elseif ($func_type == '!=' && $enum_selected_count > 1) { + $func_type = 'NOT IN'; + $parens_open = '('; + $parens_close = ')'; + + } else { + $parens_open = ''; + $parens_close = ''; + } + $enum_where = '\'' . PMA_sqlAddslashes($fields[0]) . '\''; + for ($e = 1; $e < $enum_selected_count; $e++) { + $enum_where .= ', \'' . PMA_sqlAddslashes($fields[$e]) . '\''; + } + + $w = PMA_backquote($names) . ' ' . $func_type . ' ' . $parens_open . $enum_where . $parens_close; + } + + } elseif ($fields != '') { + // For these types we quote the value. Even if it's another type (like INT), + // for a LIKE we always quote the value. MySQL converts strings to numbers + // and numbers to strings as necessary during the comparison + if (preg_match('@char|binary|blob|text|set|date|time|year@i', $types) || strpos(' ' . $func_type, 'LIKE')) { + $quot = '\''; + } else { + $quot = ''; + } + + // LIKE %...% + if ($func_type == 'LIKE %...%') { + $func_type = 'LIKE'; + $fields = '%' . $fields . '%'; + } + if ($func_type == 'REGEXP ^...$') { + $func_type = 'REGEXP'; + $fields = '^' . $fields . '$'; + } + + if ($func_type == 'IN (...)' || $func_type == 'NOT IN (...)' || $func_type == 'BETWEEN' || $func_type == 'NOT BETWEEN') { + $func_type = str_replace(' (...)', '', $func_type); + + // quote values one by one + $values = explode(',', $fields); + foreach ($values as &$value) + $value = $quot . PMA_sqlAddslashes(trim($value)) . $quot; + + if ($func_type == 'BETWEEN' || $func_type == 'NOT BETWEEN') + $w = PMA_backquote($names) . ' ' . $func_type . ' ' . (isset($values[0]) ? $values[0] : '') . ' AND ' . (isset($values[1]) ? $values[1] : ''); + else + $w = PMA_backquote($names) . ' ' . $func_type . ' (' . implode(',', $values) . ')'; + } + else { + $w = PMA_backquote($names) . ' ' . $func_type . ' ' . $quot . PMA_sqlAddslashes($fields) . $quot;; + } + + } // end if + + return $w; +} + +/** + * Formats a SVG plot for the query results. + * + * @param array $data Data for the status chart + * @param array &$settings Settings used to generate the chart + * + * @return string HTML and JS code for the SVG plot + */ +function PMA_SVG_scatter_plot($data, &$settings) +{ + require_once './libraries/svg_plot/pma_scatter_plot.php'; + + if (empty($data)) { + // empty data + return ''; + } else { + $scatter_plot = new PMA_Scatter_Plot($data, $settings); + + if ($settings != null) { + foreach ($scatter_plot->getSettings() as $setting => $val) { + if (! isset($settings[$setting])) { + $settings[$setting] = $val; + } + } + } + return $scatter_plot->asSVG(); + } + +} + + + + + + + + + +?> diff --git a/tbl_select.php b/tbl_select.php index c6b0a35332..e9b1eaec11 100644 --- a/tbl_select.php +++ b/tbl_select.php @@ -15,6 +15,7 @@ */ require_once './libraries/common.inc.php'; require_once './libraries/mysql_charsets.lib.php'; +require_once './libraries/tbl_select.lib.php'; $GLOBALS['js_include'][] = 'makegrid.js'; $GLOBALS['js_include'][] = 'sql.js'; @@ -22,18 +23,8 @@ $GLOBALS['js_include'][] = 'tbl_select.js'; $GLOBALS['js_include'][] = 'tbl_change.js'; $GLOBALS['js_include'][] = 'jquery/jquery-ui-1.8.custom.js'; $GLOBALS['js_include'][] = 'jquery/timepicker.js'; -if ($GLOBALS['cfg']['PropertiesIconic'] == true) { - $titles['Browse'] = - '' . __('Browse foreign values') . ''; - if ($GLOBALS['cfg']['PropertiesIconic'] === 'both') { - $titles['Browse'] .= __('Browse foreign values'); - } -} else { - $titles['Browse'] = __('Browse foreign values'); -} +$titles['Browse'] = PMA_tbl_setTitle($GLOBALS['cfg']['PropertiesIconic'], $pmaThemeImage); /** * Not selection yet required -> displays the selection form @@ -61,45 +52,26 @@ if (! isset($param) || $param[0] == '') { $err_url = $goto . '?' . PMA_generate_common_url($db, $table); // Gets the list and number of fields - $result = PMA_DBI_query('SHOW FULL FIELDS FROM ' . PMA_backquote($table) . ' FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); - $fields_cnt = PMA_DBI_num_rows($result); - $fields_list = $fields_null = $fields_type = $fields_collation = array(); - while ($row = PMA_DBI_fetch_assoc($result)) { - $fields_list[] = $row['Field']; - $type = $row['Type']; - // reformat mysql query output - if (strncasecmp($type, 'set', 3) == 0 - || strncasecmp($type, 'enum', 4) == 0) { - $type = str_replace(',', ', ', $type); - } else { - // strip the "BINARY" attribute, except if we find "BINARY(" because - // this would be a BINARY or VARBINARY field type - if (!preg_match('@BINARY[\(]@i', $type)) { - $type = preg_replace('@BINARY@i', '', $type); - } - $type = preg_replace('@ZEROFILL@i', '', $type); - $type = preg_replace('@UNSIGNED@i', '', $type); - - $type = strtolower($type); - } - if (empty($type)) { - $type = ' '; - } - $fields_null[] = $row['Null']; - $fields_type[] = $type; - $fields_collation[] = !empty($row['Collation']) && $row['Collation'] != 'NULL' - ? $row['Collation'] - : ''; - } // end while - PMA_DBI_free_result($result); - unset($result, $type); + list($fields_list, $fields_type, $fields_collation, $fields_null) = PMA_tbl_getFields($table,$db); + $fields_cnt = count($fields_list); // retrieve keys into foreign fields, if any // check also foreigners even if relwork is FALSE (to get // foreign keys from innodb) $foreigners = PMA_getForeigners($db, $table); ?> + +
+ +
> @@ -110,14 +82,7 @@ if (! isset($param) || $param[0] == '') {
- - - - - - - - +
' . "\n"; - // go back to first row - - // here, the 4th parameter is empty because there is no current - // value of data for the dropdown (the search page initial values - // are displayed empty) - echo PMA_foreignDropdown($foreignData['disp_row'], - $foreignData['foreign_field'], - $foreignData['foreign_display'], - '', $GLOBALS['cfg']['ForeignKeyMaxLimit']); - echo ' ' . "\n"; - } elseif ($foreignData['foreign_link'] == true) { - ?> - - - ' . "\n"; - for ($j = 0; $j < $cnt_enum_value; $j++) { - echo ' '; - } // end for - echo ' ' . "\n"; - } else { - // o t h e r c a s e s - $the_class = 'textfield'; - $type = $fields_type[$i]; - if ($type == 'date') { - $the_class .= ' datefield'; - } elseif ($type == 'datetime' || substr($type, 0, 9) == 'timestamp') { - $the_class .= ' datetimefield'; - } - echo ' ' . "\n"; - }; - ?> + echo PMA_getForeignFields_Values($foreigners, $foreignData, $field, $fields_type, $i, $db, $table, $titles,$GLOBALS['cfg']['ForeignKeyMaxLimit'], '' ); + + ?> + + + + do the work */ else { + echo "ZZ"; // Builds the query $sql_query = 'SELECT ' . (isset($distinct) ? 'DISTINCT ' : ''); @@ -354,88 +255,18 @@ else { $sql_query .= ' WHERE ' . $where; } else { $w = $charsets = array(); - $unary_operators = array( - 'IS NULL' => 1, - 'IS NOT NULL' => 1, - "= ''" => 1, - "!= ''" => 1 - ); $cnt_func = count($func); reset($func); while (list($i, $func_type) = each($func)) { - list($charsets[$i]) = explode('_', $collations[$i]); - if (isset($unary_operators[$func_type])) { - $fields[$i] = ''; - $w[] = PMA_backquote($names[$i]) . ' ' . $func_type; - - } elseif (strncasecmp($types[$i], 'enum', 4) == 0) { - if (!empty($fields[$i])) { - if (! is_array($fields[$i])) { - $fields[$i] = explode(',', $fields[$i]); - } - $enum_selected_count = count($fields[$i]); - if ($func_type == '=' && $enum_selected_count > 1) { - $func_type = $func[$i] = 'IN'; - $parens_open = '('; - $parens_close = ')'; - - } elseif ($func_type == '!=' && $enum_selected_count > 1) { - $func_type = $func[$i] = 'NOT IN'; - $parens_open = '('; - $parens_close = ')'; - - } else { - $parens_open = ''; - $parens_close = ''; - } - $enum_where = '\'' . PMA_sqlAddSlashes($fields[$i][0]) . '\''; - for ($e = 1; $e < $enum_selected_count; $e++) { - $enum_where .= ', \'' . PMA_sqlAddSlashes($fields[$i][$e]) . '\''; - } - - $w[] = PMA_backquote($names[$i]) . ' ' . $func_type . ' ' . $parens_open . $enum_where . $parens_close; - } - - } elseif ($fields[$i] != '') { - // For these types we quote the value. Even if it's another type (like INT), - // for a LIKE we always quote the value. MySQL converts strings to numbers - // and numbers to strings as necessary during the comparison - if (preg_match('@char|binary|blob|text|set|date|time|year@i', $types[$i]) || strpos(' ' . $func_type, 'LIKE')) { - $quot = '\''; - } else { - $quot = ''; - } - - // LIKE %...% - if ($func_type == 'LIKE %...%') { - $func_type = 'LIKE'; - $fields[$i] = '%' . $fields[$i] . '%'; - } - if ($func_type == 'REGEXP ^...$') { - $func_type = 'REGEXP'; - $fields[$i] = '^' . $fields[$i] . '$'; - } - - if ($func_type == 'IN (...)' || $func_type == 'NOT IN (...)' || $func_type == 'BETWEEN' || $func_type == 'NOT BETWEEN') { - $func_type = str_replace(' (...)', '', $func_type); - - // quote values one by one - $values = explode(',', $fields[$i]); - foreach ($values as &$value) - $value = $quot . PMA_sqlAddSlashes(trim($value)) . $quot; - - if ($func_type == 'BETWEEN' || $func_type == 'NOT BETWEEN') - $w[] = PMA_backquote($names[$i]) . ' ' . $func_type . ' ' . (isset($values[0]) ? $values[0] : '') . ' AND ' . (isset($values[1]) ? $values[1] : ''); - else - $w[] = PMA_backquote($names[$i]) . ' ' . $func_type . ' (' . implode(',', $values) . ')'; - } - else { - $w[] = PMA_backquote($names[$i]) . ' ' . $func_type . ' ' . $quot . PMA_sqlAddSlashes($fields[$i]) . $quot;; - } - - } // end if + + list($charsets[$i]) = explode('_', $collations[$i]); + $unaryFlag = (isset($GLOBALS['cfg']['UnaryOperators'][$func_type]) && $GLOBALS['cfg']['UnaryOperators'][$func_type] == 1) ? true : false; + $whereClause = PMA_tbl_search_getWhereClause($fields[$i],$names[$i], $types[$i], $collations[$i], $func_type, $unaryFlag); + if($whereClause) + $w[] = $whereClause; + } // end for - + //print_r($w); if ($w) { $sql_query .= ' WHERE ' . implode(' AND ', $w); } @@ -444,7 +275,6 @@ else { if ($orderField != '--nil--') { $sql_query .= ' ORDER BY ' . PMA_backquote($orderField) . ' ' . $order; } // end if - require './sql.php'; } diff --git a/tbl_zoom_select.php b/tbl_zoom_select.php new file mode 100644 index 0000000000..cdb784d00b --- /dev/null +++ b/tbl_zoom_select.php @@ -0,0 +1,447 @@ + displays the selection form + */ + + // Gets some core libraries + require_once './libraries/tbl_common.php'; + //$err_url = 'tbl_select.php' . $err_url; + $url_query .= '&goto=tbl_select.php&back=tbl_select.php'; + + /** + * Gets tables informations + */ + require_once './libraries/tbl_info.inc.php'; + + /** + * Displays top menu links + */ + require_once './libraries/tbl_links.inc.php'; + + if (! isset($goto)) { + $goto = $GLOBALS['cfg']['DefaultTabTable']; + } + // Defines the url to return to in case of error in the next sql statement + $err_url = $goto . '?' . PMA_generate_common_url($db, $table); + + // Gets the list and number of fields + + list($fields_list, $fields_type, $fields_collation, $fields_null) = PMA_tbl_getFields($table,$db); + $fields_cnt = count($fields_list); + + // retrieve keys into foreign fields, if any + // check also foreigners even if relwork is FALSE (to get + // foreign keys from innodb) + $foreigners = PMA_getForeigners($db, $table); + $flag = 1; + $tbl_fields_type = $tbl_fields_collation = $tbl_fields_null = array(); + if(!isset($zoom_submit) && !isset($inputs)) + $dataLabel = PMA_getDisplayField($db,$table); + + ?> + + +
+
+ + + +> + + + + /> + + + + +
+ + + + + +"; + } + +?> + + + + + + + + + + + + + + + + + + + +
"; + echo __("Additional search criteria"); + echo "
+ + + + + +
+ + + + + + + + + + +
         +
+ +
+ +
+
+ + +
+ + + $row[$inputs[0]], $inputs[1] => $row[$inputs[1]], 'where_clause' => $uniqueCondition[0]); + else + $data[] = array($inputs[0] => $row[$inputs[0]], $inputs[1] => $row[$inputs[1]], $dataLabel => $row[$dataLabel], 'where_clause' => $uniqueCondition[0]); + } + +?> + + +
> + + + + +
+ +
+ +
+
+ +
+
+ +
+ + +
+ +
+
+ + +