|
|
Introduction - The story of how we got into this mess!You can read in almost every computer magazine today "The key to the future in the computer industry is integration". It is clear to all of us that this integration is going to happen via the Internet. So, Mikael and myself propose to jump on the bandwagon and produce an application that everyone can use on the Web, integrating whatever our customers need to integrate. We have been in this business for a while, so the path is certainly familiar. The technology we use is the absolute latest, but we all know that it is soon to be replaced with newer technology - which can sometimes happen within a couple of months. This can sometimes even happen to "now" technology - before we even get a change to make the most of it! We hope that the "new" will solve all our problems, and integrate everything! We decide the time to join the ride is now or never. Behind already!Of course, no sooner than we start to plan the 100% web-based software, we are already behind. This is nothing new to everyone in the fast paced, media merging Internet race that we're in. The buzzword "new" means our development techniques are too old and we need to start learning the "new" - throw out "today" or "now". "But do these current technologies work?" Mikael asks me. I say "Who cares? Whatever we start using is out of date already!". We must try to work only on something new and original, using the "new" technologies. The Billion-dollar Idea?Our first project meeting starts with coffee - java of course! This is the only old development technique we can't throw away. "Where do we start? What do we do first?" I say to Mikael. Mikael sips his quadruple espresso and says nothing. The application needs to "integrate", "collaborate", "solve all problems", and be "new" (using, of course, the Internet). Since, we also have to worry about when the "new" technology will arrive, we don't have time to teach, explain and market our application. "Something everyone can understand!""Something everyone can understand!" mutters Mikael. "What is?" I reply. We brain storm. We sip our coffee. The only idea that came in my storm was a hotel/conference scheduling application. Mikael was not much more original or "new" since he had a contact management application. "Like that hasn't been done before!" Just then I think about how far we are getting behind in our Project plan. "That's it!" "Something everyone can have use for!"Everyone has use for a Project managing program. Sure, it's not much different then a super charged Day - Week - Year planner… "Does everyone have a project to do?" Mikael asks. Anything and everything can be project. The more you use it the more time you should have after as well. Even the time management Gurus will back us here. "Isn't that old?"That was the final question I let Mikael ask! "Schedule us another meeting next week. Same time." (We always go to a café for our meetings!). "We need to check out the existing Project Management Applications to see if they are 'Old'". The Meeting to Check out the Project AppsLuckily for us, our first meeting wasn't a waste. The current project applications only provide what some of them call Internet capabilities but were nothing more than screen dumps or crude static HTML pages of the project plan - nothing "new"! Our ride shifts into gear. The Model - Project 98Like Da Vinci needed Mona Lisa, we need Microsoft's Project98. Since MS Project98 was written as Win32 application it would be a test to our skill to match the layout and some of the functionality. MS Project98 is also the most common project management application sold, thus making the learning curve much shorter for our Web version. One problem: the Project98 GUI. This could be the reason why it not available on the Web today.
MS-Project98 model to duplicate. Is the GUI possible on the web? Help! Technologies Needed?Our last coffee meeting before squeezing the life out of our computers was to find out what technologies we could use and needed to make the model of WebProject: otherwise known as the webified version of MS Project98. We knew that this meeting would require several double mochas. Technology Item: Collaboration means only CDO?The initial requirements for our application were integration and collaboration. This also happens to be one of the specified problem areas of MS Project98. We knew that MS Project98 integrates with Outlook respectably, but it also requires some troublesome installations on each client in order to use the tasks created by MS Project98. We needed a way around that problem. We have worked several times before on other CDO projects and knew that CDO doesn't support regular Outlook tasks (TaskItem) directly or even via Outlook Web Access - let alone MS Project98 special tasks. I also knew that the only way for us to get any type of Task integration without installations on each client would be to make the default Outlook TaskItem work via CDO. So, CDO was in. Technology Item: Throw in ADSI?Since we are working with CDO and possibly an Exchange Server, we can assume from a technology point of view, that ADSI might come in handy as well on our way. Technology Item: ASP Please!At the current time, and for the foreseeable future, it will be hard to beat Microsoft's Active Server Pages for flexibility in development of any web-based application. Our slogan is something like, "ASP, don't drive on the web without it!" Technology Item: GUI must have DHTML!The webifying of MS Project98 would certainly be impossible without the crazy power of DHTML. DHTML pushed the limits of the Internet Activity Board to say, "No more, Lord help us, we have gone to far!" We will have to use every ounce of that power to reproduce the MS Project98 GUI. Technology Item: ASDF??ASDF, the acronym for the most common characters typed by testers and programmers alike in trouble! This represents the tricks and techniques we'll have to make use of during the development process in order to fly to completion as smoothly and quickly as possible. ASDF represents tools such as Remote Scripting, JavaScript classes, etc. The Coding Begins...Create TaskItemASDF! Where to start? So, I popped open the can of Outlook2k objects in the VB object browser:
The surprising number of TaskItems to create. To my surprise there was not only one TaskItem for a Task but several TaskItem's: TaskRequestItem, TaskRequestAcceptItem, TaskRequestDeclineItem and TaskRequestUpdateItem. Hmm! "Accept", "Decline", and "Update". These words were music to my ears. This must mean that TaskItems already have some workgroup functionality. This I would have to investigate eventually. However, I still needed to focus on my initial task; how to create a TaskItem on the web. The obvious answer could be to use Outlook on the server as server side component in ASP, but that would most likely result in some huge memory problems on the web server. Another problem would be the opening of a MAPI profile for each client who logs on. The profile would have to be created dynamically. My answer must be to create the Tasks via CDO. Unfortunately, CDO doesn't support a TaskItem in its object model. The only good thing is that I know from previous CDO work that Outlook items are functional in CDO: for example Outlook Web Access's ContactItem. The Outlook Web Access ContactItem example was the key. This must mean that the Outlook properties and values must exist in the Message object as Fields. A "cold" call to the CDO Message objects is needed. Before: Hard core Properties and Fields for diggingLuckily again, I already had this code from the work we did on our Wrox book: "ADSI CDO with ASP". It would simply be to point to the default Task Folder of the profile's private information store for a start. <!--#include file="lib/CDOconstants.inc"--> <!--#include file="lib/logon.inc"--> <!--#include file="lib/CDOlogon.inc"--> <!--#include file="lib/resUtils.inc"--> <!--#include file="lib/createTaskItems.inc"--> <% BAuthenticateUser Dim objOMSession Dim objTask Set objOMSession = Nothing LogonEx "SERVERNAME", "Administrator", "password" Set objFldTasks = objOMSession.GetDefaultFolder(CdoDefaultFolderTasks)
Set objTasks = objFldTasks.Messages Set objTask = objTasks.GetLast() getFields(objTask)
objOMSession.Logoff %> getTaskProperties.asp – Shows how to get the default Task Folder via CDO. The code in getTaskProperties.asp uses the CDO Session object's method called GetDefaultFolder. The CdoDefaultFolderTasks constant found in CDOconstants.inc file specifies value of 8. This is easiest way to get to the default Task folder. It is a little strange that CDO gives us a method to access the Task folder but doesn't supply a TaskItem object. The getFields(objTask) Sub does all the work of enumerating through the Fields collection and displaying the Field id and values. Sub getFields(objNewMsg) Response.write objNewMsg.Class & "<BR>" For each Field in objNewMsg.Fields if NOT IsArray(Field.Value) then Response.write "<I>&H" & Hex(Field.ID) & "</I> " Response.write CStr(Field.Value) & "<BR>" if NOT Err = 0 then Response.write "Error on FieldID: &H" & Hex(Field.ID) + "<BR>" Err.Clear end if else For each arField in Field.Value Response.write "IS ARRAY<BR>" Response.write "<I>&H" & Hex(Field.ID) & "</I>" Response.write " " & CStr(arField) & "<BR>" Response.write "ARRAY END" Next Response.write "<BR>" end if Next
if objNewMsg.Class = 3 then For each Att in objNewMsg.Attachments Response.write "<BR>Attachment<BR>" getFields(Att) Next end if End Sub ResUtils.inc – GetFields function show how to enumerate for Field id and values on CDO Message. The GetFields Sub is nothing to fancy except that it will confirm my hunch that Outlook Properties exist on the CDO Message as well. The enumeration of the Fields collection is easily done with a For Each Field In objNewMessage.Fields loop and the display of the Hex id with value is Hex(Field.ID) and Field.Value. A basic variant check is done to determine if our Field.Value is an Array and if so handle it gently. An addition of the Fields enumeration is the enumeration of the Fields of each Attachment for the Message as well. Before running the code it would be wise if we applied some values to a TaskItem in Outlook so we can decipher the right Field value and Hex Id to Outlook properties.
The Outlook TaskItem with some Properties to help us find the appropriate CDO Field Ids. Now when we run the getTaskProperties.asp code it will give us some easy to identity Fields (and some not so easy!).
Result of getTaskProperties.asp with some easy to identify Task Properties to Fields Ids. Actually, the displaying of the properties is the easy part. The boring and time-consuming task is to define and match the named Properties to a constant Task Field.ID names. Now that we have defined some of the possible constants for a Task Field name it would be nice to be able to update or create a Task ourselves via the web. Remember my initial task! After: New lookThe creation of a task might not be all that difficult after we have defined all the constant Task Properties that we could find. In reality, it is the same as creating a simple CDO Message and setting some custom Field values. … Set objTasks = objFldTasks.Messages Set objTask = objTasks.Add … bstrStatus = Request.Form("status") bstrMilage = Request.Form("milage") bstrBillingInfo = Request.Form("billingInfo") … 'Outlook 2000 TaskItem values objTask.Fields.Item(&H80E10003) = bstrStatus … objTask.Fields.Item(&H8033001F) = bstrMilage objTask.Fields.Item(&H8034001F) = bstrBillingInfo objTask.Update() … The receiving values are added to the right Custom Field Property to create the Task. It is initially a little difficult to come up with the right Field.ID values that are need for integration with Outlook2K but since we spent the time doing the work for you it will be no different then creating a normal CDO message. The hardcore difficulty is over and we can move onto the fun where we begin the imitation of Project98 with its TaskItem. The only difference is that ours will directly map into your Task folder of your choice. This is only some basic DHTML fun!
CreateTask.asp in action. My 1st task done! The results are exactly what we expected. A TaskItem in my Task folder and all the properties matched!
The results of createTask.asp as viewed in Outlook98. Perfect! Update TaskItemsDuring one of our previous meetings, we had discovered that Outlook supports not only the TaskItem but also several other TaskItems. Specifically, the TaskRequestItem, TaskRequestAcceptItem, TaskRequestDeclineItem, and the TaskRequestUpdateItem. So now we know we can create the TaskItem - no problem! The same techniques that we used to create the TaskItem reused again to create the other Task items. Or so I hope! First we need to understand when these Task Items come into play. The easiest way to do this is to fire up Outlook2k and create a new Task. Only this time we will use the "Assign Task" button
Pushing the "Assign Task" button on the toolbar changes lots in Outlook. The simple little button changes lots of information. Now, our task is not really a task anymore but a Message with a Task attached. To be sure, we really need to pull out the getTaskProperties.asp function again. By saving this Task assigned message without sending it will allow us to evaluate its hardcore properties to our original. Without going into too much detail, at this point, what you see is what you get. The addition of a To: and CC: means the Task needs to be able to send the message. In CDO, this is no different to adding a Recipient to the message. Beside the Recipient the normal work done to forward a Message is also applied: a subject prefix and suffix. That's it, great! If we send the Task assigned message now from the Administrator's mailbox and run to the other recipient mailbox like mine, I should have message waiting in my Inbox. Inbox? What is it doing in my Inbox? Again, to really check what is happening, we can modify the gettaskproperties.asp code to open my mailbox and Inbox to check out the properties of the waiting task. "It's a TaskRequestItem." I can tell this by the Message.Type Field which is set to IPM.TaskRequest - Very exciting! But wait…there is also a copy of this exact message sitting in my Task folder as well! So, the TaskRequest comes into the Inbox, then something makes a copy of it and drops the copy in the Task folder. I need to evaluate the new TaskItem that was copied to my Task folder. Again, changing the gettaskproperties.asp code to point this time to my Task folder. In the interface of Outlook2k, we see the exact same message both in the Inbox and Task folder. So, if they were the same message then their Field values and ids would be same? One would think so - but no! The TaskRequest that is sitting in my Inbox is really only a message carrier with some basic Outlook properties. It also has a CdoEmbeddedMessage attachment that must be our original sent TaskItem. A rule of some kind must be set up in the Inbox to copy the attachment of the TaskRequest to the Task folder. The Task folder has our original TaskItem with some extra properties that we'll come to later. The TaskRequest is now sent back to the original mailbox: Administrator. The TaskRequest is also no more. It is gone from my Inbox. A quick check of the properties of the TaskItem in my folder is needed. Moving to my original account yields to be a waste of time since nothing has been changed in that TaskItem. On to our first account (Administrator) where a new "IPM.TaskRequest.Accept" or TaskRequestAcceptItem is waiting. We could also assume that if we pressed, "Decline" that an IPM.TaskRequest.Decline or TaskRequestDeclineItem would have been sent instead. A quick check to view its properties indicates nothing more than a basic message with some extra Outlook defined properties. Almost the same as the original TaskRequest that sent out except this one does not have an Attachment. After reading it, the TaskRequestAcceptItem is gone. So that almost completes our round trip around Outlook2K's workgroup functionality: except the update capabilities. Ah! I knew there would be something else to make things more complicated. If we make changes to the sent and accepted TaskItem in my Task folder, such as % complete, increasing it to 80% complete, another message is sent and sits in the Assignee's Inbox (Administrator). We can only guess what type of a message it is. Does it have an attachment as well? That was the Easy PartWell, life is not getting easier for me. What is needed now? Well, since we cannot guarantee that all the users of the WebProject Application will have Outlook, we need to generate the same functionality as above but only from the web. I think I need something stronger than coffee for that meeting! Another problem arises; it should be possible to have the WebProject Tasks sitting in a Pubic Folder viewable by all project members. In Outlook, the assignment of Tasks exists only for local Task folders in the Private Information Store or PST, and not in public folders. Ah! But, we need to stay in the local Tasks folders to get any of the assignment benefits of Outlook2k plus we also need it for the integration benefits with Outlook2k as well. We really need to let everyone work locally and then manage an automatic assignment to the required public folder, or folder of choice for that matter. Mikael points out another problem - the Outlook assignment capabilities are only updateable if one recipient is chosen. My last Ah! What to DO?To be reveled at the conference to keep the suspense. A NewRendererWhy? The word "new" says it all! The design of the WebProject is heavily based on the client interface and that means huge amounts of DHTML coding and lots of styling. Most of this can be done with the normal CDOhtml.dll or CDO Renderer shipped with Microsoft's Exchange Server. However, it would take me twice as long as it did for me to write a renderer that was similar in functionality and in use, but has the much needed flexibility. It's all ASP. Another key reason is that the CDOhtml.dll is only shipped with MS Exchange Server for OWA and not with the normal CDO distributable. This could be a problem since it forces everyone who would use WebProject to use an Exchange Server - although it's not likely that this would be a problem. The design principles are almost exactly the same as working with the normal render. The NewRenderer has Views, Formats and Patterns which are functions just like the main one (cdohtml.dll). The main difference is that I have added the capabilities of executing Functions either based on Formats or whenever you need them - this means flexibility. Another plus is the capability to define as many %value% substitution tokens as needed. The major difference is that you get to define what the substitution tokens will do: %function% could mean any function you want executed based on whatever fields, values, or other definitions you want to include. Keeping what is in the NewRenderer aliveThe biggest obstacle in producing the same functionality as the "old" renderer is to maintain the defined Patterns, Formats, Columns, and Views. The nicest design for the NewRenderer would have been with VBScript 5's new class functionality, but the fact that its persistence is limited to only a single web page makes it completely useless for what is needed for the NewRenderer. We could always wrap a DLL to hold the needed values added to the NewRenderer in VB Classes but that again would though send us back to the current limitation of the "old" Renderer (CDO Renderer). The last and certainly easiest option is to use the Dictionary object. Using the Dictionary object, the NewRenderer can have page, session, or even application persistency. Flexible! Great! Even better is the easy pattern-matching done by the VBScript Match object, which is very similar to Wildmat format used in the CDO Renderer for its Patterns. The true test is getting it all to work together. WebProject Task TableOld RendererIt is not fair for me to cast aside the CDO or "old" renderer without an initial attempt at displaying the Task Table of Project98 for the ultimate comparison. The judging on this event might be a little biased, so, I created a new view with the old Render and produced this:
The CDORenderer's attempt at producing the Project98 TaskTable in getTaskTable.asp Of course we could have worked harder on this layout but we knew that eventually the CDO Renderer would eat too much of our time for the results we would get. So, I spent the time once rewriting a NewRenderer so it could be as flexible as needed but still provide the same functionality for rendering as the old renderer. The NewRenderer in Action
The NewRenderer in action in the getTaskTable2.asp to compare with Project98. The functionality of the NewRenderer is the same as the CDO Renderer. The code necessary to display the above Task Folder Table and create the view is very similar but yields very different results between the renderers. … 'Add formats and Patterns AddFormat CdoPR_MESSAGE_CLASS AddPattern CdoPR_MESSAGE_CLASS, "IPM.Task*", "<a href=""/exchange/forms/%classpath%read.asp""><img src=images/task.gif border=0></a>" 'Add View and begin adding Columns AddView "TaskView" ' format is like this: ViewName, HeaderName, PropID, RenderValue, ViewIndexPos AddColumn "TaskView", "", "LinkIndex", "<td class=tdIndex id=""LinkIndex%taskrowid%"">" AddColumn "TaskView", "Task Name", CdoPR_SUBJECT_W, bstrTableTD & "%value%" & bstrTableTDC, 100, 2 AddColumn "TaskView", "Duration", "GetDuration(""d""|%obj%(&H80E00040)|%obj%(&H80080040))", "<td class=tdClass onClick=""getListValue()"">%value%" & bstrTableTDC, 10, 3 … 'Actually Renders the Table RenderView "TaskView", objTasks, True GetTaskTable2.asp – code showing how similar it is to add a Format, Pattern with a View and Column but with huge differences in results. The addition of a Format or what is similar to the CDO Renderer's Format is the AddFormat (propertyID or propertyName). With the Format now defined we can specify how we want to render the specific property or more importantly, what function should run if the property value is for example "XXX". At this point we are can use almost the exact same wildcard formats as the CDO Renderer. The pattern matching is doing in the NewRenderer by VBScript's Match object. When we have added or whenever we like to add Patterns or Formats, we create our view called "TaskView". Not very original, granted! The AddView is almost self-explanatory. It creates an addition into the objViews Dictionary with another specific Dictionary object to hold the Columns information. The AddColumn function takes the View name that we want to add the Column to, the Header value of the Column if dealing with tables, the MessageField.ID or Name to renderer in the Column, how we want the Column to be rendered, and then the index position in the Columns collection. Zero being the start and any number higher than the collection being the end. The Column index capabilities are the same as in the CDO Renderer. I Don't See Anything Different?At first glance there isn't that much different - that's the point! Look closer to the LinkIndex column and you will see %taskrowid%. This is a custom substitution token that I have customized to take another custom defined property on the message and replace it where the token value is. This is tough to do with the "old" renderer. Looking a little closer at the same AddColumn call, we can see that we can actually on a per column basis define the table's <td> header. This is not so important in dealing with tables but it is crucial when dealing with dhtml, xml, or anything else besides tables. The last AddColumn, for the "Duration" column looks a little more complex and it is since we are actually running a function called GetDuration based on two property ids of the TaskItem. This is tough to do with the "old" renderer. Gantt ChartingAny project management application wouldn't be complete without the capability to display the task history in a graphical form. Once again, the Gantt charting tool is used simply because it is most commonly applied. Oh, and it's good too! The Gantt chart part of the GUI is (almost) entirely built up by using DHTML and the DOM to manipulate the objects. Initial TryThe initial design of the Gantt chart provided nothing but pain and agony. In too many ways, I (Mikael) was thinking it was too complex to really get a working GUI at the end. The main issues here are mixing a designer-friendly hierarchy like DHTML with the more dominant-mistress-like DOM/JavaScript model. I mean, just how many properties can really be used to describe an element's position on the screen? And which of these can be written to or read from? At a moment I even tried to manipulate object by just adding in new ones, to overwrite the existing elements. You know, lack of sleep can really mess up someone's brain. Final ResultsComing to my senses, I realized that if I need to manipulate objects, then that is what I'm going to do. An exaggerated use of DHTML was fixed up by removing big chunks, and instead turning to the harder-to-work, but definitely better working DOM/JavaScript combo. With Task boxes made up of background images and DIV elements (yes, the images are there for a reason) and links consisting of multiple-image DIV elements we finally got things working! (The images are withheld for the presentation only! To keep the suspense!) Linking to Task TableHaving a Gantt chart to display the tasks is nice and all, but unless it somehow works together with the TaskTable it is really no good. Linking the Gantt chart to the TaskTable is more than a matter of getting the right box on the matching row. One also must see to it that any changes made to one of the structures are replicated on the other one as well. In the midst of all GUI programming, we cannot forget that it is, after all, data that we are handling, so of course we also need to prepare the data to be flushed back to the server. Linking the TaskTable and Gantt to ServerThe pieces are coming together. It is possibly looking like we might pull off the WebProject after all. The TaskTable and Gantt chart are working in synch but also they look good. Mikael and I schedule another meeting to tackle our last, and hopefully easy task of how to handle changes done on the clients to the server. Following that means we must also deal with the Server changes back to the client. Mikael notes trouble at once: our normal café is closed! Troubles In the ChangesWhen dealing with client and server collaborative applications we are relatively lucky since the wonderful designs of MS Exchange Server have Update Sequence Numbers (USN) and other wonderful tracking capabilities thus eliminating some of our huge head aches at this stage if we had designed the WebProject application around a plain database. The obvious key problem is once we render the task and project information to the client browser and then they play with the data. The number one question is when do we send the changes back to the server? After each property change? After all property changes? Let them work offline with the data? Etc. There are also obvious times when to use any of the above. Since the WebProject is designed to run via the Web, it means we can't depend on lots of quick conversation between the client and the server. Thus, eliminating the individual property saving. All Changes @ Once PleaseSince we need to let the Client browser play with the data, we should keep track of data changes on the client end. Any changes done are held until the client presses the save button or we remind them before leaving the web site (IE's onbeforeunload event). The persistence of the Client changes is stored in a JavaScript class called clsChanges. The class holds the Message ID or TaskItem ID that was changed plus the Field name and value. Each change checks for an existing Message ID and if so adds to the already existing class. This means that we only need to use the CDO Session object's getMessage method on the server, and update the Fields specified. Simple, but effective. Better yet, we can provide an auto save function like in MS Word if the Client wants it. In order to do that, we need to provide an asynchronous method of updating the changes on the client to the server and then back again. The only method today that I know that comes close is Remote Scripting. Remote ScriptingActually, our design model or project model for the WebProject has always had MS Remote Scripting in mind. Its split Server and Client centric design leads to the Server work I did, and the Client work Mikael did. The merging of the two spells only Remote Scripting. Remote Scripting is one of the most under-rated, simple pieces of code written today. It is crucial in system designs like WebProject from the actual design point of view, to the asynchronous saves and dynamic updates to the TaskTable and Gantt chart. Just Testing Anyone? Testing Anyone? Testing?The absolute last meeting at the java shop to do the testing! Our normal professional release of the WebProject would require at least one 1000 employee company to take into their wings and be the guinea pigs. The coding shown and done here has been greatly simplified by eliminating almost all the normal error handling for a web based application of its possible size. Error Handling Seminars? Anyone? Presentation Slides - Late?This is always the hardest part but also leads to the most fun. The Presentation! See you there! The Show - Late?No it's the Late Show! |
|
|
|
|
|
| |
|
Email TopXML
|
|
Front Page Daily Stuff TopXML Forum XML blogs XML Newsgroups BizTalk Biztalk Utilities Biztalk Utilities Tutorial B2B SAP XML Microsoft .NET Dotnet System XML Soapformatter SQLXML XMLserializer XQuery PHP PHP SimpleXML PHP XML Dom PHP XML RPC PHP XSLT Java Java Java XML Xalan Microsoft ASP ASP Schemas XML SQL Server XML XMLDom XSL XSL Tutorial XSLT Stylesheets General Javascript CSS XHTML WAP |