Database Audit Trail

Discussions related to customizing hooks. Hooks are documented at http://bigprof.com/appgini/help/advanced-topics/hooks/
Post Reply
User avatar
toconnell
Veteran Member
Posts: 204
Joined: 2013-04-09 19:29
Location: Oklahoma City, OK
Contact:

Database Audit Trail

Post by toconnell » 2014-04-18 12:43

I found this.. https://www.simple-talk.com/sql/databas ... dit-trail/

It is a way to use sql server commands and program a trigger to create an audit trail for tracking changes to all tables. I just want to track changes to 3 tables. I also want to only log the changed data, not everything. Currently I have hook code in tablename_after_insert, tablename_after_update, and tablename_after_delete for it to track changes and it is working. It will not log old data but it does log all the new data.

I really want this to be a cleaner process. I want to do the audit like in POP's article above in the same database with a new table called audit or historical log.. using a trigger I can do this.. my problem for not being able to copy and paste this code from Pop's article above (see link at top) right into a trigger and do this is that I have shared hosting, my sql database is accessed through phpmyadmin and it won't take sql server commands in the sql tab, just basic sql.
In PHPMyAdmin I can create a trigger but never done it before. Not sure what goes in the big box.. ok with choosing the options though.

The tables I want to track are bus_status, compliance and routes
and I want to track only the data that is changed. Right now I have it tracking everything which is making the audit table I have temporarily working quite large. Not good.

If it will only capture the changed data it would be so small as we don't do many transactions per day (like 20 total for all users) so it would be great.

Your thoughts on how best to do that?
Tina O'Connell
Web Dev & Appgini FAN

KSan
AppGini Super Hero
AppGini Super Hero
Posts: 252
Joined: 2013-01-08 20:17

Re: Database Audit Trail

Post by KSan » 2014-04-19 20:15

Quick solution could be to give the mysql commands to run to your hosting company's helpdesk and ask them to run it for you. I'm in a similar situation and had a need to run some mysql commands and my hosting company supported me well on this. Just a quick idea. Will be back to discuss how to do this in pure AppGini.

User avatar
toconnell
Veteran Member
Posts: 204
Joined: 2013-04-09 19:29
Location: Oklahoma City, OK
Contact:

Re: Database Audit Trail

Post by toconnell » 2014-04-22 15:33

Won't work as I have to test and tinker and need hands on access and they are not that willing to play all day with me :)

I really just need to figure out how to show in my IF statement if the $data['Active'] field CHANGED from TRUE TO FALSE.. or FALSE TO TRUE.. If I can figure out that part the rest is easy.

Can you help me with that KSan?

I am trying
if ($mysqli->affected_rows!='TRUE') and no luck.. Your thoughts?
Tina O'Connell
Web Dev & Appgini FAN

KSan
AppGini Super Hero
AppGini Super Hero
Posts: 252
Joined: 2013-01-08 20:17

Re: Database Audit Trail

Post by KSan » 2014-04-23 00:01

I will try but I don't think I am following you. Can you kindly post your current hook code that is working but generating too much chatter in your audit table? Maybe we can all look at it and learn from it while at the same time offering some ideas. Thanks much!!!

User avatar
toconnell
Veteran Member
Posts: 204
Joined: 2013-04-09 19:29
Location: Oklahoma City, OK
Contact:

Re: Database Audit Trail

Post by toconnell » 2014-04-23 21:23

Actually I am using the tablename_after_update hook right now to provide a log and it is working. But it does not capture what the data was changed from, only what it was changed to. Here is my code..

This is the after insert function..

Code: Select all

