31 July 2006

Date Sorting Algorithm: Revealed!

function createAuditDocList(XMLAuditDocs)
// The Audit Doc object class
function CAuditDoc(UNID, date, time, meridian)
this.UNID = UNID;
this.date = date;
this.time = time;
this.meridian = meridian;

// The "end of array" checking function for iterations
function endOfLoop(index, array)
return (index == array.length);

// Define variables
var rowID = '';
var rowDate = '';
var auditList = '';
var tempAuditDocObject = null;
var rawDocArray = new Array();
var finalDocArray = new Array();

// Loops through the XML Data, building an audit document object for each "row"
for (var i = 0; i < XMLAuditDocs.length; i++)
rowID = XMLAuditDocs.item(i).getAttribute("unid");
rowDate = getViewRowValues(XMLAuditDocs[i])[1];
tempAuditDocObject = new CAuditDoc(rowID,
rowDate.substr(0, 10),
rowDate.substr(11, 8),
rowDate.substr(20) );


// Define sorting variables
var lastDate = rawDocArray[0].date;
var lastMeridian = rawDocArray[0].meridian;
var meridianSwitch = false;
var dayDocArray = new Array();
var currentDate = '';

for (var j = 0; j < rawDocArray.length + 1; j++)
// If this isn't the last iteration, set the currentDate variable;
// If it is, then set the currentDate to null to force the last sort
currentDate = (!endOfLoop(j, rawDocArray)) ? rawDocArray[j].date : null ;

// If the current doc's date is different from the last one checked,
// process the last day's docs and then reset the sort;
// this also runs automatically on the very last iteration
if (currentDate != lastDate)
// Check to see if a sort is in order, and if so, do it
if (meridianSwitch)
while (dayDocArray[0].meridian != 'PM')

// Concat dayDocArray[] onto the end of finalDocArray[]
finalDocArray = finalDocArray.concat(dayDocArray);

// If this is not the last iteration, reset the sort variables
if ( !endOfLoop(j, rawDocArray) )
dayDocArray = new Array();
lastDate = rawDocArray[j].date;
lastMeridian = rawDocArray[j].meridian;
meridianSwitch = false;

// If this isn't the last iteration, then push the current rawDocArray[]
// onto the current dayDocArray[]
if ( !endOfLoop(j, rawDocArray) )
if ( rawDocArray[j].meridian != lastMeridian )
lastMeridian = rawDocArray[j].meridian;
meridianSwitch = true;

} // end for


// Begin constructing HTML select object
auditList = 'Choose an audit trail... ';
auditList += '';
document.getElementById("XMLArea").innerHTML = auditList;

This function accepts an XML object of the following (repeated, of course):


mm/dd/yyyy hh:mm:ss AM|PM

Each viewentry represents a response document. The response's UNID is the "unid" attribute inside the viewentry tag -- the parent document's UNID is the first "column" of XML data. In this model, a parent document can have many response documents.

By the way, for anybody trying to post code online, "<span style="margin-left: 50px"></span>" works great for simulating tab stops!

28 July 2006

Newly Discovered Firebug Features

Hey, I just found the neatest feature ever of the new extension "Firebug" for Firefox that I've been using the past couple of days -- did you know that its JavaScript console can track the results of XMLHttpRequests? Well, it can -- all of them -- even if you have to run many at once. I don't know how I designed before I got this extension -- it certainly took a lot longer to debug, especially on Firefox (which doesn't really have a built in debugger, unfortunately).

[The pixelated areas are just XML information -- you're not missing anything, trust me.]

OpenNTF.org - WinmailExtractor: Working!

OpenNTF.org - WinmailExtractor

Just got it working! From what I hear, it's been a raving success (considering I've sent tons of test emails and it actually works).

If you're a Lotus Notes Administrator and you're having trouble with your users reporting un-readable "winmail.dat" attachments, get your design team to enact this mail-in database!

Un-fucking "winmail.dat" files

OpenNTF.org - WinmailExtractor

I'm currently helping my supervisor fix the oft recurring problem of non-Microsoft mail servers being unable to access "winmail.dat" files by using the solution found above. In case you didn't know, "TNEF" is a proprietary email format that Microsoft is using to try and force administrators and IT departments to switch to their software -- it encodes email attachments as "winmail.dat" files, making them almost unreadable.

This kind of business practice makes me sick, seriously. I don't get it -- what ever happened to the days when you put your competitors out of business by making a superior product? Nowadays, businesses just seem to be hell-bent on making their product wholly incompatible with anyone else's product, thus forcing their clients to do business with them and them only.

This whole process makes me remember the slew of programs that came out when Microsoft released its proprietary .WMA format to convert them to MP3's (my favorite was one called "Un-Fuck", honored in my title today).

Array Addition Revisited

Well, upon viewing a bit of the code I posted yesterday, I came upon an easier way to write something...

Instead of:

while (arrayTwo.length > 0)

You can just have:

arrayOne = arrayOne.concat(arrayTwo);

I talked about the array.concat() method yesterday, and knew that it concatenated one array onto the end of another for display -- what I forgot about was JavaScript's ease of use in things like this! You just take that output and assign it to another array, in this case, the array I'm concatenating onto.


I still think my code was neat, though. :p

27 July 2006

Date String Sorting Algorithm: Completed

I'm done! Wasn't all that hard once I stopped trying to do it the quick and dirty way (with delimited strings) and started doing it the right way (using object-orientated design). It took a little bit longer to code, and the code is probably just a smidgen longer, too, but it's so much easier on the eyes...

I'll try to get the code up here, once I get it formatted for the web. (Yes, I could just stick it up here right now, but all my beautiful formatting would be gone... no tabs, no spacing, nothing...)

There were two little thingies that I was fairly proud of -- the first was the following:

while (arrayTwo.length > 0)

Want to guess what that does? Why, it loops through arrayTwo, takes each entry and pushes it onto the end of arrayOne! As far as I know, JavaScript doesn't have anything like this built-in (it does have an array.concat() function, but it's just for output -- I already had three arrays by this point, and didn't want any more). This little loop will attach all the entries from one array onto the end of another, without having to create another array. Learn your push() and shift() array functions, folks -- I don't know what I ever did before I knew these two. It's sped up my programming considerably.

while (array[0].value != [some_value])

Figure that one out? It loops through an array, taking the beginning entry and moving it to the back, until it comes to a certain value (or in my case, the beginning of a type of values). This was very useful once I had separated each section of entries from a particular day -- since Domino ordered it with the AM section first and the PM section after, I just set [some_value] equal to "PM", and sent the array through this loop. Easy-peasy.

Date String Sorting Fun

Wow -- this date string sorter is turning out to be a little bit more trouble than I thought. The actual logic isn't all that hard (it's about three arrays, one class, and some string functions) -- I've just been picking over the little details for hours now!

