Saturday, 2004-04-24
Changing over...
I'm switching to UApplication's blogging app UBlog Reload this weekend (probably later tomorrow), so I thought I should give a heads-up that the URL of this blog (and the RSS feed) will be changing - they will be:
Once the changeover occurs, the old RSS URL will stop working, so please add the new one to your RSS aggregator now (although it won't work for now), and delete the old one when it dies. Incidentally, the new RSS feed will support carious parameters for recent posts, categories, etc. so that you can tailor the feed to suit you. It'll be self-explanatory once the blogs have been switched.
Finally, please note that because of the way my host implements subdomains, you will find that the current old URL will still work, but beware that some links and/or images could get mangled, so it's best that you access the new blog using the new URL. I'll be changing my sigs on various forums appropriately.
Thanks for bearing with me during this changeover.
Thursday, 2004-04-15
SQL Server output to XML
And here's another helpful function that I wrote yesterday...
Function ExecuteXMLQuery(ByRef Connection, ByRef SQL, ByVal RootNodeName)Simply pass an active SQL Server 2000 connection, an SQL statement like "SELECT Customer.ID, Customer.Name, Order.ID, Order.TotalQuantity, Order.TotalValue FROM tblCustomers AS Customer LEFT JOIN tblOrders AS Order ON Order.CustomerID = Customer.ID FOR XML AUTO, ELEMENTS", and a suitable root element name (e.g. "CustomerOrders") to get an XML document (as a text string) in response, ready for transforming, storing, sending, etc.
Dim Command, Stream
Set Command = Server.CreateObject("ADODB.Command")
Set Stream = Server.CreateObject("ADODB.Stream")
Stream.Type = 2 'adTypeText
Stream.Open
Command.ActiveConnection = Connection
Command.Dialect = "{5D531CB2-E6Ed-11D2-B252-00C04F681B71}"
Command.CommandText = "<" & RootNodeName & " xmlns:sql='urn:schemas-microsoft-com:xml-sql'>" & _
"<sql:query>" & _
SQL & _
</sql:query>" & _
"</" & RootNodeName & ">"
Command.Properties("Output Stream") = Stream
Command.Execute , , 1024 'adExecuteStreamStream.Position = 0
ExecuteXMLQuery = Stream.ReadText(-1)
Set Stream = Nothing
Set Command = Nothing
End Function
You might prefer to receive an XML DOM instead, in which case you can easily modify the above function and make the Command object populate a DOM object directly without using the Stream object.
In fact, now I've written that, I'm tempted to add an extra parameter to the function to produce either output as required! But I'm not going to post it... you can do it yourself! :p
Kludge alert!
I've got fed up with the font-size problems in these posts (largely due to poorly-formed HTML being generated by the blog app), so I've applied fixed-pixel font sizes as a workaround, and it seems to be fine. I'm almost definitely going to move to another blog app - UBlog from www.uapplication.com looks promising... is anyone running it? Otherwise I might go to TextPattern or MoveableType...
MSXML hints & tips
I'm about to sit down and write a dynamic XML-based navigation system for a new web app at work, and I thought I'd share a few handy tips about MSXML:
- Ensure that you're using the latest version of the Microsoft XML Parser (currently 4.0 SP2) unless there's a very good reason, since there were significant performance improvements between v2 and v3, and no doubt plenty more in v4 (although naturally in all cases you will only reap most of speed gains if you are aware of and use the new objects provided - see the documentation installed along with the parser). Naturally there are probably plenty of bugfixes too.
- You should also be explicitly specifying the version number when creating instances of the objects. This should always be done to ensure that an error is thrown if the correct version isn't installed, instead of dropping down to the highest version installed (e.g. 3), since doing so might cause odd problems which are rather hard to fathom until the penny drops and you realise that an older version is being used instead! For example:
Set XMLDOM = Server.CreateObject("MSXML2.DOMDocument.4.0") - Make use of Application-level DOM caching for huge speed gains (when frequently manipulating/transforming the same XML documents) by using a free-threaded DOM object. Here's a little something I've whipped up to make life easy (if you have any problems/question, simply post a comment):
Function GetCachedXMLDOM(FilePath)
If IsObject(Application(FilePath)) Then
'Load from cache
Set GetCachedXMLDOM = Application(FilePath)
Else
'Load from disk
Set GetCachedXMLDOM = Server.CreateObject("MSXML2.FreeThreadedDOMDocument.4.0")
GetCachedXMLDOM.Load(FilePath)
Application.Lock
Set Application(FilePath) = GetCachedXMLDOM
Application.UnLock
End If
End Function
'example usage follows...Set MenuXMLDOM = GetCachedXMLDOM(Server.MapPath("menu.xml"))
Response.Write Replace(Server.HTMLEncode(MenuXMLDOM.DocumentElement.XML),vbCRLF,"<br />")
- The same technique may be applied to XSLT (stylesheets) and XSD (schemas) by using the Msxml2.XSLTemplate.4.0 and Msxml2.XMLSchemaCache.4.0 objects, since they are both free-threaded (see the docs).
Tuesday, 2004-04-13
And another thing (or three)
I should have mentioned that you can (and *should*) read up on the ADODB.Stream object in your ADO documentation, which you can download as part of the ADO SDK, or view online in the MSDN library. I'm sure I saw a good article about Stream vs FSO a while ago, but can't track it down at present.
Similarly, instead of using manual procedures for CSV (or otherwise delimited) files, you should be using the JET OLEDB driver to manipulate it directly - for more on this topic, check out the recent MSDN article "Much ADO About Text Files".
And since I've just been looking at some of my old code which uses it, I thought I'd quickly draw attention to one of the Scripting.Dictionary's lesser-known properties - in a nutshell, it has a .CompareMode property, which lets you alter its case sensitivity in a couple of subtly different ways.
See the online MSDN documentation for the full lowdown. Because of a help authoring mess-up, this property doesn't *appear* to be in the downloadable windows scripting 5.6 documentation that I'm forever referring people to, but it actually is there - it's merely in the wrong place! Find it Script Runtime -- FileSystem Object -- Reference -- Properties instead (or search for it).
Finally, here's a good page about ASP/VBScript coding standards and best practices that I found by accident the other day. It's (pretty much) spot on in my book and a highly recommended read to those wishing to improve their technique.
Monday, 2004-04-05
Binary file streaming (aka "dynamic downloads")
Here's a binary file streaming function (and supporting code) that I wrote quite some time ago to make implementing dynamic download functionality almost effortless. I originally posted the first version of this code way back in 2002, in this thread @ SPF, and then it came up again in this one, by which time I'd refined it a little. I recommend that you read both threads to see the context that it is intended to be used in, as well as a few helpful comments from myself and others.
Incidentally, I use the ADO.Stream object instead of the FSO (FileSystem Object) because the latter is horribly inefficient and totally unsuitable for manipulation of binary data (although it can be done). ADO.Stream is highly optimized for fast IO, and will happily whizz through even *huge* files (i.e. tens or perhaps even hundreds of megabytes in size) with the lowest possible server load.
So anyway, here it is. I hope you find it useful. Let me know if you have any problems.
'Load a file from disk
Function LoadStream(FilePath)
Dim objStreamSet objStream = Server.CreateObject("ADODB.Stream")
objStream.Type = 1 'adTypeBinary=1
objStream.OpenobjStream.LoadFromFile FilePath
LoadStream = objStream.Read
objStream.Close
Set objStream = Nothing
End Function
'returns the MIME header type for a given extension
Function GetMIMEType(Extension)
dim Ext
Ext = UCase(Extension)
select case Ext'Common documents
case "TXT", "TEXT", "JS", "VBS", "ASP", "CGI", "PL", "NFO", "ME", "DTD"
sMIME = "text/plain"
case "HTM", "HTML", "HTA", "HTX", "MHT"
sMIME = "text/html"
case "CSV"
sMIME = "text/comma-separated-values"
case "JS"
sMIME = "text/javascript"
case "CSS"
sMIME = "text/css"
case "PDF"
sMIME = "application/pdf"
case "RTF"
sMIME = "application/rtf"
case "XML", "XSL", "XSLT"
sMIME = "text/xml"
case "WPD"
sMIME = "application/wordperfect"
case "WRI"
sMIME = "application/mswrite"
case "XLS", "XLS3", "XLS4", "XLS5", "XLW"
sMIME = "application/msexcel"
case "DOC"
sMIME = "application/msword"
case "PPT","PPS"
sMIME = "application/mspowerpoint"
'WAP/WML files
case "WML"
sMIME = "text/vnd.wap.wml"
case "WMLS"
sMIME = "text/vnd.wap.wmlscript"
case "WBMP"
sMIME = "image/vnd.wap.wbmp"
case "WMLC"
sMIME = "application/vnd.wap.wmlc"
case "WMLSC"
sMIME = "application/vnd.wap.wmlscriptc"
'Images
case "GIF"
sMIME = "image/gif"
case "JPG", "JPE", "JPEG"
sMIME = "image/jpeg"
case "PNG"
sMIME = "image/png"
case "BMP"
sMIME = "image/bmp"
case "TIF","TIFF"
sMIME = "image/tiff"
case "AI","EPS","PS"
sMIME = "application/postscript"
'Sound files
case "AU","SND"
sMIME = "audio/basic"
case "WAV"
sMIME = "audio/wav"
case "RA","RM","RAM"
sMIME = "audio/x-pn-realaudio"
case "MID","MIDI"
sMIME = "audio/x-midi"
case "MP3"
sMIME = "audio/mp3"
case "M3U"
sMIME = "audio/m3u"
'Video/Multimedia files
case "ASF"
sMIME = "video/x-ms-asf"
case "AVI"
sMIME = "video/avi"
case "MPG","MPEG"
sMIME = "video/mpeg"
case "QT","MOV","QTVR"
sMIME = "video/quicktime"
case "SWA"
sMIME = "application/x-director"
case "SWF"
sMIME = "application/x-shockwave-flash"'Compressed/archives
case "ZIP"
sMIME = "application/x-zip-compressed"
case "GZ"
sMIME = "application/x-gzip"
case "RAR"
sMIME = "application/x-rar-compressed"
'Miscellaneous
case "COM","EXE","DLL","OCX"
sMIME = "application/octet-stream"
'Unknown (send as binary stream)
case else
sMIME = "application/octet-stream"
end select
GetMimeType = sMIME
End Function
'Sends the specified file to the browser
sub SendStreamToBrowser(FileStream, FileName, ContentType, IsInline)
Dim FileExt, FileSize
'Disable error checking
on error resume next'Clear buffer
Response.Clear
FileExt = mid(FileExt, instrrev(FileName,".") + 1)
FileSize = Ubound(FileStream) + 1
'Add filename to header
Response.AddHeader "Connection", "keep-alive"
Response.AddHeader "Content-Length", FileSize
'Check if data should be delivered inline or not
If IsInline = True then
'Allow the browser to render the file inside a browser window (if it can)
Response.AddHeader "Content-Disposition","inline; filename=" & FileName
Else
'Force browser to save file
Response.AddHeader "Content-Disposition","attachment; filename=""" & FileName & """"
End If
'Get ContentType for download
select case ContentType
case false
'Generic binary ContentType and Charset
Response.ContentType = "application/octet-stream"
Response.Charset = "UTF-8"
case ""
'Find out what it should be
Response.ContentType = GetMIMEType(FileExt)
case else
'Use the ContentType that was passed
Response.ContentType = ContentType
end select'Send data to client
Response.BinaryWrite(FileStream)
Response.Flush
End Sub
Blogbits
American ATM machines... they inspire confidence! (Apparently was /.'d last month, but I only just noticed it)
And following on from an interesting thread about teaching yourself to program that was going on at Sitepoint forums last month, I found a great page which talks about what's wrong with SO MANY programming books out there.
When will I ever learn?!
I seem to keep making promises to post stuff but completely failing to because I never get round to it... and posting completely different stuff, like this very interesting blog post about the technology that powers Google. Found that at Simon Willison's excellent blog... who found it somewhere else... and so on... and so forth. Make sure you also read this related blog post mentioned in the comments.
I'm busy packing for a business trip to Germany at the moment, but I will try my damndest to catch up on my broken blog promises before I leave tomorrow morning. But that's not a promise! :p
Wednesday, 2004-03-31
Back in town
I arrived back home on Monday and started back at work today. I'll post some snaps and work through my to-do list (see previous post) as soon as I have a moment, so if you're waiting for more code, watch this space!
Saturday, 2004-03-27
Notes to self
Upon return to England:
1) Shower
2) Sleep off jetlag
3) Publish ASP HTTP class
4) Publish ASP debugging class
5) Republish ASP synamic download functions (originally posted here)
;-)
Friday, 2004-03-26
A quick update
I've been enjoying a nice relaxing holiday here in Florida, with the highlights being a day at Universal Islands Of Adventure and two day stay at the historic town of St Augustine, where the what we now know as the United States of America was actually founded (*not* Jacksonville, as is often claimed).
The plan had been to visit Universal Studios today, but unfortunately car problems have put paid to that. Nevermind, I'm going to soak up some rays in the garden instead...
Thursday, 2004-03-18
I'm off!
Just thought I'd mention that I'm off to Tampa Bay, Florida for a 10 day holiday (well-deserved, I think, because although I've had some fun on my business trips, I haven't had a proper holiday for about 2 years), so I won't be around on the forums much (if at all), and I probably won't be posting anything here...
Pleased to see that the weather's looking pretty good... :D
Florida, here I come!
Wednesday, 2004-03-10
Radical new forum architecture & functionality?
Last week I started a thread at Sitepoint Forums about some ideas I had for a new forum architecture, making extensive use of metadata to avoid some problems which are common to large forums (like which forum a thread "belongs" in), to allow users to create dynamic views of the forum tailored to their requirements, and to greatly enhance the sophistication of forum searches when trying to locate a solution to a problem (or find an old post).
There has been a little discussion, but not as much as I'd hoped, so perhaps you might have a few comments to throw into the melting pot!? Has it been done already?
Code snippet - dispose of an object / array
It's always wise to manually dispose of objects and arrays that you use, since although ASP's garbage collection is much improved in ASP 3.0 (IIS 5), it is known to overlook some things, and hence lead to memory leaks.
Rather than typing the same old "Set objSomething = Nothing" every time, it's much easier to use a function/sub to do the job for you, and unsuprisingly I have one up my sleeve. It will automatically call .Close() on ADO objects, call .RemoveAll() on Dictionaries, and Erase arrays as appropriate - how many of you remember to do those every time?! Finally, it sets the variable to Empty, as if it was never used - not essential, but I like it!
Sub Kill(ByRef Obj)NOTE: It's also good to standardize your Sub calling syntax... I use the Call SubName(Param1, Param2, ...) syntax so that whenever I pass parameters to something I use brackets.
Select Case True
Case IsObject(Obj)
Select Case LCase(TypeName(Obj))
Case "recordset", "command", "stream", "connection"
'closeable ADO objects
If Obj.State <> 0 then
Obj.Close
End If
case "dictionary"
'remove all the pairs
Obj.RemoveAll
Case else
'something else so don't
'do anything special
End Select
Set Obj = Nothing
Case IsArray(Obj)
'clear the array
Erase Obj
Case Else
'do nothing at all
End Select
'Now revert it to an unitialized state
Obj = Empty
End Sub
Code snippet - output CSV from Recordset... a better way
Amusingly, I've just found that I've coded a CSV generating function before, and I did a better job the first time round!! lol
Here's how I *should* have done it the other day...!
Sub RecordsetToCSV(ByRef RS, ByVal CSVFilePath, ByVal IncludeFieldNames)Much better! :-)
Set objCSVFile = CreateObject("ADODB.Stream")
Call objCSVFile.OpenIf IncludeFieldNames Then
'string concatenation issues aren't a problem for a small string of field names
For Each Field In RS.Fields
If FieldNames = "" Then
FieldNames = Field.Name
Else
FieldNames = FieldNames & "," & Field.Name
End If
Next
FieldNames = FieldNames & vbCRLF
Call objCSVFile.WriteText(FieldNames, 1)
End If
Call objCSVFile.WriteText(FieldNames & RS.GetString(adClipString, , ",", vbCRLF, ""))
Call objCSVFile.SaveToFile(CSVFilePath, 2)
Set objCSVFile = Nothing
End Sub
More BlogWorks XML needed features
Blog post categories with view filtering and corresponding separate RSS feeds (in addition to the existing one). I rather like YoungPup's approach but would probably use small (16x16 pixel) icons (with the category description on a tooltip via the TITLE attribute) instead of the *nix-style list.
Code snippet - create a disconnected Recordset
Disconnected Recordsets are very handy for manipulating dynamically-populated tables of information (e.g. views of folders, with names, sizes, dates, times, etc) and so I have a handy little function for summoning one when needed:
Function CreateDisconnectedRecordset()
Dim RS
Set RS = Server.CreateObject("ADODB.Recordset")
RS.CursorLocation = adUseClient
Set RS.ActiveConnection = Nothing
RS.CursorType = adOpenStatic
RS.LockType = adLockBatchOptimistic
Set CreateDisconnectedRecordset = RS
End Function
Code snippet - output CSV from Recordset
Recently I desperately needed to examine some tables from an MSSQL database on a remote server (in Italy) that I couldn't access directly, and which didn't have Enterprise Manager (or any other useful tools) installed (so I couldn't get someone to extract them for me).
I came up with a VBScript Windows shell script to do the job, and thought that others might find my CSV file dumping function handy (you can use it as-is in an ASP script or shell script). Apologies for the lack of documentation (done in a hurry and I don't have time to comment it now), but I think it's fairly self explanatory anyway...
Function RenderValue(ByVal Value)You'll probably want to add your own error handling code, and you probably should modify the RenderValue function to handle strings containing commas (I didn't need to at the time)!
If IsNull(Value) Then
RenderValue = ""
Else
RenderValue = Value
End If
End Function
'Outputs a recordset to a CSV file
Sub SaveRecordSetAsCSV(ByRef objRS, ByVal CSVFilePath)
Set objCSVFile = CreateObject("ADODB.Stream")
Call objCSVFile.OpenIf Not objRS.EOF Then
DataArray = objRS.GetRows
XMax = objRS.Fields.Count - 1
YMax = UBound(DataArray, 2)
Else
XMax = objRS.Fields.Count - 1
YMax = 0
End If
For X = 0 To XMax - 1
Call objCSVFile.WriteText(objRS.Fields(X).Name & ",")
Next
Call objCSVFile.WriteText(objRS.Fields(XMax).Name, 1)If IsArray(DataArray) Then
For Y = 0 To YMax
For X = 0 To XMax - 1
Call objCSVFile.WriteText(RenderValue(DataArray(X, Y)) & ",")
Next
Call objCSVFile.WriteText(HandleNull(RenderValue(XMax, Y)), 1)
Next
End If
Call objCSVFile.SaveToFile(CSVFilePath, 2)Set objCSVFile = Nothing
End Sub
UPDATE: See post above for a better way.
Blog bug
There seems to be a problem (at least in IE 6, haven't had a chance to test in anything else) with the paragraph immediately following a <pre> element (which I've been using to post the code below)... I'll sort it out when I get a chance...
EDIT 1: it looks like there's problems *inside* <pre> elements too... hmm...
EDIT 2: In fact, I'm really not happy with the design of the site at all... it's virtually untouched from the default BlogWorks XML install, and for some unfathomable reason, all the pages currently have an embedded CSS stylesheet rather than linking to a shared external one.
<MentalNote>Really must give the site a redesign soon...</MentalNote>
A few words on enumerated constants
A particularly common error which comes up again and again (although happily on the decline) is people using enumerated constants (e.g. adOpenStatic) - something which is definitely to be encouraged (because of increased readability/maintainability, futureproofing if the value of the enumerated constant changes, etc.) - but failing to actually import the constants themselves into their scripts, which leads to all sorts of errors. This is entirely understandable, since this essential step is shamefully overlooked in the scripting documentation and most other relevant Microsoft reference material. When it *is* mentioned, usually only the antiquated method of including the ADOVBS.INC file is offered. However, there is a better way, which is to import the binary type library (a file containing information about the contents of a DLL, including enumerated constants) directly.
In your scripts (preferably in an include file that every script in your site will share, or in the GLOBAL.ASA) simply add the following lines *outside* of the script tags:
<!-- METADATA TYPE="TypeLib" NAME="Microsoft ActiveX Data Objects 2.5 Library"The above will import all the enumerated constants you need for using the ADO and ADOX objects (from ADO version 2.5), which include ADODB.Connection, ADODB.Recordset, ADODB.Stream, ADOX.Catalog, etc. The import of these values is much quicker (i.e. more efficient) than the traditional ADOVBS.INC method because the values are retrieved directly from the compiled binary typelib (rather than evaluating an ASP script) and also helps to avoid versioning problems. You will also find that only ADO has a readily available .INC file (which is out of date), so you would have to create your own for other libraries if you wanted to use that old approach.
UUID="{00000205-0000-0010-8000-00AA006D2EA4}" -->
<!-- METADATA TYPE="TypeLib" NAME="Microsoft ADO Ext. 2.5 for DDL and Security"
UUID="{00000600-0000-0010-8000-00AA006D2EA4}" -->
These typelib imports work by looking up the UUID number in the section of the Windows registry where all the typelibs are stored (HKCR\TypeLib) to find where the typelib file is located on disk. For more on finding these yourself (which you may well need to do), read this excellent tutorial.
Incidentally, if you use the FSO, you may find it helpful to import is the Microsoft Scripting Runtime ({420B2830-E718-11CF-893D-00A0C9054228}), so that you don't have to do this.
Code snippets - random integers and URL decoding
It's about time I posted some code (I keep promising people on forums, but somehow I never get round to it) so here's a few bits & bobs to start off with. I've got a huge code library to sift through, so I'll try to pick some things which will be helpful to a lot of people, as well as a few more interesting things here & there...
Firstly, generating random integer numbers within a specified range seems to crop up from time to time, so here's what I wrote a number of years ago. Notice the way I use the Select Case statement, which I think it far more elegant than the equivalent If...ElseIf...Else statements would be...
'Returns a random integer in the range specified (inclusive)Quite some time ago I noticed on MSDN that VBScript does actually have Escape() and UnEscape() functions, which rather oddly are not documented in the downloadable Windows Scripting 5.6 reference help (if you don't have this downloaded, installed, and in regular use when coding then shame on you!!). I then used the UnEscape() function to whip up a simple to decode URLs encoded with Server.URLEncode (Microsoft didn't see fit to provide one themselves):
Function RndInt(LowerBound, UpperBound)
Randomize()Select Case True
'rather pointless, but since it's possible...
Case UpperBound = LowerBound
RndInt = UpperBound
'swap bounds if they're the wrong way round
Case UpperBound < LowerBound
RndInt = Int((LowerBound - UpperBound + 1) * Rnd + UpperBound)
'just generate it as normal
Case Else
RndInt = Int((UpperBound - LowerBound + 1) * Rnd + LowerBound)
End Select
End Function
'Decodes a string encoded with Server.URLEncode()More to follow later... must do some work!
Function URLDecode(ByVal str)
str = UnEscape(str)
str = Replace(str,"+"," ")
str = Replace(str,"%2A","*")
str = Replace(str,"%40","@")
str = Replace(str,"%2D","-")
str = Replace(str,"%5F","_")
str = Replace(str,"%2B","+")
str = Replace(str,"%2E",".")
str = Replace(str,"%2F","/")
URLDecode = str
End Function
Sunday, 2004-03-07
Ok, so I lied!
Been back a week, no posts. I've been busy, ok?
I've just stumbled across "disc golf"! Apparently it's big in the States, but I'm totally flabbergasted - I honestly have never heard of it!! It sounds good fun though, and I'll try to give it a shot when I visit Florida at the end of the month. There's even a course here in London, so if I like it I might take it up! :-)
Saturday, 2004-02-28
Fundamental flaw...
I'm now back in the UK and will get busy on a few posts over the next couple of days, but for the moment I'm just going to comment on a fairly major (IMHO) bug that I've found in BlogWorks XML...
Compare http://marcustucker.com/blog/ and http://www.marcustucker.com/blog/... what do you notice? Well, I'll give you a handy hint... all the comments disappear on the main pages (but are present on the comments pages accessed via the respective links)!
I haven't had a chance to look at the source, but it would seem that the code responsible for this is using absolute (rather than relative) URLs in the comments-related storage and lookup.. which doesn't make much sense to me, and is something I'll definitely have a look at pronto...
Thursday, 2004-02-19
BlogWorks XML missing features
There are a few missing features in this software, of which the most serious is the total lack of an image upload facility, something I hadn't checked out before I left for my trip!
So far I have been going through the relatively laborious process of uploading to my homegrown CMS (that powers my main site), then grabbing the URLs (of the uploaded image and the thumbnail that my CMS automatically generates), building an HTML table manually with the necessary hyperlink and image code, and then finally being able to publish the post. This is plain silly, and I'll develop something to sort this out when I return!
Similarly, I think it would be handy (for me at least) to be able to see all the comments (and trackbacks) in one place, ordered reverse-chronologically, rather than having to click the "comments" link at the bottom of each post. Although I've seen all the comments that have been posted so far because I've noticed them, once I've got quite a few posts up, it will cease to be practical to check them all manually. There are also a few bugs in the post preview feature which I'll fix while I'm at it.
Do any of you use BlogWorks? If you have any requests for new features/fixes, I'd be happy to have a go at them too.
Naturally, I will get in touch with Rick (who's currently looking after BlogWorks XML) and offer my modifications for integration/public use (if he wants to do so).
Tuesday, 2004-02-17
Apologies
Two to make (not that anyone complained, but I feel a little guilty).
Firstly, for those expecting to tune in to a technical blog, I have committed the cardinal blogging sin of hijacking it for personal, non-technical stuff - I promise that once I return to England (in 10 days) I'll make up for it with some quality posts (perhaps even before then)... in the meantime, perhaps my input (and that of others) in these posts/threads might be of interest to some of you (for some reason, closing/deallocating/reusing objects seems to be a hot topic at the moment, and things aren't always as you might expect)...
Deallocating objects in VBScript (see here too)
Calling ByVal or ByRef?
Secondly, for those of you who ARE actually interested in finding out how my travels are going, I'm sorry haven't been posting very regularly, however this is largely because it's been damn near impossible to get an internet connection in hotels. However, now I've finally sorted out an Italian ISP I should be fine for the rest of my stay, although since it's going on the company tab I can't use it as much as I would like to!! lol
Some more eye candy from Como last week:
Since my last post I've had a great time in Venice thanks to Orsola (last photo, with me & two of her friends), who has been kind enough to show me around (helping me avoid the hordes of tourists and bad restaurants along the way). We've got quite a lot planned for next weekend (when I return to Venice), including my attending a concert on Friday evening, where she is performing as part of a Venetian gospel choir. Other anticipated highlights should include the glassmaking island of Murano, the Basilica (best avoided until next weekend because of the 2hr queues), and perhaps another few churches.
Venice - damn cold, but very beautiful:
I left Venice on Sunday evening, I've been staying in a beautiful hotel in Mira (just outside Venice) since then, and will be moving to a hotel in Padova tomorrow. Expect another update in a few days!
Incidentally, I'm about halfway through my business trip (that is after all why I'm here), and I must say that I'm delighted with the way the trip has gone from a that perspective! For those of you that don't know, I've been migrating from a web developer role to a business analyst, and this is my first real taste of how things will (hopefully) be.
It's been *very* satisfying to be the person doing the actual systems analysis from scratch for a change, rather than being the poor sod that's got to work with a half-planned half-specified system that someone else has come up with and deal with all the things have been overlooked and inevitably turn up at the 11th hour during development!!
Naturally I have no illusions that I've managed some sort of "perfect" analysis or achieved an ideal technical specification, and I'm realistic about there being changes between now and project completion, but I reckon I've got 99.9% of it nailed down based on the information I've been able to extract from each consultation session, and I've really enjoyed the challenge of working with new people and new systems to get the job done. It hasn't just been about the creature comforts of being on a business trip abroad!!
This bodes very well for the future, and I'm looking forward to carrying on with everything tomorrow (for the third of four consultations). Perhaps I've spoken too soon - I hope not, but I guess I'll just have to wait and see...
And finally, I've booked my 9-day holiday to Florida for the end of March, and I can't wait! Hurray!!
Saturday, 2004-02-14
Venezia
I've dropped into an Internet café here in Venice, but alas it's not possible to post any more snaps at the moment (nor can I print out the Word document in my email which contains my hotel reservation information)... perhaps tomorrow.
The carnival is in full swing AND it's Valentine's day too, so Piazza San Marco is overrun with tourists! I must admit that I was slightly depressed at the thought of being in the most romantic city in the world on this particular day (travelling and eating alone is depressing enough as it is), but fortunately I met a lovely Venetian girl last night who has been showing me around a few less touristy places this morning, and she's promised to show me more this evening & tomorrow... and next weekend when I return! So perhaps today might not turn out to be so bad after all - and I hope you enjoy your evening too!! ;)
Arrivederchi!
EDIT: corrected typo in title
Wednesday, 2004-02-11
Ciao amici!
Some of you may know that I'm out in Italy on a business trip at the moment...
My journey out (yesterday) was rather nice, with magnificent views of the Alps from the plane as I neared Milan, and a magical night-time scene at Lake Como as I arrived at my hotel. I went for an evening walk, and enjoyed a wonderful 5-course meal at a local trattoria (a small family run restaurant), before a few DVDs in bed (on the laptop) before dozing off.
The lake was beautiful this morning, and the hills around it are quite something too. There's a funicular railway, and since I'm done for the day, I'm going to take a trip up to the top - hopefully I'll be able to catch the sunset from the summit...
Here's a few choice snaps:
More tomorrow! Arrivederchi!
Monday, 2004-02-09
Top ten web application vulnerabilities
OWASP last month published their list of the top ten vulnerabilities in web applications. Now how many of us can honestly put our hand on our heart and say "Yes! I have given serious consideration to each of these issues and how they might affect my new web application. No worries there." ??
Not me, that's for sure. Don't get me wrong. I would *like* to! There's nothing I hate more than having to cut corners on a project... but if you've barely been given enough time to implement the basic functionality that the project requires before it goes live, it's nigh on impossible to find the time to add anything beyond the most basic secruity features, let alone devote the time and effort throughout the entire project development process to cover the full spectrum.
Of course, by using a modular library of code that I've built up over the years, I have plenty of data validation (server-side and client-side), password hashing, and other security-related snippets of code that I can pull out of a hat and drop straight into an app, but that should only be a starting point, not the end of the security measures!
I think the biggest problem is perception - the powers that be seem to think that web applications are a quick 'n' easy alternative to developing a "real" application! They think it's all RAD (man)!!
Well I've got news... web applications ARE "real" applications - all the same rules apply! But there is a very important difference... you're not just exposing the application to your intended user base... if it's published on the WWW then anyone can have a go at exploiting it... and what's the business cost going to be if someone causes mayhem? A damn sight more than it would take to get it right in the first place, I'll bet!!
Ok, rant over.
I'd like to see more discussion on security throughout the web development cycle, so I've proposed that a new forum is created at Sitepoint forums to this end. Security should not be an afterthought. It should be there right from the start.
Managing expectations
Any of you who know me from the forums will know that I religiously code everything myself from scratch, since 3rd party scripts are usually poorly written from a number of standpoints (good practice, efficiency, code neatness) and rarely live up to expectation.
However, I needed a blog in a hurry (I'm going on a 3-week business trip to Italy tomorrow) and so had to grab something to do the job off-the-peg. BlogWorks XML, a GPL'd classic ASP blog script abandoned by its original author - and now kept alive (but unsupported) by Rick Hurst - seemed to fit the bill the best, so I downloaded it and had it up and running within 5 minutes. In the brief time that I've spent playing with it, I've been pretty impressed with the user interface, perceived speed, and overall sophistication that it offers, although I'm yet to delve into the code itself to pass judgement on how it's put together! lol
Anyway, what spurned me to start this particular post was that I just noticed that there's no commenting facility, which is a bit of a shame. As soon as I return from my travels I'll add this feature, but until then, I invite you to submit your comments (if any) to me via email.
EDIT: I take it all back! It turns out that BlogWorks XML *does* support commenting! I've edited the text of the links at the bottom of each of these posts to make it more obvious... so comment away!
And so it began...
We've all got to start somewhere, so this is the first post of my new blog, which over time will hopefully come to be an interesting blend of my thoughts on web development (ASP / XHTML / JavaScript / MSSQL in particular), comments about various threads from the forums at Sitepoint and Codingforums, code snippets and other random bits & bobs.
Your own thoughts and comments are welcomed, so don't be shy to get in touch!!