function compliance_after_insert($data, $memberInfo, &$args){
global $old_data;

foreach($data as $field => $value){
$messageData .= "$field: $value \n";
}

$recID=mysql_insert_id();
sql("insert into membership_userrecords set tableName='compliance' , pkValue='#recID', 
memberID=' ".getLoggedMemberID()."', dateAdded=' ".time()."', dateUpdated='".time()."',
groupID= ".getLoggedGroupID().".", $eo);
 
@mail(
// mail recipient
"[email protected]",
 
// subject
"A Record in the Driver Compliance table of the database was NEWLY CREATED -notification - Driver RECORD NUMBER-{$data['Record_Id']}",
 
// message
      "The above record {$data['Record_Id']} was just modified by: {$memberInfo['username']} The record was updated on and by user id: {$data['LastUpdatedby']},\n. To view the record please go to \n http://MyDomainName.com/MyAppGiniFolder/MyTableName_view.php?SelectedID=".$data['selectedID']."  Newly entered data shows as actual table values below:,
	 {$messageData}  The user that changed this record did so from IP Address: {$memberInfo['IP']}  \n\n", 
	  
 
// sender address
"[email protected]"
);


//Now Capture and log changes to compliance record into the Compliance History Log table called Comp_Hist_Log
sql("INSERT INTO `Comp_Hist_Log` (`Com_Rec_Id`, `old_data`, `new_data`, `ChangedDate`, `ChangedBy`) VALUES('{$data['Record_Id']}', '{$CapoldData}', '{$messageData}', '{$data['LastUpdated']}', '{$memberInfo['username']}')", $eo);
		return TRUE;


	}
This does capture the user id name, the record id number, the time and date the change was done, and all the new data for the entire record is recorded.. so if I compare it to the last change for this record id number, I do have a full log of old and new that way only. I really want to only capture the CHANGED data, not all the new data that was not updated to save space in my log table.
Also I would like to show what fields were changed from what to what.

This does not capture the old data, even with the global declared for old data which was written in the BEFORE INSERT function, I even tried this as a STATIC instead of GLOBAL variable, no luck.

My biggest hurdle is I have to compare the old to the new to get the condition for a function to do my next project and I cannot get the 'Old data" from anywhere. I even tried triggers in phpmyadmin but no luck. I have been researching this for weeks. I need help. I have become a bit of a php expert now, not by choice but still no solution.

Here is the AFTER UPDATE code.. same thing essentially.. just put it in both places.. After insert sends the email and the data to the log after a NEW record is created and AFTER UPDATE sends the email and the data to the log only when a table is modified at all with any update to any field.

Code: Select all

function compliance_after_update($data, $memberInfo, &$args){

global $old_data;

foreach($data as $field => $value){
$messageData .= "$field: $value \n";
}
$recID=mysql_insert_id();
sql("insert into membership_userrecords set tableName='compliance' , pkValue='#recID', 
memberID=' ".getLoggedMemberID()."', dateAdded=' ".time()."', dateUpdated='".time()."',
groupID= ".getLoggedGroupID().".", $eo);
 
@mail(
// mail recipient
"[email protected]",
 
// subject
"A Record in the Driver Compliance table of the database was changed - notification - Driver RECORD NUMBER-{$data['Record_Id']}",
 
// message
      "The above record {$data['Record_Id']} was just modified by:{$memberInfo['username']} The record was updated on and by:{$data['LastUpdatedby']} \n To view the record please go to \n http://domainname.com/appginifolder/compliance_view.php?SelectedID=".$data['selectedID']."  Newly entered data shows as actual table values below:,
	 {$messageData} \n.  The user that changed this record did so from IP Address: {$memberInfo['IP']}  \n\n", 
	  
 
// sender address
"[email protected]"
);
// Capture and log changes to compliance record into the Compliance History Log table
sql("INSERT INTO `Comp_Hist_Log` (`Com_Rec_Id`, `old_data`, `new_data`, `ChangedDate`, `ChangedBy`) VALUES('{$data['Record_Id']}', '{$CapoldData}', '{$messageData}', '{$data['LastUpdated']}', '{$memberInfo['username']}')", $eo);
	
	return TRUE;

	}
	
	

I know SQL 2008 now has this log, but I have my database on a shared hosted server (budget reasons) and only have PHPMyAdmin and cpanel access with hostgator so I do not have SUPERUSER Sql privledges and cannot get them to write MYSQL triggers.. only can do the triggers inside phpmyadmin and have not had any luck doing a single one.

Thanks for any help KSAN. T
Tina O'Connell
Web Dev & Appgini FAN

jimfri
Posts: 12
Joined: 2014-05-05 16:41

Re: Database Audit Trail

Post by jimfri » 2014-05-05 20:04

Hi.

I've just done a quick skim of this, but is this what you'd want? (I'm just speaking generically; no reference to actual code or where to place it.)

I've had AppGini for years but am working on a fairly complex app with "lots of moving parts" so I'm trying it out to see if at least I won't get bogged down in a bunch of data entry forms and can concentrate on what needs to happen.

But here is how I'd approach it at least to research. As I understand it, you want to log what the values were before an update and what they were after an update. So just before the update take the "record key" you're about to update and do a select statement. So let's say it's record 1063. Select * from table where key=1063 There's the fields in the database now. You'd have the variables you're going to write. Before and after...do with them as you wish.

If that won't work, proceed to the next thing which is less a 'sure bet' in its present form. (And hard to explain too...)
===========================================================================================================================================
On the page where I'm editing I just have 4 records. So ideally to try this out you'd want to have more than 1 page of data. (Like default of 10 per page...make sure you have more than 10 records.) I'm working on a trucking company app, so the first thing I looked for in "viewing the html source" was truck #.

(Now "Betty".) So you see SelectedID=2
<td id="Driver_Master-First_Name-2" class="Driver_Master-First_Name "><a onclick="document.myform.SelectedField.value=this.parentNode.cellIndex; document.myform.SelectedID.value='2'; document.myform.submit(); return false;" href="Driver_Master_view.php?SelectedID=2" class="TableBodySelected" style="display: block; padding:0px;">Betsy</a></td>

***VERY IMPORTANT*** I'll also add that in the snippet above is also SelectedID=1, 2, 3 & 4 so it's not unique. When I clicked on a row to edit, then viewed source, I did see that truck #860, 860 was in the html twice now, whereas my other truck #'s were only in there once. But truck #'s not my key so I can't say if I'd be able to do anything with that info...

So because I just have 4 records I don't know if I had 100 records if it would still display the 2nd record as SelectedID=2 or as 92 etc. So when you click and it puts the record to edit down in the detail view I don't know if that is accessible or not. But what if you could capture that record key (assuming it's just not "2" as the 2nd record on the page) and do something with that. So if I click on the 2nd record on the page (whatever its unique key is) and I don't end up saving the record nothing to log. I could click, click, click but finally I save a record. "That's" what I want to capture assuming it's the key. Then before update do the select etc...

Again, hopefully the first example will give you enough to go on to help. Definitely let me know if that works for you. (I'm working on logging but that will be one of the least important features and a lot to do meanwhile but that's how I ended up reading this.)

Take care.

Jim

jimfri
Posts: 12
Joined: 2014-05-05 16:41

Re: Database Audit Trail

Post by jimfri » 2014-05-05 20:15

Me again.

Different file so don't get confused about lack of truck #. :-)

But in my Company_Master.php file, in the comment before update look at $data['selectedID'] which holds the primary key for the record to be updated. That's where I'd try to query with a select to get the fields. (Again, new to appgini, so you may know this isn't the place...but _after_update is too late...)

...
* $data['Company_Master_Key'], $data['Company_Name'], $data['Company_Type_Key'], $data['Percentage_Withheld_From_Payment']
* Also includes the item $data['selectedID'] which stores the value of the primary key for the record to be updated.
* $data array is passed by reference so that modifications to it apply to the update query.
...

function Company_Master_before_update(&$data, $memberInfo, &$args){

return TRUE;
}

Wish I was further along in the project to be logging myself and I'd take the time to test my ideas before spewing them out here. :-)

Jim

jimfri
Posts: 12
Joined: 2014-05-05 16:41

Re: Database Audit Trail

Post by jimfri » 2014-05-05 22:06

Hi.

Got before updates working. I'll post code in a few as soon as I clean it up.

Also, to be clear, I just logged to a data file for now to speed things up. I know from another thread that you had your after_update code logging to a database so you'll be fine. :-)

Jim

jimfri
Posts: 12
Joined: 2014-05-05 16:41

Re: Database Audit Trail

Post by jimfri » 2014-05-06 01:33

Sorry for the delay in getting this posted.

I'd hoped to point out some things but can't due to schedule. I'll say that the code is adapted from the support files online.

As previously mentioned I'm writing to log files for now instead of putting in database but I know you got the after_update code logging to a database so you'll be fine with before_update.

I have omitted my ip address and logged in username but the code works.

Jim

Code: Select all

	function Company_Master_before_update(&$data, $memberInfo, &$args){

#Getting a single field works fine...now working to get all fields...
$selectedID = $data['selectedID'];
$oldcompanyname=sqlValue("select Company_Name from Company_Master where Company_Master_Key='{$data['selectedID']}'");

$res=sql("select * from Company_Master where Company_Master_Key='{$data['selectedID']}'");
while($row=mysql_fetch_row($res)){
#Company_Type_Key 	Percentage_Withheld_From_Payment 	ActiveOrInactive
$thisdbkey = $row[0];
$dboldco = $row[1];
$cotypek = $row[2];
$percenttaken = $row[3];
$isactiverecordornot = $row[4];
}

$res2=sql("select * from Company_Master where Company_Master_Key='{$data['selectedID']}'");
#mysql_fetch_array
while($row2=mysql_fetch_array($res2)){
$dboldcobynamearray = $row2["Company_Name"];
$cotypekbynamearray = $row2["Company_Type_Key"];
}

// log file
#Despite the filename, so far I'm just logging updates before.  I'll adapt this to do _after_update and expect it to work just fine...
$logFile='05052014testlogrecordsbeforeandafterupdates.log';
 
// attempt to open the log file for appending
if(!$fp = @fopen($logFile, 'a')) return;
 
// write log data: date/time, username, IP, record ID
$datetime=date('r');

#'BEFOREUPDATE',Mon, 05 May 2014 16:03:35 -0700,Get Paid Right Now LLC,3,Get Paid Right Now LLC,Get Paid Right Now LLC,1,1 ,10.20,Active,{username works; obscured},{ip #address works; obscured},3

fwrite($fp, "'BEFOREUPDATE',$datetime,$oldcompanyname,$thisdbkey,$dboldco,$dboldcobynamearray,$cotypek,$cotypekbynamearray ,$percenttaken,$isactiverecordornot,{$memberInfo['username']},{$memberInfo['IP']},$selectedID\n");
fclose($fp);

		return TRUE;
	}

User avatar
toconnell
Veteran Member
Posts: 204
Joined: 2013-04-09 19:29
Location: Oklahoma City, OK
Contact:

Re: Database Audit Trail

Post by toconnell » 2014-05-08 21:10

Jim,

I cannot thank you enough for your efforts.. I am working to convert this code to my field names and table names and will let you know how it goes.. I may have a few quick questions after that. Please let me know if you need any help. I love Appgini and it is one of my favorite programs!!

For reporting.. check out reportico.org.. you may like that too!

Thanks again.. be back soon to report.
Tina O'Connell
Web Dev & Appgini FAN

User avatar
toconnell
Veteran Member
Posts: 204
Joined: 2013-04-09 19:29
Location: Oklahoma City, OK
Contact:

Re: Database Audit Trail

Post by toconnell » 2014-05-08 21:27

Jim,

It looks like you have done this 3 different ways???

OK..

I get $res is the variable.. they query is the

Code: Select all

$res=sql("select * from Company_Master where Company_Master_Key='{$data['selectedID']}'");
while($row=mysql_fetch_row($res)){
part..

What is this?
#Company_Type_Key Percentage_Withheld_From_Payment ActiveOrInactive
$thisdbkey = $row[0];
$dboldco = $row[1];
$cotypek = $row[2];
$percenttaken = $row[3];
$isactiverecordornot = $row[4];
}


# Company_Type.. is that just a note?
$thisdbkey=row[0] is this to put that field name into row 0 of your log file?

I want all the fields to drop into only 1 field.. just like the new data does I used the {$messageData} from the email function I did and just dropped that in.. that code was

Code: Select all

foreach($data as $field => $value){
$messageData .= "$field: $value \n";
}

Can I not in the BEFORE UPDATE hook just do the same thing but call it a different name.. like this..???

Code: Select all

global $old_data;

foreach($data as $field => $value){
$old_data .= "$field: $value \n";
}

?


Or from what I see in your code it should be instead:

Code: Select all

   function routes_before_update(&$data, $memberInfo, &$args){

$selectedID = $data['selectedID'];
$old_data=sqlValue("select * from routes where ='{$data['selectedID']}'");

//record_id is my primary key in the routes table

$old1=sql("select * from routes where '{$data['record_id']}'='{$data['selectedID']}'");
while($row=mysql_fetch_row($old1)){

//NOT SURE WHAT TO DO WITH THIS PART you have below?? If fields mine to capture are 
/*
  $data['School'], $data['RouteNumber'], $data['County'], $data['school_name'], $data['school_contact'], $data['school_contact_number'], $data['DriverAssigned'], $data['DriverAssignedpm'], $data['UnitAssigned'], $data['PMUnitAssigned'], $data['Lead_driver'], $data['LeadDriverBackUp'], $data['OpsMgr'], $data['Sub_assigned1'], $data['Sub_assigned2'], $data['SpareBusses'], $data['KMLFileLink'], $data['miles'], $data['DateDriverAssigned'], $data['DateDriverUnassigned'], $data['Notes'], $data['RouteStart'], $data['RouteEnd'], $data['FirstStopOnRoute'], $data['LastStopOnRoute'], $data['MorningBellTime'], $data['AfternoonBellTime'], $data['LastUpdated'], $data['DateSubAssigned'], $data['TripSubAssigned'], $data['route_start_date'], $data['route_stop_date'], $data['school_contact_ah'], $data['school_ah_contact'], $data['record_id']
*/


$thisdbkey = $row[0];
$dboldco = $row[1];
$cotypek = $row[2];
$percenttaken = $row[3];
$isactiverecordornot = $row[4];
}

$old2=sql("select * from routes where '{$data['record_id']}'='{$data['selectedID']}'");
#mysql_fetch_array
while($row2=mysql_fetch_array($old2)){
$RouteNumber = $row2["RouteNumber"];
$School = $row2["School"];
}

//I am lost after this part???   Can you help with my fields above and the table name as routes??   Just want to get this right. 

// log file
#Despite the filename, so far I'm just logging updates before.  I'll adapt this to do _after_update and expect it to work just fine...
$logFile='05052014testlogrecordsbeforeandafterupdates.log';
 
// attempt to open the log file for appending
if(!$fp = @fopen($logFile, 'a')) return;
 
// write log data: date/time, username, IP, record ID
$datetime=date('r');

#'BEFOREUPDATE',Mon, 05 May 2014 16:03:35 -0700,Get Paid Right Now LLC,3,Get Paid Right Now LLC,Get Paid Right Now LLC,1,1 ,10.20,Active,{username works; obscured},{ip #address works; obscured},3

fwrite($fp, "'BEFOREUPDATE',$datetime,$oldcompanyname,$thisdbkey,$dboldco,$dboldcobynamearray,$cotypek,$cotypekbynamearray ,$percenttaken,$isactiverecordornot,{$memberInfo['username']},{$memberInfo['IP']},$selectedID\n");
fclose($fp);

      return TRUE;
   }
Tina O'Connell
Web Dev & Appgini FAN

jimfri
Posts: 12
Joined: 2014-05-05 16:41

Re: Database Audit Trail

Post by jimfri » 2014-05-13 19:48

Hi.

I'm just now getting a chance to look at your code.

However, I'll add the following meanwhile:

Yes I demonstrated 3 different techniques to accomplish retrieving existing (old) data from a database table before updating the record with new data. (All of my code was adapted from examples given by appgini. I'm sorry that I wasn't able to take the time to document better. Due to time, it was either post "as is" or "never post" because I didn't have time to re-find the code here.)

a. Retrieve a single field from a table.
b. Retrieve a record from table based on it's position. (I'd never personally do this but it's based on an example here so I included it.) So the number 0 is the first field in the table, 1 is 2nd field, 2 is 3rd field etc. So, yes, the code breaks if you're expecting the 2nd field to contain a certain value and you modify your table to put another field anywhere but the end. (Hence why I'd never use...)
c. Retrieve a list of fields by name of the field in the table.

Hope this gets you started...

Jim

jimfri
Posts: 12
Joined: 2014-05-05 16:41

Re: Database Audit Trail

Post by jimfri » 2014-05-13 22:07

Hi.

A "5-10" minute conversation turns into back-and-forth on the forum. So here is what I did in the example shown below:

a. I only gave 1 example vs. 3 ways to do it. (And the way I do it isn't documented anywhere on appgini's site that I can find which is regrettable it's so simple.)
b. I commented *everything* assuming *no* knowledge which can be confusing in and of itself if I am trying to explain (without feedback) something you already know.
c. ***THIS IS UNTESTED CODE WHEREAS MY PRIOR CODE WAS TESTED. I SIMPLY DON'T HAVE TIME TO TEST...I've reviewed and think all my braces line up etc. but if this code simply causes an error print out my original example and this one and you'll likely find where I made the mistake in the *new* code.
d. Inside the code comments I give the appgini URL where I found what I needed to do *before update* and put inside the comments a couple of keywords to search for
so you can see for yourself.
e. There was *at least* one bug in your code back to me so I just coded it from scratch given the info on field names you supplied to me.
f. Rather than add further to the code you should use my old code for an example of how to write out the variables to the log file.
g. I've added some code like makesafe, testing to make sure we got a record retrieved etc which I didn't include in the original example.
h. Anytime you see # or // or /* */ those are comments so for example I left *my* query example in and a variable assignment of mine in and commented out the line.

The steps are:

a. Get the key of the record we're about to update.
b. Get results from database showing the existing record data and store the database fields in variables.
c. Do "something" with those variables (in this case I'm writing out to a log file.) You could write to a "log" table or whatever...for simplicity I've left out the *what* to do with the data you just fetched and assigned to variables.

Hope this helps! :-)

Jim

Code: Select all

function routes_before_update(&$data, $memberInfo, &$args){

/*
To find concepts that I adapted from help, go to the following URL:
http://www.bigprof.com/appgini/help/advanced-topics/hooks/table-specific-hooks#tablename_before_update

And search for: 
makeSafe (which help notes "Also, notice the use of the makeSafe() function which prepares variables 
to be used safely inside SQL queries.")
sql(   (found under tablename_dv()) which retrieves an entire row in a table vs. a single field.)

mysql_fetch_array is not shown in the help file so I adapted what I learned from mysql_fetch_row 
in the appgini help (mysql_fetch_row fetches fields by positionnumber in database which is *not* 
a good way to do it.  (Imagine you have 5 fields initially.  Later you insert another field in the middle 
of the 5 fields.  What used to be field 4 is now field 5, etc so if you just broke all your code...)

mysql_fetch_array fetches the record by field names and number.  (So I reference my field by the actual 
field name rather than field position.)  See:
http://php.net/manual/en/function.mysql-fetch-array.php
/*

$selectedID = makeSafe($data['selectedID']);

//The next line is just *my* example from *my* table where I commented it out with // so see the last line for routes table
//$res2=sql("select * from Company_Master where Company_Master_Key='{$data['selectedID']}'");

    //record_id is my primary key in the routes table (YOUR COMMENT TO ME)
//The SQL statement format is Select * from {tablename} where {databasekeyfield} = '{$data['selectedID']}'");
$res2=sql("select * from routes where record_id='{$data['selectedID']}'");

//If there is at least 1 row returned do what's inside the braces of if(mysql_num_rows($res2))
if(mysql_num_rows($res2)){

//Since we're getting a row *by key* we don't need to loop inside a while.  (Only 1 record should be 
//returned obviously.)  But I'm leaving it in to show if you had *5* child records you could fetch all
//of the "result set" at once.  (Obviously you'd have to insert *what* you wanted to do with the row
//inside of the while in that case where here I know I'm only expecting 1 record.)

while($row2=mysql_fetch_array($res2)){
//Commented out the following line so you can see an example of a field name in *my* table which I assign to a variable.
//$dboldcobynamearray = $row2["Company_Name"];

//$data['DriverAssigned'] $data['miles'] (YOUR COMMENT TO ME)
//To keep things simple I'm only giving 2 examples of fields from your entire list.  Just make a variable
//to hold the contents from the database on the left side of the equal sign and put the field name from 
//database inside double quotes as shown below.
$busdrivername = $row2["DriverAssigned"];
$busroutemiles = $row2["miles"];
}  //End while loop while($row2=mysql_fetch_array($res2))

//To keep this short just refer to my original post as I write the variables out to a log file.
//Open the log file appending data to the old log and write out your variables.
//Example:  You'd write out the variables $busdrivername & $busroutemiles.
//Close log file

} //End if(mysql_num_rows($res2)) (For simplicity, I didn't code an "else" condition if no records found.)

} //End function routes_before_update(&$data, $memberInfo, &$args)

jimfri
Posts: 12
Joined: 2014-05-05 16:41

Re: Database Audit Trail

Post by jimfri » 2014-05-13 22:16

Hi.

I realize I didn't address the putting everything in 1 field.

I'd get my code working 'as is' first and then experiment. This is so different than how I normally write code in that I normally am writing a section of code at a time and continuously testing my code as I build it.

Jim

jimfri
Posts: 12
Joined: 2014-05-05 16:41

Re: Database Audit Trail

Post by jimfri » 2014-05-13 22:25

Argh, also forgot the
return TRUE;
before the function closing brace.

(See appgini before_update documentation if unclear where to place...)

User avatar
toconnell
Veteran Member
Posts: 204
Joined: 2013-04-09 19:29
Location: Oklahoma City, OK
Contact:

Re: Database Audit Trail

Post by toconnell » 2014-05-23 19:15

Thanks Jim.. I will check it out and let you know how it goes and post up my final :) Tanks : )
Tina O'Connell
Web Dev & Appgini FAN

User avatar
toconnell
Veteran Member
Posts: 204
Joined: 2013-04-09 19:29
Location: Oklahoma City, OK
Contact:

Re: Database Audit Trail

Post by toconnell » 2014-06-20 20:25

Jim,

I got it working with the help of my friend UDAY a programmer who is brilliant.. but you were right on target.. was just that little typo and wala.. we did a few other edits to make it capture the old data and only email if a certain field was updated but it works perfectly now if you want the code I can depersonalize it.. just PM me.

Thanks, Tina
Tina O'Connell
Web Dev & Appgini FAN

Post Reply