26 July 2006

A "blog?" What the hell is that?!?

You know, nothing personal, but I find it enormously funny that Blogger's spell-checker didn't recognize the word "blog."

Sorting Date Strings

As of this moment, I'm trying to get Domino date lists to sort out in a more intuitive fashion.

In the XML file that I'm accessing, the dates are ordered from most recent day to oldest day; however, the entries belonging to the times of each day are sorted with AM first (it appears to be sorted alphabetically -- silly Domino). For example, a document with the date 07/25/06 will come before 07/24/06, but a document made at 10 a.m. on 07/25 will come before one made at 3 p.m. on that same day.

Well, this certainly cannot be! If anybody knows of a way to get Domino to do this automatically, feel free to comment. (And if you've found this blog so soon after I started it, thanks for posting!)

Right now I'm just taking the document dates, putting them into arrays and string lists, and parsing the hell out of them until they're ordered right. Fun stuff.

Auditing Explained

Okay, let me explain a little bit about what I've been doing. I've been instructed to produce "auditing" scripts on a database application that has been assigned to me -- basically, a series of scripts that will keep track of changes made by users in documents of the application.

Not so hard, eh?

I wish. Making an actual script like this work is no big deal, of course -- making it secure, however, is.

This application is going to be web-based, so that means that the bulk of my programming is going to be done in JavaScript -- although, as I'm sure you know, as far as security is concerned, JavaScript has very, very little. The entire nature of a client-based scripting language like JavaScript pretty much negates the idea of security entirely -- you see, the user has access to everything, and, well, what's stopping them from changing something stored on their own computer, hmm? Nothing.

So, I've had to learn the server-side scripting language necessary for this to work, and on a Domino server that means LotusScript. And of course, to utilize LotusScript in a web application, you have to make use of the "WebQueryOpen" and "WebQuerySave" server events.

I combined these two technologies together into one pretty mean auditing script. Since variables you assign in the WebQuery events aren't saved, the only way I could track changes in the document currently being edited by the user was to actually create a temp document that stores the values of the document at it's opening. Then, when the user saves, the values they've toyed with are tested against the values found in the temp document, the necessary audit record is made, and the temp document is deleted. Pretty neat, huh?

Well, there were bugs, of course. Due to the stateless nature of the web, sometimes temp documents are orphaned if the (always scatterbrained) user closes down an edit window before he/she has saved -- the WebQuerySave event never fires on the server, and the temp document is never deleted. There's a quick and easy fix to that, though -- just have an agent run every night, deleting any orphaned temp documents from the previous day. Have it run at about 2 a.m. or so -- that way the unlikely scenario of a user editing a document at midnight and his changes not being audited won't occur.

(Now that I think about it, I guess I can try using the JavaScript "onunload" event to somehow control what happens if a user closes down a window without saving -- I'll have to look into that.)

Another little problem happened when I tried to test the app with a test user who was barebones in the way of file permissions (up until this point I had been testing it with my own Designer-level access) -- you can probably guess what happened, eh? No changes were audited! Why? Because they didn't have enough access to create temp documents! Doh-doh!

Well, that was a simple fix -- just uncheck "Run as Web User" on the agent properties box. That way the agent will always run as its designer -- also, logging the current user's name has to be taken care of with the CGI variable "Remote_User" instead of the more typical Session.EffectiveUserName.

So, there you have it! I'll try posting the code one day, once I've taken it apart and made it portable.

Once I've gotten rid of any more bugs, of course.

Fixed Agent Running Problem

Finally fixed that annoying access problem! Turns out I didn't need to give Create and Delete access to the Default user at all -- I just deselected "Run as Web User" in the properties box. Now, it takes a little more finagling to get the current effective user name (actually, you just grab it form the CGI variable "Remote_User"), but hey -- it works!

Starting up

One sec -- let me get the hang of this.