You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1161 lines
						
					
					
						
							33 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							1161 lines
						
					
					
						
							33 KiB
						
					
					
				
								/*
							 | 
						|
								 * Copyright (c)2012 Nicolas Cannasse
							 | 
						|
								 *
							 | 
						|
								 * Permission is hereby granted, free of charge, to any person obtaining
							 | 
						|
								 * a copy of this software and associated documentation files (the
							 | 
						|
								 * "Software"), to deal in the Software without restriction, including
							 | 
						|
								 * without limitation the rights to use, copy, modify, merge, publish,
							 | 
						|
								 * distribute, sublicense, and/or sell copies of the Software, and to
							 | 
						|
								 * permit persons to whom the Software is furnished to do so, subject to
							 | 
						|
								 * the following conditions:
							 | 
						|
								 *
							 | 
						|
								 * The above copyright notice and this permission notice shall be
							 | 
						|
								 * included in all copies or substantial portions of the Software.
							 | 
						|
								 *
							 | 
						|
								 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
							 | 
						|
								 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
							 | 
						|
								 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
							 | 
						|
								 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
							 | 
						|
								 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
							 | 
						|
								 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
							 | 
						|
								 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
							 | 
						|
								 */
							 | 
						|
								package sys.db;
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								import sys.db.Object;
							 | 
						|
								import sys.db.Manager;
							 | 
						|
								import sys.db.Types;
							 | 
						|
								
							 | 
						|
								import sys.db.Custom;
							 | 
						|
								import sys.db.TableInfos.TableType;
							 | 
						|
								import sys.db.TableInfos.ManagerAccess;
							 | 
						|
								#if neko
							 | 
						|
								import neko.Lib;
							 | 
						|
								import neko.Web;
							 | 
						|
								#elseif php 
							 | 
						|
								import php.Lib;
							 | 
						|
								import php.Web;
							 | 
						|
								#end
							 | 
						|
								
							 | 
						|
								@:access(sys.db.Manager)
							 | 
						|
								class Admin {
							 | 
						|
								
							 | 
						|
									var style : AdminStyle;
							 | 
						|
									var hasSyncAction : Bool;
							 | 
						|
									var countCache : Map<String,Bool>;
							 | 
						|
									public var allowDrop : Bool;
							 | 
						|
									public var default_rights : RightsInfos;
							 | 
						|
									public var maxUploadSize : Int;
							 | 
						|
									public var maxInstanceCount : Int;
							 | 
						|
								
							 | 
						|
									public function new() {
							 | 
						|
										maxInstanceCount = 100;
							 | 
						|
										maxUploadSize = 1000000;
							 | 
						|
										allowDrop = false;
							 | 
						|
										countCache = new Map();
							 | 
						|
										default_rights = {
							 | 
						|
											can : {
							 | 
						|
												insert : true,
							 | 
						|
												delete : true,
							 | 
						|
												modify : true,
							 | 
						|
												truncate : false,
							 | 
						|
											},
							 | 
						|
											invisible : [],
							 | 
						|
											readOnly : [],
							 | 
						|
										};
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function execute(sql) {
							 | 
						|
										return Manager.cnx.request(sql);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function request( t : TableInfos, sql ) {
							 | 
						|
										return Manager.cnx.request(sql);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function boolResult(sql) {
							 | 
						|
										try {
							 | 
						|
											execute(sql);
							 | 
						|
											return true;
							 | 
						|
										} catch( e : Dynamic ) {
							 | 
						|
											return false;
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function getTables():Array<sys.db.TableInfos> {
							 | 
						|
										var tables = new Array();
							 | 
						|
										var classes = Lib.getClasses();
							 | 
						|
										crawl(tables, classes);
							 | 
						|
										tables.sort(function(t1,t2) { return if( t1.name > t2.name ) 1 else if( t1.name < t2.name ) -1 else 0; });
							 | 
						|
										return tables;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function has<T>( a : { function iterator() : Iterator<T>; }, v : T ) {
							 | 
						|
										for( x in a.iterator() )
							 | 
						|
											if( x == v )
							 | 
						|
												return true;
							 | 
						|
										return false;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/**
							 | 
						|
									 * Filter classes who sublass sys.db.Object and get TableInfos
							 | 
						|
									 */
							 | 
						|
									function crawl(tables : Array<TableInfos>,classes : Dynamic) {
							 | 
						|
										for( cname in Reflect.fields(classes) ) {
							 | 
						|
								
							 | 
						|
											var v : Dynamic = Reflect.field(classes,cname);
							 | 
						|
											var c = cname.charAt(0);
							 | 
						|
											if( c >= "a" && c <= "z" ) {
							 | 
						|
												//explore sub packages
							 | 
						|
												crawl(tables,v);
							 | 
						|
												continue;
							 | 
						|
											}
							 | 
						|
											#if !php
							 | 
						|
											if( haxe.rtti.Meta.getType(v).rtti == null )
							 | 
						|
												continue;
							 | 
						|
											#end
							 | 
						|
											var s = Type.getSuperClass(v);
							 | 
						|
											while( s != null ) {
							 | 
						|
												if( s == sys.db.Object ) {
							 | 
						|
													tables.push(new TableInfos(Type.getClassName(v)));
							 | 
						|
													break;
							 | 
						|
												}
							 | 
						|
												s = Type.getSuperClass(s);
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									public function index( ?errorMsg ) {
							 | 
						|
										style.begin("Tables");
							 | 
						|
										style.beginForm("doSync");
							 | 
						|
										style.beginTable();
							 | 
						|
										var sync = false;
							 | 
						|
										var allTables = new List();
							 | 
						|
										var rq = execute(TableInfos.allTablesRequest());
							 | 
						|
										for( r in rq ){
							 | 
						|
											if (rq.getResult(0) == null) continue;
							 | 
						|
											allTables.add(rq.getResult(0));
							 | 
						|
										}
							 | 
						|
										var windows = Sys.systemName() == "Windows";
							 | 
						|
										for( t in getTables() ) {
							 | 
						|
											var rights = getRights(createInstance(t));
							 | 
						|
											style.beginLine(true);
							 | 
						|
											style.text(t.name);
							 | 
						|
											style.nextRow();
							 | 
						|
											if( !boolResult(t.existsRequest()) ) {
							 | 
						|
												style.linkConfirm(t.className+"/doCreate","create");
							 | 
						|
												style.text("Table is Missing !");
							 | 
						|
											} else {
							 | 
						|
												if( needSync(t) )
							 | 
						|
													sync = true;
							 | 
						|
												if( rights.can.insert )
							 | 
						|
													style.link(t.className+"/insert","insert");
							 | 
						|
												style.nextRow();
							 | 
						|
												style.link(t.className+"/search","search");
							 | 
						|
												if( rights.can.truncate )
							 | 
						|
													style.linkConfirm(t.className+"/doCleanup","cleanup");
							 | 
						|
												if( allowDrop ) {
							 | 
						|
													style.nextRow();
							 | 
						|
													style.linkConfirm(t.className+"/doDrop","drop");
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
											style.endLine();
							 | 
						|
											allTables.remove(t.name);
							 | 
						|
											if( windows || TableInfos.OLD_COMPAT ) allTables.remove(t.name.toLowerCase());
							 | 
						|
										}
							 | 
						|
										style.endTable();
							 | 
						|
										if( sync )
							 | 
						|
											style.addSubmit("Synchronize Database",true);
							 | 
						|
										style.endForm();
							 | 
						|
										if( !allTables.isEmpty() ) {
							 | 
						|
											style.beginList();
							 | 
						|
											for( t in allTables ) {
							 | 
						|
												style.beginItem();
							 | 
						|
												style.text('Table "$t" does not have any matching object');
							 | 
						|
												style.endItem();
							 | 
						|
											}
							 | 
						|
											style.endList();
							 | 
						|
										}
							 | 
						|
										if( errorMsg != null )
							 | 
						|
											style.error(errorMsg);
							 | 
						|
										style.end();
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function isBinary(t) {
							 | 
						|
										return switch( t ) {
							 | 
						|
										case DBinary, DSmallBinary, DLongBinary, DBytes(_): true;
							 | 
						|
										default: false;
							 | 
						|
										};
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function canDisplay( m : ManagerAccess ) {
							 | 
						|
										var c = countCache.get(m.table_name);
							 | 
						|
										if( c != null )
							 | 
						|
											return c;
							 | 
						|
										c = execute(TableInfos.countRequest(m,maxInstanceCount)).length < maxInstanceCount;
							 | 
						|
										countCache.set(m.table_name,c);
							 | 
						|
										return c;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function inputField( table : TableInfos, f, id : String, readonly, ?defval : Dynamic, ?rawValue : Bool ) {
							 | 
						|
										var prim = has(table.primary,f.name);
							 | 
						|
										var insert = (id == null);
							 | 
						|
										for( r in table.relations )
							 | 
						|
											if( r.key == f.name ) {
							 | 
						|
												var values = null;
							 | 
						|
												if( canDisplay(r.manager) || r.className == "db.File" )
							 | 
						|
													values = r.manager.all(false).map(function(d) {
							 | 
						|
														return {
							 | 
						|
															id : Std.string(Reflect.field(d,r.manager.table_keys[0])),
							 | 
						|
															str : d.toString()
							 | 
						|
														};
							 | 
						|
													});
							 | 
						|
												var cname = r.className.substr(0,3) == "db." ? r.className.substr(3) : r.className;
							 | 
						|
												style.choiceField(
							 | 
						|
													r.prop,
							 | 
						|
													values,
							 | 
						|
													Std.string(defval),
							 | 
						|
													cname+"/edit/",
							 | 
						|
													!insert && (prim || readonly),
							 | 
						|
													r.className == "db.File"
							 | 
						|
												);
							 | 
						|
												return;
							 | 
						|
											}
							 | 
						|
										if( defval != null && !rawValue ) {
							 | 
						|
											switch( f.type ) {
							 | 
						|
											case DEncoded:
							 | 
						|
												defval = Std.parseInt(Std.string(defval));
							 | 
						|
												defval = Id.decode(defval);
							 | 
						|
											case DSerialized:
							 | 
						|
												defval = new Serialized(defval).escape();
							 | 
						|
												
							 | 
						|
											case DNekoSerialized:
							 | 
						|
												#if neko
							 | 
						|
												var v = try haxe.Serializer.run(Lib.localUnserialize(defval)) catch( e : Dynamic ) ("ERROR : " + Std.string(e));
							 | 
						|
												defval = new Serialized(v).escape();
							 | 
						|
												#else
							 | 
						|
												throw "DNekoSerialized is only available on neko target";
							 | 
						|
												#end
							 | 
						|
											case DData:
							 | 
						|
												#if haxe3
							 | 
						|
													var str = try haxe.Serializer.run((untyped table.manager).doUnserialize(f.name, defval)) catch( e : Dynamic ) ("ERROR : " + Std.string(e));
							 | 
						|
												#else
							 | 
						|
													var str = defval.toString();
							 | 
						|
												#end
							 | 
						|
												defval = new Serialized(str).escape();
							 | 
						|
											default:
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										if( isBinary(f.type) )
							 | 
						|
											style.binField(f.name,table.nulls.exists(f.name),defval,if( insert ) null else function() { return table.name+"/doDownload/"+id+"/"+f.name; });
							 | 
						|
										else if( insert && readonly )
							 | 
						|
											return;
							 | 
						|
										else if( !insert && (prim || readonly) )
							 | 
						|
											style.infoField(f.name,defval);
							 | 
						|
										else
							 | 
						|
											style.inputField(f.name,f.type,table.nulls.exists(f.name),defval);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/**
							 | 
						|
									 * Prints an insert form
							 | 
						|
									 */
							 | 
						|
									function insert(table : TableInfos, ?params : Map<String,String>, ?error : String, ?errorMsg : String ) {
							 | 
						|
										var binary = false;
							 | 
						|
										for( f in table.fields )
							 | 
						|
											if( isBinary(f.type) ) {
							 | 
						|
												binary = true;
							 | 
						|
												break;
							 | 
						|
											}
							 | 
						|
										style.begin("Insert new "+table.name);
							 | 
						|
										style.beginForm(table.className+"/doInsert",binary,table.name);
							 | 
						|
										var rights = getRights(table);
							 | 
						|
										for( f in table.fields ) {
							 | 
						|
											if( f.name == error ) {
							 | 
						|
												style.errorField((errorMsg == null) ? "Invalid format" : errorMsg);
							 | 
						|
												errorMsg = null;
							 | 
						|
											}
							 | 
						|
											if( has(rights.invisible,f.name) )
							 | 
						|
												continue;
							 | 
						|
											var readonly = has(rights.readOnly,f.name);
							 | 
						|
											inputField(table,f,null,readonly,if( params == null ) null else params.get(f.name), params != null );
							 | 
						|
										}
							 | 
						|
										style.addSubmit("Insert");
							 | 
						|
										style.addSubmit("Insert New",null,false,"__new");
							 | 
						|
										style.endForm();
							 | 
						|
										if( errorMsg != null )
							 | 
						|
											style.error(errorMsg);
							 | 
						|
										style.end();
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function updateField( fname : String, v : String, ftype : TableType, table : TableInfos ) : Dynamic {
							 | 
						|
										switch( ftype ) {
							 | 
						|
										case DId, DUId, DBigId:
							 | 
						|
											return null;
							 | 
						|
										case DDate:
							 | 
						|
											var d = if( v == "NOW" || v == "NOW()" ) Date.now() else try Date.fromString(v) catch( e : Dynamic ) null;
							 | 
						|
											if( d == null )
							 | 
						|
												return null;
							 | 
						|
											try d.toString() catch( e : Dynamic ) return null;
							 | 
						|
											return d;
							 | 
						|
										case DDateTime, DTimeStamp:
							 | 
						|
											var d = if( v == "NOW" || v == "NOW()" ) Date.now() else try Date.fromString(v) catch( e : Dynamic ) null;
							 | 
						|
											if( d == null )
							 | 
						|
												return null;
							 | 
						|
											try d.toString() catch( e : Dynamic ) return null;
							 | 
						|
											return d;
							 | 
						|
										case DInt:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											return Std.parseInt(v);
							 | 
						|
										case DUInt, DFlags(_):
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseInt(v);
							 | 
						|
											if( i < 0 )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DTinyInt:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseInt(v);
							 | 
						|
											if( i < -128 || i > 127 )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DTinyUInt:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseInt(v);
							 | 
						|
											if( i < 0 || i > 255 )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DSmallInt:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseInt(v);
							 | 
						|
											if( i < -32768 || i > 32767 )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DSmallUInt:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseInt(v);
							 | 
						|
											if( i < 0 || i > 65535 )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DMediumInt:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseInt(v);
							 | 
						|
											if( i < -8388608 || i > 8388607 )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DMediumUInt:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseInt(v);
							 | 
						|
											if( i < 0 || i > 16777215 )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DBigInt:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseFloat(v);
							 | 
						|
											if( i == null || i%1 != 0 || i < -9223372036854775808.0 || i > 9223372036854775807.0 )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DFloat, DSingle:
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var fl = Std.parseFloat(v);
							 | 
						|
											if( Math.isNaN(fl) )
							 | 
						|
												return null;
							 | 
						|
											return fl;
							 | 
						|
										case DString(n):
							 | 
						|
											if( v.length > n )
							 | 
						|
												return null;
							 | 
						|
											return v;
							 | 
						|
										case DTinyText:
							 | 
						|
											if( v.length > 255 )
							 | 
						|
												return null;
							 | 
						|
											return v;
							 | 
						|
										case DSmallText, DSmallBinary:
							 | 
						|
											if( v.length > 0xFFFF )
							 | 
						|
												return null;
							 | 
						|
											return v;
							 | 
						|
										case DText, DBinary:
							 | 
						|
											if( v.length > 0xFFFFFF )
							 | 
						|
												return null;
							 | 
						|
											return v;
							 | 
						|
										case DBytes(n):
							 | 
						|
											if( v.length > n )
							 | 
						|
												return null;
							 | 
						|
											return v;
							 | 
						|
										case DLongBinary:
							 | 
						|
											return v;
							 | 
						|
										case DBool:
							 | 
						|
											return (v == "true");
							 | 
						|
										case DEncoded:
							 | 
						|
											if( v == "" ) return null;
							 | 
						|
											return try Id.encode(v) catch( e : Dynamic ) null;
							 | 
						|
										case DSerialized:
							 | 
						|
											return new Serialized(v).encode();
							 | 
						|
										case DNekoSerialized:
							 | 
						|
											#if neko
							 | 
						|
											var str = new Serialized(v).encode();
							 | 
						|
											var val = neko.Lib.serialize(haxe.Unserializer.run(str));
							 | 
						|
											return val;
							 | 
						|
											#else
							 | 
						|
											throw "DNekoSerialized is only available on neko target";
							 | 
						|
											return null;
							 | 
						|
											#end
							 | 
						|
										case DData:
							 | 
						|
											var s = new Serialized(v).encode();	
							 | 
						|
											if( s.length > 0xFFFFFF )
							 | 
						|
												return null;
							 | 
						|
											#if haxe3
							 | 
						|
												return (untyped table.manager).doSerialize(fname, haxe.Unserializer.run(s)) ;
							 | 
						|
											#else
							 | 
						|
												return haxe.io.Bytes.ofString(s);
							 | 
						|
											#end
							 | 
						|
										case DEnum(e):
							 | 
						|
											if( v == "" ) return 0;
							 | 
						|
											var i = Std.parseInt(v);
							 | 
						|
											var ev = Type.resolveEnum(e);
							 | 
						|
											if( i < 0 || (ev != null && i >= Type.getEnumConstructs(ev).length) )
							 | 
						|
												return null;
							 | 
						|
											return i;
							 | 
						|
										case DNull, DInterval:
							 | 
						|
											throw "assert";
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function createInstance( table : TableInfos ) : Object {		
							 | 
						|
										var c = Type.createEmptyInstance(table.cl);
							 | 
						|
										#if !neko
							 | 
						|
										untyped if( c._manager == null )  c._manager = c.__getManager();
							 | 
						|
										#end
							 | 
						|
										return c;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function getRights( ?t : Object, ?table : TableInfos ) : RightsInfos {
							 | 
						|
										if( t == null )
							 | 
						|
											t = createInstance(table);
							 | 
						|
										if( untyped t.dbRights == null )
							 | 
						|
											return default_rights;
							 | 
						|
										var r : RightsInfos = untyped t.dbRights();
							 | 
						|
										if( r == null )
							 | 
						|
											return default_rights;
							 | 
						|
										if( r.can == null )
							 | 
						|
											r.can = default_rights.can;
							 | 
						|
										return r;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function getSInfos( t : Object ) : SearchInfos {
							 | 
						|
										if( untyped t.dbSearch == null )
							 | 
						|
											return null;
							 | 
						|
										return untyped t.dbSearch();
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/**
							 | 
						|
									 * insert a new object in a table
							 | 
						|
									 */
							 | 
						|
									function doInsert( table : TableInfos, params : Map<String,String> ) {
							 | 
						|
										var inst = createInstance(table);
							 | 
						|
										updateParams(table,params);
							 | 
						|
										for( f in table.fields ) {
							 | 
						|
											var v = params.get(f.name);
							 | 
						|
											#if (haxe_ver < 3.2)
							 | 
						|
											var fieldName = f.name;
							 | 
						|
											#else
							 | 
						|
											var fieldName = Manager.getFieldName({name: f.name, t: f.type, isNull: table.nulls.exists(f.name)});
							 | 
						|
											#end
							 | 
						|
											if( v == null ) {
							 | 
						|
												if( table.nulls.exists(f.name) )
							 | 
						|
													Reflect.setField(inst,fieldName,null);
							 | 
						|
												else
							 | 
						|
													for( r in table.relations )
							 | 
						|
														if( f.name == r.key ) {
							 | 
						|
															insert(table,params,f.name);
							 | 
						|
															return;
							 | 
						|
														}
							 | 
						|
												continue;
							 | 
						|
											}
							 | 
						|
											var msg = null;
							 | 
						|
											var v = try updateField(f.name, v, f.type, table) catch( err : String ) { msg = err; null; };
							 | 
						|
											if( v == null ) {
							 | 
						|
												// loop in case of error Invalid_format
							 | 
						|
												insert(table,params,f.name,msg);
							 | 
						|
												return;
							 | 
						|
											}
							 | 
						|
											Reflect.setField(inst,fieldName,v);
							 | 
						|
										}
							 | 
						|
										if( table.primary.length == 1 && Reflect.field(inst,table.primary.first()) == 0 )
							 | 
						|
											Reflect.deleteField(inst,table.primary.first());
							 | 
						|
										try {
							 | 
						|
											if( !getRights(inst).can.insert )
							 | 
						|
												throw "Can't insert";
							 | 
						|
											if(inst == null)
							 | 
						|
												throw "instance is null";
							 | 
						|
											inst.insert();
							 | 
						|
											log("Inserted "+table.name+" "+table.identifier(inst));
							 | 
						|
										} catch( e : Dynamic ) {
							 | 
						|
											insert(table,params,null,Std.string(e));
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										
							 | 
						|
										//display a blank form to insert a new object
							 | 
						|
										if( params.exists("__new") ) {
							 | 
						|
											insert(table,params);
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										style.goto(table.className+"/edit/"+table.identifier(inst));
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function doCreate(table : TableInfos) {
							 | 
						|
										try {
							 | 
						|
											execute(table.createRequest(false));
							 | 
						|
											style.goto("");
							 | 
						|
										} catch( e : Dynamic ) {
							 | 
						|
											index(Std.string(e));
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function doDrop(table : TableInfos) {
							 | 
						|
										if( !allowDrop )
							 | 
						|
											throw "Drop not allowed";
							 | 
						|
										execute(table.dropRequest());
							 | 
						|
										style.goto("");
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function doCleanup( table : TableInfos ) {
							 | 
						|
										if( !getRights(table).can.truncate )
							 | 
						|
											throw "Can't cleanup";
							 | 
						|
										execute(table.truncateRequest());
							 | 
						|
										style.goto("");
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function edit( table :  TableInfos, id : String, ?params : Map<String,String>, ?error : String, ?errorMsg : String ) {
							 | 
						|
										var obj = table.fromIdentifier(id);
							 | 
						|
										var objStr = try Std.string(obj) catch( e : Dynamic ) "#"+id;
							 | 
						|
										style.begin("Edit "+table.name+" "+objStr);
							 | 
						|
										if( obj == null ) {
							 | 
						|
											style.error("This object does not exists");
							 | 
						|
											style.end();
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										var binary = false;
							 | 
						|
										for( f in table.fields )
							 | 
						|
											if( isBinary(f.type) ) {
							 | 
						|
												binary = true;
							 | 
						|
												break;
							 | 
						|
											}
							 | 
						|
										style.beginForm(table.className+"/doEdit/"+id,binary,table.name);
							 | 
						|
										var rights = getRights(obj);
							 | 
						|
										var hasBinary = false;
							 | 
						|
										for( f in table.fields ) {
							 | 
						|
											if( f.name == error ) {
							 | 
						|
												style.errorField((errorMsg == null) ? "Invalid format" : errorMsg);
							 | 
						|
												errorMsg = null;
							 | 
						|
											}
							 | 
						|
											if( has(rights.invisible,f.name) )
							 | 
						|
												continue;
							 | 
						|
											var readonly = has(rights.readOnly,f.name);
							 | 
						|
											inputField(table,f,id,readonly,if( params == null ) Reflect.field(obj,f.name) else params.get(f.name), params != null);
							 | 
						|
											if( !readonly && isBinary(f.type) )
							 | 
						|
												hasBinary = true;
							 | 
						|
										}
							 | 
						|
										if( rights.can.modify ) {
							 | 
						|
											style.addSubmit("Modify");
							 | 
						|
											if( hasBinary )
							 | 
						|
												style.addSubmit("Upload",null,null,"__upload");
							 | 
						|
										}
							 | 
						|
										style.addSubmit("Cancel",table.className+"/edit/"+id);
							 | 
						|
										if( rights.can.delete )
							 | 
						|
											style.addSubmit("Delete",table.className+"/doDelete/"+id,true);
							 | 
						|
										style.endForm();
							 | 
						|
										if( errorMsg != null )
							 | 
						|
											style.error(errorMsg);
							 | 
						|
										style.end();
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function doEdit( table : TableInfos, id : String, params : Map<String,String> ) {
							 | 
						|
										var inst = table.fromIdentifier(id);
							 | 
						|
										if( inst == null ) {
							 | 
						|
											style.goto(table.className+"/edit/"+id);
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										updateParams(table,params);
							 | 
						|
										var rights = getRights(inst);
							 | 
						|
										var binaries = new List();
							 | 
						|
										for( f in table.fields ) {
							 | 
						|
											if( has(rights.readOnly,f.name) || has(rights.invisible,f.name) )
							 | 
						|
												continue;
							 | 
						|
											#if (haxe_ver < 3.2)
							 | 
						|
											var fieldName = f.name;
							 | 
						|
											#else
							 | 
						|
											var fieldName = Manager.getFieldName({name: f.name, t: f.type, isNull: table.nulls.exists(f.name)});
							 | 
						|
											#end
							 | 
						|
											var v = params.get(f.name);
							 | 
						|
											if( v == null ) {
							 | 
						|
												if( table.nulls.exists(f.name) )
							 | 
						|
													Reflect.setField(inst,fieldName,null);
							 | 
						|
												continue;
							 | 
						|
											}
							 | 
						|
											var msg = null;
							 | 
						|
											var v = try updateField(f.name, v, f.type, table) catch( err : Dynamic ) { msg = err; null; };
							 | 
						|
											if( v == null ) {
							 | 
						|
												// insert ID into params
							 | 
						|
												if( table.primary != null ) {
							 | 
						|
													for( p in table.primary )
							 | 
						|
														params.set(p,Reflect.field(inst,p));
							 | 
						|
												}
							 | 
						|
												for( f in rights.readOnly )
							 | 
						|
													params.set(f, Reflect.field(inst, f));
							 | 
						|
												// error Invalid_format
							 | 
						|
												edit(table,id,params,f.name, msg);
							 | 
						|
												return;
							 | 
						|
											}
							 | 
						|
											var bin = isBinary(f.type);
							 | 
						|
											if( Std.is(v,String) && v == "" && bin )
							 | 
						|
												continue;
							 | 
						|
											Reflect.setField(inst,fieldName,v);
							 | 
						|
											if( bin )
							 | 
						|
												binaries.add({ name : f.name, value : v });
							 | 
						|
										}
							 | 
						|
										try {
							 | 
						|
											if( !rights.can.modify )
							 | 
						|
												throw "Can't modify";
							 | 
						|
											if( params.exists("__upload") )
							 | 
						|
												request(table,table.updateFields(inst,binaries));
							 | 
						|
											else {
							 | 
						|
												inst.update();
							 | 
						|
												log("Updated "+table.name+" "+table.identifier(inst));
							 | 
						|
											}
							 | 
						|
										} catch( e : Dynamic ) {
							 | 
						|
											edit(table,id,params,null,Std.string(e));
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										style.goto(table.className+"/edit/"+table.identifier(inst));
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/**
							 | 
						|
									 * Sync some of the object fields from the web request
							 | 
						|
									 */
							 | 
						|
									function updateParams( table : TableInfos, params : Map<String,String> ) {
							 | 
						|
										var tmp = Web.getMultipart(maxUploadSize);
							 | 
						|
										for( k in tmp.keys() )
							 | 
						|
											params.set(k,tmp.get(k));
							 | 
						|
										for( r in table.relations ) {
							 | 
						|
											var p = params.get(r.prop);
							 | 
						|
											params.remove(r.prop);
							 | 
						|
											if( p == null || p == "" )
							 | 
						|
												continue;
							 | 
						|
											params.set(r.key,p);
							 | 
						|
											params.remove(r.prop+"__data");
							 | 
						|
											params.set(r.key+"__data","on");
							 | 
						|
										}
							 | 
						|
										for( f in table.fields ) {
							 | 
						|
											switch( f.type ) {
							 | 
						|
											case DFlags(flags,_):
							 | 
						|
												var vint = 0;
							 | 
						|
												for( i in 0...flags.length )
							 | 
						|
													if( params.exists(f.name + "_" + flags[i]) )
							 | 
						|
														vint |= 1 << i;
							 | 
						|
												if( table.nulls.exists(f.name) && !params.exists(f.name+"__data") && vint == 0 ) {
							 | 
						|
													params.remove(f.name);
							 | 
						|
													continue;
							 | 
						|
												}
							 | 
						|
												params.set(f.name, Std.string(vint));
							 | 
						|
												params.set(f.name + "__data", "true");
							 | 
						|
											default:
							 | 
						|
											}
							 | 
						|
											if( table.nulls.exists(f.name) && !params.exists(f.name+"__data") && (params.get(f.name) == "" || params.get(f.name) == null) ) {
							 | 
						|
												params.remove(f.name);
							 | 
						|
												continue;
							 | 
						|
											}
							 | 
						|
											if( f.type == DBool ) {
							 | 
						|
												var v = params.exists(f.name);
							 | 
						|
												params.set(f.name,if( v ) "true" else "false");
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/**
							 | 
						|
									 * Deletes a record
							 | 
						|
									 */
							 | 
						|
									function doDelete( table : TableInfos, id : String ) {
							 | 
						|
										var inst = table.fromIdentifier(id);
							 | 
						|
										if( inst == null ) {
							 | 
						|
											style.goto(table.className+"/edit/"+id);
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										if( !getRights(inst).can.delete ) {
							 | 
						|
											edit(table,id,null,null,"Can't Delete");
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										inst.delete();
							 | 
						|
										log("Deleted "+table.name+" "+id);
							 | 
						|
										style.goto("");
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function doDownload( table : TableInfos, id : String, field : String ) {
							 | 
						|
										var inst = table.fromIdentifier(id);
							 | 
						|
										if( inst == null ) {
							 | 
						|
											style.goto(table.className+"/edit/"+id);
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										var rights = getRights(inst);
							 | 
						|
										var f = table.hfields.get(field);
							 | 
						|
										var data : String = Reflect.field(inst,field);
							 | 
						|
										if( has(rights.invisible,field) || data == null || !isBinary(f) ) {
							 | 
						|
											edit(table,id,null,null,"Can't Download data");
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										Web.setHeader("Content-Type","text/binary");
							 | 
						|
										Web.setHeader("Content-Length",Std.string(data.length));
							 | 
						|
										Sys.print(data);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function search( table : TableInfos, params : Map<String,String> ) {
							 | 
						|
										style.begin("Search "+table.name);
							 | 
						|
								
							 | 
						|
										var pagesize = 30;
							 | 
						|
										var page = Std.parseInt(params.get("__p"));
							 | 
						|
										var order = params.get("__o");
							 | 
						|
										if( page == null )
							 | 
						|
											page = 0;
							 | 
						|
										params.remove("__p");
							 | 
						|
										params.remove("__o");
							 | 
						|
								
							 | 
						|
										// save params for later usage
							 | 
						|
										var paramsStr = "";
							 | 
						|
										for( p in params.keys() ) {
							 | 
						|
											var v = params.get(p);
							 | 
						|
											paramsStr += p+"="+StringTools.urlEncode(v)+";";
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// set nullable for all types which can be searched with empty string or no values
							 | 
						|
										for( f in table.fields )
							 | 
						|
											switch( f.type ) {
							 | 
						|
											case DBool, DString(_), DTinyText, DText, DSmallText, DFlags(_):
							 | 
						|
												table.nulls.set(f.name,true);
							 | 
						|
											default:
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
										updateParams(table,params);
							 | 
						|
								
							 | 
						|
										// remove not null fields that have not been completed
							 | 
						|
										for( f in table.fields )
							 | 
						|
											if( !table.nulls.exists(f.name) && params.get(f.name) == "" )
							 | 
						|
												params.remove(f.name);
							 | 
						|
								
							 | 
						|
										var rights = getRights(table);
							 | 
						|
										for( f in rights.invisible )
							 | 
						|
											params.remove(f);
							 | 
						|
								
							 | 
						|
										var results = table.fromSearch(params,order,page*pagesize,pagesize+1);
							 | 
						|
										var hasNext = false;
							 | 
						|
										if( results.length > pagesize ) {
							 | 
						|
											results.remove(results.last());
							 | 
						|
											hasNext = true;
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										var sinfos : SearchInfos = getSInfos(createInstance(table));
							 | 
						|
										var fields;
							 | 
						|
										if( sinfos != null && sinfos.fields != null )
							 | 
						|
											fields = sinfos.fields;
							 | 
						|
										else {
							 | 
						|
											fields = new Array();
							 | 
						|
											for( f in table.fields ) {
							 | 
						|
												var bad = false;
							 | 
						|
												for( r in table.relations )
							 | 
						|
													if( r.key == f.name && r.className == "db.File" ) {
							 | 
						|
														bad = true;
							 | 
						|
														break;
							 | 
						|
													}
							 | 
						|
												if( bad )
							 | 
						|
													continue;
							 | 
						|
												fields.push(f.name);
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										style.beginForm(table.className+"/search",table.name);
							 | 
						|
										for( f in fields ) {
							 | 
						|
											var t = table.hfields.get(f);
							 | 
						|
											if( t == null )
							 | 
						|
												continue;
							 | 
						|
											var t = switch( t ) {
							 | 
						|
												case DId, DUId: DInt;
							 | 
						|
												case DBigId: DFloat;
							 | 
						|
												case DText,DSmallText: DTinyText;
							 | 
						|
												default: t;
							 | 
						|
											};
							 | 
						|
											inputField(table,{ name : f, type : t },null,false,if( params == null ) null else params.get(f));
							 | 
						|
										}
							 | 
						|
										style.addSubmit("Search");
							 | 
						|
										style.endForm();
							 | 
						|
								
							 | 
						|
										style.beginTable("results");
							 | 
						|
										style.beginLine(true,"header");
							 | 
						|
										style.text("actions");
							 | 
						|
								
							 | 
						|
										if( sinfos != null && sinfos.names != null ) {
							 | 
						|
											for( f in sinfos.names ) {
							 | 
						|
												style.nextRow(true);
							 | 
						|
												if( table.hfields.exists(f) ) {
							 | 
						|
													var cur = (order == f);
							 | 
						|
													var curNeg = (order == "-"+f);
							 | 
						|
													style.link(table.className+"/search?"+paramsStr+"__o="+(cur ? "-"+f : f),(cur ? "+" : curNeg ? "-" : "") + f);
							 | 
						|
												} else
							 | 
						|
													style.text(f);
							 | 
						|
											}
							 | 
						|
										} else {
							 | 
						|
											for( f in table.fields ) {
							 | 
						|
												if( has(rights.invisible,f.name) )
							 | 
						|
													continue;
							 | 
						|
												style.nextRow(true);
							 | 
						|
												var cur = (order == f.name);
							 | 
						|
												var curNeg = (order == "-"+f.name);
							 | 
						|
												style.link(table.className+"/search?"+paramsStr+"__o="+(cur ? "-"+f.name : f.name),(cur ? "+" : curNeg ? "-" : "") + f.name);
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										style.endLine();
							 | 
						|
								
							 | 
						|
										var odd = false;
							 | 
						|
										for( r in results ) {
							 | 
						|
											var k = table.fields.iterator();
							 | 
						|
											style.beginLine(if( odd ) "odd" else null);
							 | 
						|
											style.link(table.className+"/edit/"+table.identifier(r),"Edit");
							 | 
						|
											odd = !odd;
							 | 
						|
											if( sinfos != null && sinfos.names != null ) {
							 | 
						|
												var rinfos = getSInfos(r);
							 | 
						|
												for( v in rinfos.values ) {
							 | 
						|
													style.nextRow(false);
							 | 
						|
													style.text(Std.string(v));
							 | 
						|
												}
							 | 
						|
											} else {
							 | 
						|
												var rinst = getRights(r);
							 | 
						|
												for( f in k ) {
							 | 
						|
													if( has(rights.invisible,f.name) )
							 | 
						|
														continue;
							 | 
						|
													var data = Reflect.field(r,f.name);
							 | 
						|
													var str = try Std.string(data) catch( e : Dynamic ) { if(!Std.is(data,Date)) Lib.rethrow(e); "#INVALID"; };
							 | 
						|
													if( str.length >= 20 )
							 | 
						|
														str = str.substr(0,17) + "...";
							 | 
						|
													style.nextRow(false);
							 | 
						|
													if( has(rinst.invisible,f.name) )
							 | 
						|
														style.text("???");
							 | 
						|
													else if( data == null )
							 | 
						|
														style.text(str)
							 | 
						|
													else switch( f.type ) {
							 | 
						|
													case DEncoded:
							 | 
						|
														style.text(Id.decode(data),str);
							 | 
						|
													case DDate:
							 | 
						|
														style.text(str.substr(0,10)); // remove 00:00:00 time
							 | 
						|
													case DFlags(flags,_):
							 | 
						|
														var fl = [];
							 | 
						|
														for( i in 0...flags.length )
							 | 
						|
															if( data & (1 << i) != 0 )
							 | 
						|
																fl.push(flags[i]);
							 | 
						|
														str = fl.join(",");
							 | 
						|
														if( str.length >= 20 )
							 | 
						|
															style.text(str.substr(0,17) + "...",fl.join(",")+" ("+data+")");
							 | 
						|
														else
							 | 
						|
															style.text(str,"("+data+")");
							 | 
						|
													default:
							 | 
						|
														style.text(str);
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
											style.endLine();
							 | 
						|
										}
							 | 
						|
										style.endTable();
							 | 
						|
								
							 | 
						|
										if( order != null )
							 | 
						|
											paramsStr += "__o="+order+";";
							 | 
						|
								
							 | 
						|
										if( page > 0 )
							 | 
						|
											style.link(table.className+"/search?"+paramsStr+"__p="+(page-1),"Previous");
							 | 
						|
										else
							 | 
						|
											style.text("Previous");
							 | 
						|
										style.text(" | ");
							 | 
						|
										if( hasNext )
							 | 
						|
											style.link(table.className+"/search?"+paramsStr+"__p="+(page+1),"Next");
							 | 
						|
										else
							 | 
						|
											style.text("Next");
							 | 
						|
										style.end();
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function syncAction( t : TableInfos, act : Array<String>, text : String, ?def ) {
							 | 
						|
										if( !hasSyncAction ) {
							 | 
						|
											style.beginList();
							 | 
						|
											hasSyncAction = true;
							 | 
						|
										}
							 | 
						|
										style.beginItem();
							 | 
						|
										style.checkBox(t.className+"@"+act.join("@"),if( def == null ) true else def);
							 | 
						|
										style.text(text);
							 | 
						|
										style.endItem();
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function doSync( params : Map<String,String> ) {
							 | 
						|
										var order = ["create","add","reldel","idxdel","update","remove","rename","idxadd","reladd"];
							 | 
						|
										var cmd = new Array();
							 | 
						|
										for( p in params.keys() ) {
							 | 
						|
											if( !~/[A-Za-z0-9_@]*/.match(p) )
							 | 
						|
												throw "Invalid command "+p;
							 | 
						|
											cmd.push(p.split("@"));
							 | 
						|
										}
							 | 
						|
										cmd.sort(function(c1,c2) {
							 | 
						|
											var p1 = 0, p2 = 0;
							 | 
						|
											for( i in 0...order.length )
							 | 
						|
												if( order[i] == c1[1] )
							 | 
						|
													p1 = i;
							 | 
						|
												else if( order[i] == c2[1] )
							 | 
						|
													p2 = i;
							 | 
						|
											return p1 - p2;
							 | 
						|
										});
							 | 
						|
										for( data in cmd ) {
							 | 
						|
											var tname = data.shift();
							 | 
						|
											#if php
							 | 
						|
											// php replaces dots by _ in post keys
							 | 
						|
											tname = tname.split("_").join(".");
							 | 
						|
											#end
							 | 
						|
											var table = new TableInfos(tname);
							 | 
						|
											var act = data.shift();
							 | 
						|
											var field = data.shift();
							 | 
						|
											try {
							 | 
						|
												switch( act ) {
							 | 
						|
												case "create":
							 | 
						|
													execute(table.createRequest(false));
							 | 
						|
												case "add":
							 | 
						|
													execute(table.addFieldRequest(field));
							 | 
						|
												case "update":
							 | 
						|
													execute(table.updateFieldRequest(field));
							 | 
						|
												case "remove":
							 | 
						|
													execute(table.removeFieldRequest(field));
							 | 
						|
												case "rename":
							 | 
						|
													execute(table.renameFieldRequest(field,data.shift()));
							 | 
						|
												case "reladd":
							 | 
						|
													execute(table.addRelationRequest(field,data.shift()));
							 | 
						|
												case "reldel":
							 | 
						|
													execute(table.deleteRelationRequest(field));
							 | 
						|
												case "idxadd":
							 | 
						|
													execute(table.addIndexRequest(data,field == "true"));
							 | 
						|
												case "idxdel":
							 | 
						|
													execute(table.deleteIndexRequest(field));
							 | 
						|
												default:
							 | 
						|
													throw "Unknown action "+act;
							 | 
						|
												}
							 | 
						|
											} catch( e : Dynamic ) {
							 | 
						|
												index(Std.string(e));
							 | 
						|
												return;
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										style.goto("");
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									function indexId( i : { unique : Bool, keys : List<String> } ) {
							 | 
						|
										return i.unique+"@"+i.keys.join("@");
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/**
							 | 
						|
									 * check for differences between classes and tables in DB
							 | 
						|
									 */
							 | 
						|
									function needSync( t : TableInfos ) {
							 | 
						|
										var desc = execute(t.descriptionRequest()).getResult(1);
							 | 
						|
										var inf = TableInfos.fromDescription(desc);
							 | 
						|
										var renames = new Map();
							 | 
						|
										hasSyncAction = false;
							 | 
						|
										
							 | 
						|
										// ADD/CHANGE FIELDS
							 | 
						|
										for( f in t.fields ) {
							 | 
						|
											var t2 = inf.fields.get(f.name);
							 | 
						|
											if( t2 == null ) {
							 | 
						|
												var rename = false;
							 | 
						|
												for( n in inf.fields.keys() )
							 | 
						|
													if( !t.hfields.exists(n) && Type.enumEq(inf.fields.get(n),f.type) && inf.nulls.get(n) == t.nulls.get(f.name) ) {
							 | 
						|
														rename = true;
							 | 
						|
														renames.set(n,true);
							 | 
						|
														syncAction(t,["rename",n,f.name],'Rename field "$n" to "${f.name}"');
							 | 
						|
														break;
							 | 
						|
													}
							 | 
						|
												syncAction(t,["add",f.name],"Add field "+f.name,!rename);
							 | 
						|
											} else {
							 | 
						|
												inf.fields.remove(f.name);
							 | 
						|
												var isnull = inf.nulls.get(f.name);
							 | 
						|
												var changed = false;
							 | 
						|
												var txt = "Change "+f.name+" : ";
							 | 
						|
												if( !Type.enumEq(f.type,t2) && !TableInfos.sameDBStorage(t2,f.type) ) {
							 | 
						|
													changed = true;
							 | 
						|
													txt += " S"+Std.string(t2).substr(1)+" becomes S"+Std.string(f.type).substr(1);
							 | 
						|
												}
							 | 
						|
												if( isnull != t.nulls.get(f.name) ) {
							 | 
						|
													if( changed )
							 | 
						|
														txt += " and";
							 | 
						|
													else
							 | 
						|
														changed = true;
							 | 
						|
													if( isnull )
							 | 
						|
														txt += " can't be NULL";
							 | 
						|
													else
							 | 
						|
														txt += " can be NULL";
							 | 
						|
												}
							 | 
						|
												if( changed )
							 | 
						|
													syncAction(t,["update",f.name],txt);
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										// REMOVE FIELDS
							 | 
						|
										for( f in inf.fields.keys() )
							 | 
						|
											syncAction(t,["remove",f],"Remove field "+f,!renames.exists(f));
							 | 
						|
										// ADD RELATIONS
							 | 
						|
										for( r in t.relations ) {
							 | 
						|
											if( !t.isRelationActive(r) )
							 | 
						|
												continue;
							 | 
						|
											var tname = TableInfos.unescape(r.manager.table_name);
							 | 
						|
											var found = false;
							 | 
						|
											var setnull = t.nulls.get(r.key);
							 | 
						|
											if( setnull && untyped r.cascade == true )
							 | 
						|
												setnull = null;
							 | 
						|
											for( r2 in inf.relations )
							 | 
						|
												if( (t.name + "_" + r.prop == r2.name || TableInfos.OLD_COMPAT) &&
							 | 
						|
													r.key == r2.key &&
							 | 
						|
													tname.toLowerCase() == r2.table.toLowerCase() &&
							 | 
						|
													r.manager.table_keys.length == 1 && r.manager.table_keys[0] == r2.id &&
							 | 
						|
													r2.setnull == setnull
							 | 
						|
												) {
							 | 
						|
													found = true;
							 | 
						|
													inf.relations.remove(r2);
							 | 
						|
													break;
							 | 
						|
												}
							 | 
						|
											if( !found )
							 | 
						|
												syncAction(t,["reladd",r.key,r.prop],"Add Relation "+r.prop+"("+r.key+") on "+tname+"("+r.manager.table_keys[0]+")"+if( setnull ) " set-null" else "");
							 | 
						|
										}
							 | 
						|
										// REMOVE RELATIONS
							 | 
						|
										for( r in inf.relations )
							 | 
						|
											syncAction(t,["reldel",r.name],"Remove Relation "+r.name+"("+r.key+") on "+r.table+"("+r.id+")"+if( r.setnull ) " set-null" else "");
							 | 
						|
										// INDEXES
							 | 
						|
										var hidx = new Map();
							 | 
						|
										for( i in t.indexes )
							 | 
						|
											hidx.set(indexId(i),i);
							 | 
						|
										var used = new List();
							 | 
						|
										for( r in t.relations ) {
							 | 
						|
											var found : { keys : List<String>, unique : Bool } = null;
							 | 
						|
											for( i in t.indexes )
							 | 
						|
												if( i.keys.first() == r.key && (found == null || found.keys.length < i.keys.length) )
							 | 
						|
													found = i;
							 | 
						|
											if( found == null ) {
							 | 
						|
												// in primary key ?
							 | 
						|
												if( t.primary.first() == r.key )
							 | 
						|
													continue;
							 | 
						|
												// default relation-index
							 | 
						|
												found = { keys : Lambda.list([r.key]), unique : false };
							 | 
						|
											}
							 | 
						|
											hidx.remove(indexId(found));
							 | 
						|
											for( i in inf.indexes )
							 | 
						|
												if( i.keys.join("#") == found.keys.join("#") && i.unique == found.unique ) {
							 | 
						|
													used.add(i);
							 | 
						|
													found = null;
							 | 
						|
													break;
							 | 
						|
												}
							 | 
						|
											// we need it
							 | 
						|
											if( found != null )
							 | 
						|
												hidx.set(indexId(found),found);
							 | 
						|
										}
							 | 
						|
										for( i in used )
							 | 
						|
											inf.indexes.remove(i.name);
							 | 
						|
										for( iname in inf.indexes.keys() ) {
							 | 
						|
											var i = inf.indexes.get(iname);
							 | 
						|
											if( !hidx.remove(indexId(i)) )
							 | 
						|
												syncAction(t,["idxdel",iname],"Remove "+(if( i.unique ) "Unique " else "")+"Index "+iname+" ("+i.keys.join(",")+")");
							 | 
						|
										}
							 | 
						|
										for( i in hidx )
							 | 
						|
											syncAction(t,["idxadd",indexId(i)],"Add "+(if( i.unique ) "Unique " else "")+"Index ("+i.keys.join(",")+")");
							 | 
						|
										// PRIMARY KEYS
							 | 
						|
										if( (inf.primary == null) != (t.primary == null) || (inf.primary != null && inf.primary.join("-") != t.primary.join("-")) ) {
							 | 
						|
											style.text("PRIMARY KEY CHANGED !");
							 | 
						|
											hasSyncAction = true;
							 | 
						|
										}
							 | 
						|
										if( hasSyncAction )
							 | 
						|
											style.endList();
							 | 
						|
										return hasSyncAction;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									public function process( ?url : Array<String> ) {
							 | 
						|
										if( url == null ) {
							 | 
						|
											url = Web.getURI().split("/");
							 | 
						|
											url.shift(); // empty : url starts with /
							 | 
						|
											url.shift(); // "admin"
							 | 
						|
											if( url[0] == "index.n" )
							 | 
						|
												url.shift();
							 | 
						|
										}
							 | 
						|
										if( url.length == 0 ) url.push("");
							 | 
						|
										var params = Web.getParams();
							 | 
						|
										switch( url[0] ) {
							 | 
						|
										case "":
							 | 
						|
											
							 | 
						|
											style = new AdminStyle(null);
							 | 
						|
											index();
							 | 
						|
											return;
							 | 
						|
											
							 | 
						|
										case "doSync":
							 | 
						|
											style = new AdminStyle(null);
							 | 
						|
											doSync(params);
							 | 
						|
											return;
							 | 
						|
										}
							 | 
						|
										var table = new TableInfos(url.shift());
							 | 
						|
										style = new AdminStyle(table);
							 | 
						|
										var act = url.shift();
							 | 
						|
										switch( act ) {
							 | 
						|
										case "insert":
							 | 
						|
											insert(table,params);
							 | 
						|
										case "doInsert":
							 | 
						|
											doInsert(table,params);
							 | 
						|
										case "edit":
							 | 
						|
											edit(table,url.join("/"));
							 | 
						|
										case "doEdit":
							 | 
						|
											doEdit(table,url.join("/"),params);
							 | 
						|
										case "doCreate":
							 | 
						|
											doCreate(table);
							 | 
						|
										case "doDrop":
							 | 
						|
											doDrop(table);
							 | 
						|
										case "doCleanup":
							 | 
						|
											doCleanup(table);
							 | 
						|
										case "doDelete":
							 | 
						|
											doDelete(table,url.join("/"));
							 | 
						|
										case "doDownload":
							 | 
						|
											doDownload(table,url.shift(),url.shift());
							 | 
						|
										case "search":
							 | 
						|
											search(table,params);
							 | 
						|
										default:
							 | 
						|
											throw "Unknown action "+act;
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									static function log(msg:String) {
							 | 
						|
										#if neko
							 | 
						|
										Web.logMessage("[DBADM] " + neko.Web.getHostName() + " " + Date.now().toString() + " " + neko.Web.getClientIP() + " - " + msg);
							 | 
						|
										#end
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									public static function handler() {
							 | 
						|
										Manager.initialize(); // make sure it's been done
							 | 
						|
										try {
							 | 
						|
											new Admin().process();
							 | 
						|
										} catch( e : Dynamic ) {
							 | 
						|
											// rollback in case of multiple delete/update - no effect on DB struct changes
							 | 
						|
											// since they are done outside of transaction
							 | 
						|
											Manager.cnx.rollback();
							 | 
						|
											
							 | 
						|
											Sys.print("<h2>Error :</h2>"+Std.string(e));
							 | 
						|
											Sys.print("<h2>Stack :</h2><pre>");
							 | 
						|
											Sys.print(haxe.CallStack.toString(haxe.CallStack.exceptionStack()));
							 | 
						|
											Sys.print("</pre>");
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									public static function initializeDatabase( initIndexes = true, initRelations = true ) {
							 | 
						|
										var a = new Admin();
							 | 
						|
										var tables = a.getTables();
							 | 
						|
										for( t in tables )
							 | 
						|
											a.execute(t.createRequest(false));
							 | 
						|
										for( t in tables ) {
							 | 
						|
											if( initIndexes )
							 | 
						|
												for( i in t.indexes )
							 | 
						|
													a.execute(t.addIndexRequest(Lambda.array(i.keys), i.unique));
							 | 
						|
											if( initRelations )
							 | 
						|
												for( r in t.relations )
							 | 
						|
													a.execute(t.addRelationRequest(r.key, r.prop));
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								}
							 |