summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume (ioguix) de Rorthais2009-10-01 23:26:01 +0000
committerGuillaume (ioguix) de Rorthais2009-10-01 23:26:01 +0000
commita6da6f8ae536a8da187edd2ec5bdbe963452e790 (patch)
treebba4c0bb6b44df12dafedc8544647f2c1262b3db
parent6513b379700affca49a95a11a4772f3b66fd9897 (diff)
Refactor and improve the auto-complete foreign-key support in the insert form
- clean the code - add support for multi-colmn foreign key - show all the table column in the choice list - add pagination in the list to browse prev/next 11 values - use of jquery lib
-rw-r--r--aciur.js400
-rw-r--r--ajax-ac-insert.php74
-rw-r--r--autocomplete.php20
-rwxr-xr-xclasses/database/Postgres.php24
-rw-r--r--classes/database/Postgres73.php8
-rw-r--r--classes/database/Postgres81.php8
-rw-r--r--js/ac_insert_row.js219
-rw-r--r--lang/english.php1
-rw-r--r--lang/recoded/english.php1
-rw-r--r--tables.php128
-rw-r--r--themes/default/global.css45
11 files changed, 443 insertions, 485 deletions
diff --git a/aciur.js b/aciur.js
deleted file mode 100644
index 8ce0d5a6..00000000
--- a/aciur.js
+++ /dev/null
@@ -1,400 +0,0 @@
-var nkeycode = 0;
-var curopt = 0;
-var bnsr = false;
-var aSuggests = [];
-var otxb = null;
-var xo = null;
-var acv = false;
-var fac_c = 1;
-var c_fac_c = 1;
-var asg = new Array();
-var rasg = new Array();
-var rasgc = new Array();
-var iMoR = false;
-var g_c_ns = "";
-var g_c_tb = "";
-var g_c_fk = "";
-var g_c_sid = "";
-var g_c_db = "";
-var g_c_v = "";
-var g_i_ac = true;
-
-document.body.onclick=function() {deAC();};
-
-function aS(aSuggests) {
- if (aSuggests.length > 0) {
- tA(aSuggests[curopt]);
- }
-}
-
-function hKU(oEvent) {
- nkeycode = oEvent.keyCode;
- var iKeyCode = oEvent.keyCode;
- if(iKeyCode!=13) {
- if(iKeyCode==40 && (curopt+1)<=(rasg.length-1)) {
- curopt++;
- bnsr = true;
- tA(rasg[curopt]);
- htr(document.getElementById('option_tr_'+(curopt+1)));
- } else if(iKeyCode==38 && ((curopt-1)<=(rasg.length-1) && (curopt-1)>=0)) {
- curopt--;
- bnsr = true;
- tA(rasg[curopt]);
- htr(document.getElementById('option_tr_'+(curopt+1)));
- } else {
- if ( (iKeyCode!=8 && iKeyCode <= 32) || (iKeyCode >= 33 && iKeyCode <= 46) || (iKeyCode >= 112 && iKeyCode <= 123)) {
- } else {
- var sTextboxValue = otxb.value.toLowerCase();
- SG1(sTextboxValue);
- bnsr = false;
- }
- }
- }
- else {
- if(acv) {
- hideAC();
- }
- return false;
- }
-}
-
-function initac(ns,tb,fk,sid,db,v) {
- g_c_ns = ns;
- g_c_tb = tb;
- g_c_fk = fk;
- g_c_sid = sid;
- g_c_db = db;
-}
-
-function sR(p_start,p_len) {
-if(!bnsr) {
- if (otxb.createTextRange) {
- var oRange = otxb.createTextRange();
- oRange.moveStart("character", p_start);
- oRange.moveEnd("character", p_len - otxb.value.length);
- oRange.select();
- } else if (otxb.setSelectionRange) {
- otxb.setSelectionRange(p_start, p_len);
- }
-} else {
- if (otxb.createTextRange) {
- var oRange = otxb.createTextRange();
- oRange.moveStart("character",p_len);
- oRange.moveEnd("character",p_len);
- oRange.select();
- }
- else if (otxb.setSelectionRange) {
- otxb.setSelectionRange(p_len,p_len);
- }
- }
- otxb.focus();
-}
-
-function tA(p_suggestion) {
- if(nkeycode!=8) {
- if ((otxb.createTextRange || otxb.setSelectionRange) && p_suggestion) {
- var p_len = otxb.value.length;
- otxb.value = p_suggestion;
- sR(p_len, p_suggestion.length);
- }
- }
-}
-
-function findPosX(obj) {
- if(obj) {
- var curleft = 0;
- if (obj.offsetParent) {
- while (obj.offsetParent)
- {
- curleft += obj.offsetLeft
- obj = obj.offsetParent;
- }
- }
- else if (obj.x)
- curleft += obj.x;
- return curleft;
- }
-}
-
-function findPosY(obj) {
- if(obj) {
- var curtop = 0;
- var n = 0;
- if (obj.y) {
- curtop += obj.y;
- }
- else if (obj.offsetParent) {
- while (obj.offsetParent) {
- curtop += obj.offsetTop;
- obj = obj.offsetParent;
- }
- }
- return curtop;
- }
-}
-
-function hideAC() {
- var d = document.getElementById('ac');
- d.innerHTML='';
- d.style.zIndex=0;
- d.style.border=0;
- d.style.visibility='hidden';
- acv = false;
-}
-
-function deAC() {
- if(acv) {
- hideAC();
- }
-}
-
-function makenormaltr(id) {
- var tr = document.getElementById(id);
- tr.style.backgroundColor='white';tr.style.color='black';
-}
-
-function fillinval(val) {
- var tx = document.getElementById('fac' + c_fac_c);
- tx.value=val;
- hideAC();
-}
-
-function showAC() {
- var d = document.getElementById('ac');
- d.innerHTML='';
- var tb = document.getElementById('fac'+c_fac_c+'_ph');
- d.style.top=findPosY(tb)+'px';
- d.style.visibility='visible';
- acv = true;
- d.style.left=findPosX(tb)+'px';
- d.style.border='1px solid black';
- d.style.position='absolute';
- d.style.width=(document.getElementById('aciwp' + c_fac_c).offsetWidth-3) + "px";
- d.style.zIndex=20;
- d.style.backgroundColor='white';
- d.style.margin='0px';
- d.style.padding='0px';
- d.style.textAlign='left';
- document.getElementById("ac_form").onsubmit=function(){return false;};
- return d;
-}
-
-function aftr() {
- for(i=0;i<rasg.length;i++) {
- ftr(document.getElementById('option_tr_'+(i+1)));
- }
-}
-
-function htr(obj) {
- aftr();
- obj.style.backgroundColor='#3d80df';
- obj.style.color='white';
-}
-
-function ftr(obj) {
- obj.style.backgroundColor='white';
- obj.style.color='black';
-}
-
-function buildSelectOptions() {
- if(rasg.length>0) {
- var t = document.createElement('table');
- var tb = document.createElement('tbody');
- var d = showAC();
- t.style.width='100%';
- t.style.margin='0px';
- t.style.padding='0px';
- t.cellPadding='0px';
- t.cellSpacing='0px';
- t.style.zIndex=6;
- t.style.visibility='visible';
- for(i=0;i<rasg.length;i++) {
- var tr = document.createElement('tr');
- var td = document.createElement('td');
- var tx = document.createTextNode(rasg[i]);
- if(i==0) {
- td.style.color='white';
- td.id='option_tr_1';
- td.style.backgroundColor='#3d80df';
- } else {
- td.id='option_tr_' + (i+1);
- td.style.color='black';
- td.style.backgroundColor='white';
- }
- td.onmouseover=function() { htr(this); this.style.cursor='pointer'; };
- td.onmouseout=function() { ftr(this); this.style.cursor='normal'; };
- td.onclick=function() { fillinval(this.firstChild.innerHTML); };
- td.style.paddingLeft='10px';
- td.style.fontSize='9pt';
- td.style.fontFamily='Arial';
- td.appendChild(tx);
- tr.appendChild(td);
- tb.appendChild(tr);
- }
- t.appendChild(tb);
- d.appendChild(t);
- } else {
- hideAC();
- }
-}
-
-function dF(v) {
- g_i_ac = document.getElementById(v).checked;
-}
-
-function makeAC(tx,n,ns,tb,fk,sid,db) {
- otxb = document.getElementById(tx);
- c_fac_c = n;
- if(document.getElementById('no_ac').checked) {
- if(acv) {
- hideAC();
- }
- otxb = document.getElementById(tx);
- v = otxb.value;
- curopt = 0;
- bnsr = false;
- otxb.onkeyup = function (oEvent) {
- if (!oEvent) { oEvent = window.event; } hKU(oEvent);
- };
- initac(ns, tb,fk,sid,db,v);
- } else {
- otxb.onkeyup = function() {};
- }
-}
-
-function gfacc(s) {
- return s.substr(3);
-}
-
-function iA(parent, node, referenceNode) {
- parent.insertBefore(node, referenceNode.nextSibling);
-}
-
-var asg = new Array();
-var rasg = new Array();
-var rasgc = new Array();
-
-function rS(tx) {
- rasg = [];
- rasgc = [];
- var sTextboxValue = otxb.value.toLowerCase();
- if (sTextboxValue.length > 0) {
- for (var i=0;i<asg.length;i++) {
- var c = asg[i];
- if (c.indexOf(sTextboxValue) == 0) {
- rasg.push(asg[i]);
- }
- }
- buildSelectOptions();
- } else {
- hideAC();
- }
-}
-
-function SG1(s) {
- if(!iMoR && s.length>0) {
- pr("autocomplete.php","ns="+g_c_ns+"&tb="+g_c_tb+"&database="+g_c_db+"&server="+g_c_sid+"&fk="+g_c_fk+"&v="+escapeHTML(s)+"");
- } else if(!s.length) {
- hideAC();
- }
-}
-
-function escapeHTML(s) {
- s = escape(s);
- s = s.replace(/\//g,"%2F");
- s = s.replace(/\?/g,"%3F");
- s = s.replace(/=/g,"%3D");
- s = s.replace(/&/g,"%26");
- s = s.replace(/@/g,"%40");
- return s;
-}
-
-function rEB(v) {
- var il = document.getElementsByTagName('input');
- for(i in il) {
- if(il[i].className=='ac_field' || il[i].className=='normal_field') {
- if(v==false) {
- il[i].className='normal_field';
- il[i].setAttribute("autocomplete",'on');
- } else {
- il[i].className='ac_field';
- il[i].setAttribute("autocomplete",'off');
- }
- }
- }
-}
-
-function pgsg(v) {
- prgsp(v);
-}
-
-function prgsp(v) {
- var d = v.split("PPA_EOF;|"); // pondered using RegExp here, but overhead to gain ratio is not convincing
- var l = d.length;
- asg = [];
- for(i=0;i<l;i++) {
- asg.push(d[i]);
- }
- rS(otxb);
- aS(rasg);
-}
-
-function rS(tx) {
- rasg = [];
- rasgc = [];
- var sTextboxValue = otxb.value.toLowerCase();
- if (sTextboxValue.length > 0) {
- for (var i=0;i<asg.length;i++) {
- var c = asg[i];
- if (c.indexOf(sTextboxValue) == 0) {
- rasg.push(asg[i]);
- }
- }
- buildSelectOptions();
- } else {
- hideAC();
- }
-}
-
-function pr(pg,sr) {
- mii();
- xo.open("POST",pg,true);
- xo.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
- xo.onreadystatechange=xoc;
- xo.send(sr);
-}
-
-function mii() {
- if(!xo) {
- /*@cc_on @*//*@if (@_jscript_version >= 5) try {
- xo = new ActiveXObject("Msxml2.XMLHTTP");
- }
- catch (e) {
- try {
- xo = new ActiveXObject("Microsoft.XMLHTTP");
- }
- catch (E) {
- xo = false;
- }
- }
- @end @*/if (!xo && typeof XMLHttpRequest!='undefined') {
- xo = new XMLHttpRequest();
- }
- }
-}
-
-function xoc(){
- iMoR = true;
- szr = "";
- if (xo.readyState==4) {
- if(xo.status==200) {
- pgsg(xo.responseText);
- iMoR = false;
- }
- else {
- alert("Oops, something unexpected happened, try again");
- window.location.reload();
- }
- }
-}
-
diff --git a/ajax-ac-insert.php b/ajax-ac-insert.php
new file mode 100644
index 00000000..ee338688
--- /dev/null
+++ b/ajax-ac-insert.php
@@ -0,0 +1,74 @@
+<?php
+ include_once('./libraries/lib.inc.php');
+
+ if(isset($_POST['offset']))
+ $offset = " OFFSET {$_POST['offset']}";
+ else {
+ $_POST['offset'] = 0;
+ $offset = " OFFSET 0";
+ }
+ $keyspos = array_combine($_POST['fkeynames'], $_POST['keys']);
+ $keysnames = array_combine($_POST['fkeynames'], $_POST['keynames']);
+
+ $q = "SELECT *
+ FROM \"{$_POST['f_schema']}\".\"{$_POST['f_table']}\"
+ WHERE \"{$_POST['fkeynames'][$_POST['fattpos']]}\"::text LIKE '{$_POST['fvalue']}%'
+ ORDER BY \"{$_POST['fkeynames'][$_POST['fattpos']]}\" LIMIT 12 {$offset};";
+
+ $res = $data->selectSet($q);
+
+ if (!$res->EOF) {
+ echo "<table class=\"ac_values\">";
+ echo '<tr>';
+ foreach (array_keys($res->fields) as $h) {
+ echo '<th>';
+
+ if (in_array($h,$_POST['fkeynames']))
+ echo '<img src="'. $misc->icon('ForeignKey') .'" alt="[referenced key]" />';
+
+ echo htmlentities($h), '</th>';
+
+ }
+ echo "</tr>\n";
+ $i=0;
+ while ((!$res->EOF) && ($i < 11)) {
+ echo "<tr class=\"acline\">";
+ foreach ($res->fields as $n => $v) {
+ if (in_array($n,$_POST['fkeynames']))
+ echo "<td><a href=\"javascript:void(0)\" class=\"fkval\" name=\"{$keyspos[$n]}\">",htmlentities($v), "</a></td>";
+ else
+ echo "<td><a href=\"javascript:void(0)\">", htmlentities($v), "</a></td>";
+ }
+ echo "</tr>\n";
+ $i++;
+ $res->moveNext();
+ }
+ echo "</table>\n";
+
+ $page_tests='';
+
+ $js = "<script type=\"text/javascript\">\n";
+
+ if ($_POST['offset']) {
+ echo "<a href=\"javascript:void(0)\" id=\"fkprev\">&lt;&lt; Prev</a>";
+ $js.= "fkl_hasprev=true;\n";
+ }
+ else
+ $js.= "fkl_hasprev=false;\n";
+
+ if ($res->recordCount() == 12) {
+ $js.= "fkl_hasnext=true;\n";
+ echo "&nbsp;&nbsp;&nbsp;<a href=\"javascript:void(0)\" id=\"fknext\">Next &gt;&gt;</a>";
+ }
+ else
+ $js.= "fkl_hasnext=false;\n";
+
+ echo $js ."</script>";
+ }
+ else {
+ printf("<p>{$lang['strnofkref']}</p>", "\"{$_POST['f_schema']}\".\"{$_POST['f_table']}\".\"{$_POST['fkeynames'][$_POST['fattpos']]}\"");
+
+ if ($_POST['offset'])
+ echo "<a href=\"javascript:void(0)\" class=\"fkprev\">Prev &lt;&lt;</a>";
+ }
+?>
diff --git a/autocomplete.php b/autocomplete.php
deleted file mode 100644
index 5a662dfe..00000000
--- a/autocomplete.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
- include_once('libraries/lib.inc.php');
- $data->clean($_REQUEST['tb']);
- $data->clean($_REQUEST['ns']);
- $data->clean($_REQUEST['fk']);
- $data->clean($_REQUEST['v']);
-
- // FIXME: At some point this should be schema qualified
- $szSQL = 'SELECT * FROM "' . $_REQUEST['ns'] . '"."' . $_REQUEST['tb'] . '" WHERE "' . $_REQUEST['fk']
- . "\"::text LIKE '" . $_REQUEST['v'] . "%' ORDER BY \"". $_REQUEST['fk'] .'" LIMIT 11';
-
- $objRes = $data->selectSet($szSQL);
- $arrayRes = array();
- while (!$objRes->EOF) {
- $arrayRes[] = $objRes->fields[$_REQUEST['fk']];
- $objRes->moveNext();
- }
-
- echo implode('PPA_EOF;|', $arrayRes);
-?>
diff --git a/classes/database/Postgres.php b/classes/database/Postgres.php
index 07066d81..e7c88411 100755
--- a/classes/database/Postgres.php
+++ b/classes/database/Postgres.php
@@ -250,8 +250,9 @@ class Postgres extends ADODB_base {
* @param $value The value of the field. Note this could be 'numeric(7,2)' sort of thing...
* @param $type The database type of the field
* @param $actions An array of javascript action name to the code to execute on that action
+ * @param $extra Some extra attributes to add.
*/
- function printField($name, $value, $type, $actions = array(),$szExtra="") {
+ function printField($name, $value, $type, $actions = array(), $extra='') {
global $lang;
// Determine actions string
@@ -276,7 +277,7 @@ class Postgres extends ADODB_base {
echo "</select>\n";
}
else {
- echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} {$szExtra} />\n";
+ echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} {$extra} />\n";
}
break;
case 'bytea':
@@ -303,7 +304,7 @@ class Postgres extends ADODB_base {
echo "</textarea>\n";
break;
default:
- echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} {$szExtra} />\n";
+ echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} {$extra} />\n";
break;
}
}
@@ -1163,7 +1164,6 @@ class Postgres extends ADODB_base {
AND pc.relkind='S'
) IS NOT NULL AS attisserial,
pg_catalog.col_description(a.attrelid, a.attnum) AS comment
-
FROM
pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
ON a.attrelid=adef.adrelid
@@ -3268,11 +3268,10 @@ class Postgres extends ADODB_base {
// get the max number of col used in a constraint for the table
$sql = "SELECT DISTINCT
- max(SUBSTRING(array_dims(c.conkey) FROM E'^\\\[.*:(.*)\\\]$')) as nb
- FROM
- pg_catalog.pg_constraint AS c
- JOIN pg_catalog.pg_class AS r ON (c.conrelid = r.oid)
- JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
+ max(SUBSTRING(array_dims(c.conkey) FROM E'^\\\[.*:(.*)\\\]$')) as nb
+ FROM pg_catalog.pg_constraint AS c
+ JOIN pg_catalog.pg_class AS r ON (c.conrelid=r.oid)
+ JOIN pg_catalog.pg_namespace AS ns ON (r.relnamespace=ns.oid)
WHERE
r.relname = '$table' AND ns.nspname='". $this->_schema ."'";
@@ -3283,10 +3282,11 @@ class Postgres extends ADODB_base {
$sql = '
SELECT
- c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc,
+ c.oid AS conid, c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc,
ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
- r2.relname as f_table, f1.attname as p_field, f2.attname as f_field,
- pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment
+ r2.relname as f_table, f1.attname as p_field, f1.attnum AS p_attnum, f2.attname as f_field,
+ f2.attnum AS f_attnum, pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment,
+ c.conrelid, c.confrelid
FROM
pg_catalog.pg_constraint AS c
JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
diff --git a/classes/database/Postgres73.php b/classes/database/Postgres73.php
index 87315b97..1ebc89b4 100644
--- a/classes/database/Postgres73.php
+++ b/classes/database/Postgres73.php
@@ -426,8 +426,8 @@ class Postgres73 extends Postgres74 {
}
$sql = "
- SELECT contype, conname, consrc, ns1.nspname as p_schema, sub.relname as p_table,
- f_schema, f_table, p_field, f_field, indkey
+ SELECT oid AS conid, contype, conname, consrc, ns1.nspname as p_schema, sub.relname as p_table,
+ f_schema, f_table, p_field, f_field, indkey, confrelid
FROM (
SELECT
contype, conname,
@@ -436,8 +436,8 @@ class Postgres73 extends Postgres74 {
ELSE
'CHECK (' || consrc || ')'
END AS consrc, r1.relname,
- f1.attname as p_field, ns2.nspname as f_schema, r2.relname as f_table,
- conrelid, r1.relnamespace, f2.attname as f_field, NULL AS indkey
+ f1.attname as p_field, f1.attnum AS p_attnum, ns2.nspname as f_schema, r2.relname as f_table,
+ conrelid, r1.relnamespace, f2.attname as f_field, f2.attnum AS f_attnum, NULL AS indkey
FROM
pg_catalog.pg_constraint AS c
JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
diff --git a/classes/database/Postgres81.php b/classes/database/Postgres81.php
index f8479a3f..d387592f 100644
--- a/classes/database/Postgres81.php
+++ b/classes/database/Postgres81.php
@@ -167,10 +167,12 @@ class Postgres81 extends Postgres82 {
$sql = '
SELECT
- c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid,true) AS consrc,
+ c.oid AS conid, c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid,true) AS consrc,
ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
- r2.relname as f_table, f1.attname as p_field, f2.attname as f_field,
- pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment
+ r2.relname as f_table, f1.attname as p_field, f1.attnum AS p_attnum,
+ f2.attname as f_field, f2.attnum AS f_attnum,
+ pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment,
+ confrelid
FROM
pg_catalog.pg_constraint AS c
JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
diff --git a/js/ac_insert_row.js b/js/ac_insert_row.js
new file mode 100644
index 00000000..ae062a3d
--- /dev/null
+++ b/js/ac_insert_row.js
@@ -0,0 +1,219 @@
+var fkl_hasnext=false;
+var fkl_hasprev=false;
+
+/* hide the value list */
+function hideAc() {
+ jQuery.ppa.o=0;
+ with (jQuery.ppa) {
+ fklist.hide();
+ fkbg.hide();
+ }
+}
+
+/* enable/disable auto-complete feature */
+function triggerAc(ac) {
+ if (ac) {
+ jQuery.ppa.attrs
+ .keyup(autocomplete)
+ .keypress(move)
+ .addClass('ac_field');
+ }
+ else {
+ jQuery.ppa.attrs
+ .removeClass('ac_field')
+ .unbind('keyup',autocomplete)
+ .unbind('keypress',move);
+ }
+}
+
+/* select the given index value and highlight it */
+function selectVal(index) {
+ if (index == jQuery.ppa.i)
+ return;
+
+ // we catch the header as well so it takes th index 0
+ var trs = jQuery.ppa.fklist.find('tr');
+
+ // change colors for unselected
+ if (jQuery.ppa.i > 0)
+ trs.eq(jQuery.ppa.i).find('*').css({
+ 'background-color': '#fff',
+ 'color': ''
+ });
+
+ // change colors for newly selected
+ trs.eq(index).find('*').css({
+ 'background-color': '#3d80df',
+ 'color': '#fff'
+ });
+
+ jQuery.ppa.i = index;
+}
+
+function openlist(e) {
+ var elt = jQuery(e);
+ var attnum = elt.attr('id').match(/\d+/)[0];
+ var conid = attrs['attr_'+attnum];
+
+ var constr = constrs["constr_" + conid];
+
+ // get the changed attribute position in the arrays
+ for (i=0; (constr.pattnums[i] != attnum);i++);
+
+ var datas = {
+ fattpos: i,
+ fvalue: e.value,
+ database: database,
+ 'keys[]': constr.pattnums,
+ 'keynames[]': constr.pattnames,
+ 'fkeynames[]': constr.fattnames,
+ f_table: constr.f_table,
+ f_schema: constr.f_schema,
+ offset: jQuery.ppa.o
+ };
+
+ jQuery.ajax({
+ url: 'ajax-ac-insert.php?server=' + server,
+ type: 'post',
+ data: datas,
+ success: function (ret) {
+ jQuery.ppa.i = 0;
+ jQuery.ppa.fkbg.show();
+ with(jQuery.ppa.fklist) {
+ html(ret);
+ appendTo('#row_att_'+ attnum);
+ css('width',elt.css('width'));
+ show();
+ jQuery.ppa.numrow = find('tr').length;
+ }
+ }
+ });
+}
+
+
+/* move the cursor down or up,
+ * load available next/prev values if going out of bound */
+function move(event) {
+ /* selecting next value down.
+ * if the list is closed, it will open next */
+ if(event.keyCode == 40) {
+ if (jQuery.ppa.fklist[0].style.display == 'block') {
+ if ((jQuery.ppa.i + 1) < jQuery.ppa.numrow) {
+ selectVal(jQuery.ppa.i + 1);
+ }
+ else if (fkl_hasnext == true) {
+ jQuery.ppa.o+=11;
+ openlist(this);
+ }
+ }
+ else {
+ openlist(this);
+ }
+ }
+ /* selecting prev value up */
+ else if(event.keyCode == 38) {
+ if ((jQuery.ppa.i - 1) > 0) {
+ selectVal(jQuery.ppa.i - 1);
+ }
+ else if ((fkl_hasprev == true) && (jQuery.ppa.i == 1)) {
+ jQuery.ppa.o-=11;
+ openlist(this);
+ }
+ else {
+ selectVal(jQuery.ppa.numrow -1);
+ }
+ }
+}
+
+/* open/update the value list on keyup event */
+function autocomplete(event) {
+
+ /* if pressing enter, fire a click on the selected line */
+ if (event.keyCode == 13) {
+ if (jQuery.ppa.i > 0) {
+ jQuery.ppa.fklist.find('tr').eq(jQuery.ppa.i).click();
+ }
+ hideAc();
+ return false;
+ }
+ /* ignoring 38:up and 40:down */
+ else if ( event.keyCode == 38 || event.keyCode == 40 ) {
+ return false;
+ }
+ /* ignoring 9:tab, 37:left, 39:right, 16:shift, ctrl: 17, alt:18, 20:lockmaj */
+ else if ( event.keyCode == 9 || event.keyCode == 37 || event.keyCode == 39
+ || event.keyCode == 16 || event.keyCode == 17
+ || event.keyCode == 18 || event.keyCode == 20) {
+ return true;
+ }
+ /* esc */
+ else if (event.keyCode == 27) {
+ hideAc();
+ }
+ /* request the list of possible values asynchronously */
+ else {
+ openlist(this);
+ }
+
+ return true;
+}
+
+/* bind actions on values lines: hover for style change, click for select */
+with(jQuery('tr.acline')) {
+ live('mouseover', function () {
+ selectVal(jQuery('table.ac_values tr').index(this));
+ });
+
+ live('click', function () {
+ var a = jQuery(this).find('td > a.fkval');
+
+ for (i=0; i < a.length; i++) {
+ jQuery('input[name=values\\['+ a[i].name +'\\]]').val(a[i].innerHTML);
+ }
+ hideAc();
+ });
+}
+
+jQuery('#fkprev').live('click', function () {
+ jQuery.ppa.o-=11;
+ /* get the field that is the previous html elt from the #fklist
+ * and trigger its keyup to refresh the list */
+ jQuery('#fklist').prev().keyup().focus();
+});
+
+jQuery('#fknext').live('click', function () {
+ jQuery.ppa.o+=11;
+ /* get the field that is the previous html elt from the #fklist
+ * and trigger its keyup to refresh the list */
+ jQuery('#fklist').prev().keyup().focus();
+});
+
+jQuery(document).ready(function () {
+ /* register some global value in the ppa namespace */
+ jQuery.ppa = {
+ fklist: jQuery('#fklist'),
+ attrs: jQuery('input[id^=attr_]'),
+ fkbg: jQuery('#fkbg'),
+ i:0, // selected value indice
+ o:0 // offset when navigating prev/next
+ };
+
+ /* close the list when clicking outside of it */
+ jQuery.ppa.fkbg.click(function (e) {
+ hideAc();
+ });
+
+ /* do not submit the form when selecting a value by pressing enter */
+ jQuery.ppa.attrs
+ .keydown(function (e) {
+ if (e.keyCode == 13 && jQuery.ppa.fklist[0].style.display == 'block')
+ return false;
+ });
+
+ /* enable/disable auto-complete according to the checkbox */
+ triggerAc(
+ jQuery('#no_ac').click(function () {
+ triggerAc(this.checked);
+ })[0].checked
+ );
+});
diff --git a/lang/english.php b/lang/english.php
index 66c67045..d1834e4f 100644
--- a/lang/english.php
+++ b/lang/english.php
@@ -214,6 +214,7 @@
$lang['strinsertrow'] = 'Insert row';
$lang['strrowinserted'] = 'Row inserted.';
$lang['strrowinsertedbad'] = 'Row insert failed.';
+ $lang['strnofkref'] = 'There is no matching value in the foreign key %s.';
$lang['strrowduplicate'] = 'Row insert failed, attempted to do duplicate insert.';
$lang['streditrow'] = 'Edit row';
$lang['strrowupdated'] = 'Row updated.';
diff --git a/lang/recoded/english.php b/lang/recoded/english.php
index 4ebe06b4..c231e2de 100644
--- a/lang/recoded/english.php
+++ b/lang/recoded/english.php
@@ -214,6 +214,7 @@
$lang['strinsertrow'] = 'Insert row';
$lang['strrowinserted'] = 'Row inserted.';
$lang['strrowinsertedbad'] = 'Row insert failed.';
+ $lang['strnofkref'] = 'There is no matching value for this foreign key in %s.';
$lang['strrowduplicate'] = 'Row insert failed, attempted to do duplicate insert.';
$lang['streditrow'] = 'Edit row';
$lang['strrowupdated'] = 'Row updated.';
diff --git a/tables.php b/tables.php
index 313c484f..23a03471 100644
--- a/tables.php
+++ b/tables.php
@@ -458,7 +458,7 @@
global $data, $misc, $conf;
global $lang;
- $bAllowAC = (($conf['autocomplete'] != 'disable') ? TRUE : FALSE);
+ $auto_complete = ($conf['autocomplete'] != 'disable');
if ($confirm) {
$misc->printTrail('table');
@@ -466,21 +466,75 @@
$misc->printMsg($msg);
$attrs = $data->getTableAttributes($_REQUEST['table']);
- if($bAllowAC) {
- $constraints = $data->getConstraintsWithFields($_REQUEST['table']);
-
- $arrayLocals = array();
- $arrayRefs = array();
- $nC = 0;
- while(!$constraints->EOF) {
- // FIXME: add a better support for FKs on multi columns
- if ($constraints->fields['contype'] == 'f') {
- $arrayLocals[$nC] = $constraints->fields['p_field'];
- $arrayRefs[$nC] = array($constraints->fields['f_schema'], $constraints->fields['f_table'], $constraints->fields['f_field']);
- $nC++;
+ $fksprops = array(
+ 'byconstr' => array(),
+ 'byfield' => array(),
+ );
+ if($auto_complete) {
+ $constrs = $data->getConstraintsWithFields($_REQUEST['table']);
+
+ if (!$constrs->EOF) {
+ $conrelid = $constrs->fields['conrelid'];
+ while(!$constrs->EOF) {
+ if ($constrs->fields['contype'] == 'f') {
+ if (!isset($fksprops['byconstr'][$constrs->fields['conid']])) {
+ $fksprops['byconstr'][$constrs->fields['conid']] = array (
+ 'confrelid' => $constrs->fields['confrelid'],
+ 'f_table' => $constrs->fields['f_table'],
+ 'f_schema' => $constrs->fields['f_schema'],
+ 'pattnums' => array(),
+ 'pattnames' => array(),
+ 'fattnames' => array()
+ );
+ }
+
+ $fksprops['byconstr'][$constrs->fields['conid']]['pattnums'][] = $constrs->fields['p_attnum'];
+ $fksprops['byconstr'][$constrs->fields['conid']]['pattnames'][] = $constrs->fields['p_field'];
+ $fksprops['byconstr'][$constrs->fields['conid']]['fattnames'][] = $constrs->fields['f_field'];
+
+ if (!isset($fksprops['byfield'][$constrs->fields['p_attnum']]))
+ $fksprops['byfield'][$constrs->fields['p_attnum']] = array();
+ $fksprops['byfield'][$constrs->fields['p_attnum']] = $constrs->fields['conid'];
+ }
+ $constrs->moveNext();
+ }
+
+ echo "<script type=\"text/javascript\">\n";
+ echo "var constrs = {};\n";
+ foreach ($fksprops['byconstr'] as $conid => $props) {
+ echo "constrs.constr_{$conid} = {\n";
+ echo 'pattnums: [', implode(',',$props['pattnums']), "],\n";
+ echo "f_table:\"", htmlentities($props['f_table']), "\",\n";
+ echo "f_schema:\"", htmlentities($props['f_schema']), "\",\n";
+ $_='';
+ foreach ($props['pattnames'] as $n) {
+ $_.= ",'". htmlentities($n, ENT_QUOTES) ."'";
+ }
+ echo 'pattnames: [', substr($_, 1), "],\n";
+
+ $_='';
+ foreach ($props['fattnames'] as $n) {
+ $_.= ",'". htmlentities($n, ENT_QUOTES) ."'";
+ }
+
+ echo 'fattnames: [', substr($_, 1), "]\n";
+ echo "};\n";
+ }
+
+ echo "var attrs = {};\n";
+ foreach ($fksprops['byfield'] as $attnum => $cstrs ) {
+ echo "attrs.attr_{$attnum} = {$fksprops['byfield'][$attnum]};\n";
}
- $constraints->moveNext();
+
+ echo "var table='", htmlentities($_REQUEST['table']), "';";
+ echo "var server='", htmlentities($_REQUEST['server']), "';";
+ echo "var database='", htmlentities($_REQUEST['database']), "';";
+ echo "</script>\n";
+
+ echo '<div id="fkbg"></div>';
+ echo '<div id="fklist"></div>';
}
+ else $auto_complete = false;
}
echo "<form action=\"tables.php\" method=\"post\" id=\"ac_form\">\n";
@@ -496,18 +550,6 @@
$fields = array();
while (!$attrs->EOF) {
$fields[$attrs->fields['attnum']] = $attrs->fields['attname'];
- $szValueName = "values[{$attrs->fields['attnum']}]";
- $szEvents = '';
- $szDivPH = '';
- if($bAllowAC) {
- $idxFound = array_search($attrs->fields['attname'], $arrayLocals);
- // In PHP < 4.2.0 array_search returns NULL on failure
- if ($idxFound !== NULL && $idxFound !== FALSE) {
- $szEvent = "makeAC('{$szValueName}',{$i},'{$arrayRefs[$idxFound][0]}','{$arrayRefs[$idxFound][1]}','{$arrayRefs[$idxFound][2]}','{$_REQUEST['server']}','{$_REQUEST['database']}');";
- $szEvents = "onfocus=\"{$szEvent}\" onblur=\"hideAC();document.getElementById('ac_form').onsubmit=function(){return true;};\" onchange=\"{$szEvent}\" id=\"{$szValueName}\" onkeyup=\"{$szEvent}\" autocomplete=\"off\" class='ac_field'";
- $szDivPH = "<div id=\"fac{$i}_ph\"></div>";
- }
- }
$attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']);
// Set up default value if there isn't one already
if (!isset($_REQUEST['values'][$attrs->fields['attnum']]))
@@ -522,32 +564,40 @@
echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">", $misc->printVal($attrs->fields['attname']), "</td>";
echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">\n";
echo $misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod']));
- echo "<input type=\"hidden\" name=\"types[", htmlspecialchars($attrs->fields['attnum']), "]\" value=\"",
+ echo "<input type=\"hidden\" name=\"types[{$attrs->fields['attnum']}]\" value=\"",
htmlspecialchars($attrs->fields['type']), "\" /></td>";
echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">\n";
- echo "<select name=\"format[", htmlspecialchars($attrs->fields['attnum']), "]\">\n";
+ echo "<select name=\"format[{$attrs->fields['attnum']}]\">\n";
echo "<option value=\"VALUE\"", ($_REQUEST['format'][$attrs->fields['attnum']] == 'VALUE') ? ' selected="selected"' : '', ">{$lang['strvalue']}</option>\n";
echo "<option value=\"EXPRESSION\"", ($_REQUEST['format'][$attrs->fields['attnum']] == 'EXPRESSION') ? ' selected="selected"' : '', ">{$lang['strexpression']}</option>\n";
echo "</select>\n</td>\n";
echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">";
// Output null box if the column allows nulls (doesn't look at CHECKs or ASSERTIONS)
if (!$attrs->fields['attnotnull']) {
- echo "<input type=\"checkbox\" name=\"nulls[", htmlspecialchars($attrs->fields['attnum']), "]\"",
+ echo "<input type=\"checkbox\" name=\"nulls[{$attrs->fields['attnum']}]\"",
isset($_REQUEST['nulls'][$attrs->fields['attnum']]) ? ' checked="checked"' : '', " /></td>";
}
else {
echo "&nbsp;</td>";
}
- echo "<td class=\"data{$id}\" id=\"aciwp{$i}\" style=\"white-space:nowrap;\">", $data->printField($szValueName,
- $_REQUEST['values'][$attrs->fields['attnum']], $attrs->fields['type'],array(),$szEvents),$szDivPH ,"</td>";
+ echo "<td class=\"data{$id}\" id=\"row_att_{$attrs->fields['attnum']}\" style=\"white-space:nowrap;\">";
+ if ($auto_complete && isset($fksprops['byfield'][$attrs->fields['attnum']])) {
+ echo $data->printField("values[{$attrs->fields['attnum']}]", $_REQUEST['values'][$attrs->fields['attnum']],
+ 'fktype'/*force FK*/,array(), "id=\"attr_{$attrs->fields['attnum']}\" autocomplete=\"off\"");
+ }
+ else {
+ echo $data->printField("values[{$attrs->fields['attnum']}]", $_REQUEST['values'][$attrs->fields['attnum']],
+ $attrs->fields['type'],array());
+ }
+ echo "</td>\n";
echo "</tr>\n";
$i++;
$attrs->moveNext();
}
echo "</table>\n";
- if($bAllowAC) {
- echo '<script src="aciur.js" type="text/javascript"></script>';
- echo "<div id=\"ac\"></div>";
+ if($auto_complete) {
+ echo "<script src=\"libraries/js/jquery.js\" type=\"text/javascript\"></script>";
+ echo "<script src=\"js/ac_insert_row.js\" type=\"text/javascript\"></script>";
}
if (!isset($_SESSION['counter'])) { $_SESSION['counter'] = 0; }
@@ -559,12 +609,14 @@
echo "<p><input type=\"submit\" name=\"insert\" value=\"{$lang['strinsert']}\" />\n";
echo "<input type=\"submit\" name=\"insertandrepeat\" value=\"{$lang['strinsertandrepeat']}\" />\n";
echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n";
- if($bAllowAC) {
- $szChecked = $conf['autocomplete'] != 'default off' ? 'checked="checked"' : '';
- echo "<input type=\"checkbox\" name=\"no_ac\" id=\"no_ac\" onclick=\"rEB(this.checked);\" value=\"1\" {$szChecked} /><label for='no_ac' onmouseover='this.style.cursor=\"pointer\";'>{$lang['strac']}</label>\n";
+
+ if($auto_complete) {
+ if ($conf['autocomplete'] != 'default off')
+ echo "<input type=\"checkbox\" id=\"no_ac\" value=\"1\" checked=\"checked\" /><label for=\"no_ac\">{$lang['strac']}</label>\n";
+ else
+ echo "<input type=\"checkbox\" id=\"no_ac\" value=\"0\" /><label for=\"no_ac\">{$lang['strac']}</label>\n";
}
echo "</p>\n";
- echo "<script type=\"text/javascript\">rEB(document.getElementById('no_ac').checked);</script>";
}
else {
echo "<p>{$lang['strnofieldsforinsert']}</p>\n";
diff --git a/themes/default/global.css b/themes/default/global.css
index 48522239..1584a9df 100644
--- a/themes/default/global.css
+++ b/themes/default/global.css
@@ -5,7 +5,6 @@
*/
/** ELEMENTS */
-
body, td
{
background-color: #FFFFFF;
@@ -275,7 +274,6 @@ table.tabs {
.tab .icon {
display: block;
}
-
a:active
{
color: #989973;
@@ -411,13 +409,44 @@ pre.data
.arg_tr_pc {
}
-.ac_field {
-border:1px solid #D9D95F;
+#fkbg {
+ display:none;
+ position:fixed;
+ top:0;left:0;
+ width:100%;
+ height:100%;
+ z-index:10;
+}
+#fklist {
+ display:none;
+ position:absolute;
+ background:#fff;
+ border:1px solid #000;
+ overflow:auto;
+ z-index:15;
+}
+#fklist table {
+ border-collapse:collapse;
+ border: 1px solid #aaa;
+}
+#fklist th {border: 1px solid #aaa}
+#fklist td,
+#fklist th {
+ padding: 3px 10px;
+ border-right: 1px solid #aaa;
+ font-size: 12px;
+}
+#fklist td a {
+ display:block;
+ color:#000;
+}
+#fklist td a.fkval {
+ color:red;
}
-
.normal_field {
}
-
.pre {
-white-space:pre;
-} \ No newline at end of file
+ white-space:pre;
+}
+.ac_values {width:100%}
+.ac_field {border:1px solid #D9D95F} \ No newline at end of file