SQL Server SP2 quirks
Wednesday, February 28, 2007 6:35:40 PM (GMT Standard Time, UTC+00:00)
Ok, as sad as it sounds I've been looking forward to the latest SQL Server service pack since I heard about some of the issues it fixed as there was two "glitches" in particular that seriously bugged me, namely:
- Loading a SQL file into the query editor on a live connection would ask you to log in again -this bugged the hell out of me because I have a number of routines saved on my disk as SQL files that manage client's servers and so I don't always have the password to easily hand which would just delay development (ok all be it by 30seconds or so but that's not the point)
- It would never remember my password in the initial splash screen, again see why above.
Anyway, it's great to see that these two points were fixed as well as a load of other issues but I couldn't help but chuckle when I saw the new context menus, I don't know about you but when editing this table I didn't know which one I needed to choose:

FWIW you need "Design" to open the design view and "Edit" to generate a SQL CREATE Script. Genius!
Employing someone 101
Tuesday, February 27, 2007 9:22:48 AM (GMT Standard Time, UTC+00:00)
Stacey recently wrote an excellent brief on the pro's and con's of employing someone however this morning when I came to post it online I realised that it was deleted when I formatted the laptop for a system demonstration last week. Luckily however I have a hard copy so I'll have it re-typed and put online ASAP.
In the meantime however I shall reflect with this classic (which I’m looking to implement in The Site Doctor ASAP)…
Attire
It is advised that you come to work dressed according to your salary. If we see you wearing Prada sneakers and carrying a Gucci bag, we assume you are doing well financially and therefore you do not need a raise.
If you dress poorly, you need to learn to manage your money better, and therefore you do not need a raise. If you dress in-between, you are right where you need to be and therefore you do not need a raise.
Personal Days
Each employee will receive 104 personal days a year. They are called Saturday and Sunday.
Lunch Break
Skinny people get 30 minutes for lunch as they need to eat more so that they can look healthy.
Normal size people get 15 minutes for lunch to g et a balanced meal to maintain their average figure.
Sick Days
We will no longer accept a doctor statement as proof of sickness. If you are able to go to the doctor, you are able to come to work.
Restroom Use
Entirely too much time is being spent in the restroom. There is now a strict 3-minute time limit in the stalls. At the end of three minutes, an alarm will sound, the toilet paper roll will retract, the stall door will open and a picture will be taken. After your second offence, your picture will be posted on the company bulletin board under the "Chronic Offenders" category.
Surgery
As long as you are an employee here, you need all your organs. You should not consider removing anything. We hired you intact. To have something removed constitutes a breach of employment.
Thank you for your loyalty to our company. We are here to provide a positive employment experience. Therefore, all questions, comments, concerns, complaints, frustrations, irritations, aggravations, insinuations, allegations, accusations, contemplation, consternation and input should be directed to the State Unemployment Offices.
DasBlog RSS Feed Macro
Saturday, February 24, 2007 2:39:04 PM (GMT Standard Time, UTC+00:00)
As part of my blog’s re-design I wanted to integrate my statistics from Last.FM which monitors what music you’re listening to and generates a stack of statistics about your listening habit (see About Last FM for more information).
Anyways, I started writing my own RSS macro when I came across one already developed by John Forsythe (http://www.jforsythe.com/) which did pretty much exactly what I was planning on developing, the only difference though was that his was hard-coded to preset node names whereas I was planning on using an XSL file to format mine to offer maximum flexibility in the long run so I updated his with the use of reflector (thanks to John Forsythe though!!).
There are a couple of difference to note with this code and John Forsythe's:
- The RSS retrieval is no longer handled by an external library -in this instance I wanted to keep this as simple and stand-alone as possible.
- There is no max item count at present -this is mainly because I didn't need it for the Last.FM Feed, I may alter that later.
Source code for a dasBlog XSL based RSS reader
using System;
using System.IO;
using System.Security.Cryptography;
using System.Diagnostics;
using System.Text;
using System.Web;
using System.Web.UI;

using newtelligence.DasBlog.Runtime;
using newtelligence.DasBlog.Web.Core;

namespace TSDMacros
{...}

{
public class TheSiteDoctor
{...}

{
protected SharedBasePage requestPage;
protected Entry currentEntry;

public TheSiteDoctor(SharedBasePage page, Entry entry)
{...}

{

requestPage = page;

currentEntry = entry;

}

/// <summary>
/// A dasBlog macro to retrieve an RSS feed and apply XSL to
/// it before caching it for x minutes
/// </summary>
/// <param name="xslVPath">The virtual path of the XSL file</param>
/// <param name="rssPath">The RSS feed URL</param>
/// <param name="minutesToCache">Number of minutes to cache the file for</param>
/// <param name="debugMode">Output the debug information</param>
/// <returns>A control that can be inserted into a dasBlog template</returns>
public virtual Control GetRSS(
string xslVPath,
string rssPath,
int minutesToCache,
bool debugMode)
{...}

{
string cacheVDir =
"./content/getrsscache/";
string cachedFileLoc =
String.Empty;

StringBuilder output =
new StringBuilder();

bool writeToCache =
false;
bool cacheExpired =
false;
bool cacheExists =
false;
Debug output
#region Debug output
if (debugMode)
{...}

{

output.Append(
"<strong><start debug></strong><hr />\r\n");

output.AppendFormat(
"<i>RssPath</i>: {0}<br />\r\n", rssPath);

output.AppendFormat(
"<i>minutesToCache</i>: {0}<br />\r\n", minutesToCache);

output.AppendFormat(
"<i>CacheStorageFolder</i>: {0}<br />\r\n", cacheVDir);

output.Append(
"<hr />\r\n");

}
#endregion
Check whether we need to cache or not
#region Check whether we need to cache or not
if (minutesToCache >
0)
{...}

{

writeToCache =
true;
//Find the cache directory
string cacheDir =
HttpContext.Current.Server.MapPath(cacheVDir);
//Work out what the file would be called based on the RSS URL
cachedFileLoc = Path.Combine(cacheDir, HttpUtility.UrlEncode(TheSiteDoctor.GetMd5Sum(rssPath)) +
".cache");
Debug output
#region Debug output
if (debugMode)
{...}

{

output.AppendFormat(
"<i>cache file</i>: {0}\r\n", cachedFileLoc);

}
#endregion
if (!File.Exists(cachedFileLoc))
{...}

{

cacheExpired =
true;
Debug output
#region Debug output
if (debugMode)
{...}

{

output.Append(
"<i>cache age</i>: no file exists<br />");

}
#endregion
}
else
{...}

{

FileInfo info1 =
new FileInfo(cachedFileLoc);

TimeSpan span1 = (TimeSpan)(DateTime.Now - info1.LastWriteTime);
if (span1.TotalMinutes > minutesToCache)
{...}

{

cacheExists =
true;

cacheExpired =
true;

}
Debug output
#region Debug output
if (debugMode)
{...}

{

output.AppendFormat(
"<i>cache age</i>: : {0} min old <br />\r\n", span1.TotalMinutes);

}
#endregion
}

}
else
{...}

{
Debug output
#region Debug output
if (debugMode)
{...}

{

output.Append(
"<strong>caching disabled - CacheStorageAgeLimit=0</strong><br /><span style=\"color:red; font-weight: bold;\">FYI: All requests to this page will cause a new server request to the RssPath</span><br />");

}
#endregion
cacheExpired =
true;

}

#endregion
Debug output
#region Debug output
if (debugMode)
{...}

{

output.Append(
"<hr />");

}
#endregion
//Check whether or not the cache has expired
if (cacheExpired)
{...}

{
Debug output
#region Debug output
if (cacheExists & debugMode)
{...}

{

output.Append(
"<strong>file cache is expired, getting a new copy right now</strong><br />");

}
else if (debugMode)
{...}

{

output.Append(
"<strong>no cache, getting file</strong><br />");

}
#endregion
//The cache has expired so retrieve a new copy
output.Append(TheSiteDoctor.delegateRss(xslVPath, rssPath,
0, writeToCache, cachedFileLoc, debugMode));

}
else
{...}

{
Debug output
#region Debug output
if (debugMode)
{...}

{

output.Append(
"<strong>cool, we got the file from cache</strong><br />");

}
#endregion
//The cache still exists and is valid
StreamReader reader1 = File.OpenText(cachedFileLoc);

output.Append(reader1.ReadToEnd());

reader1.Close();

}
Debug output
#region Debug output
if (debugMode)
{...}

{

output.Append(
"<hr /><strong><end debug></strong>");

}
#endregion

output.Append(
"\r\n<!-- \r\ndasBlog RSS feed produced using the macro from Tim Gaunt\r\nhttp://blogs.thesitedoctor.co.uk/tim/\r\n-->");

return new LiteralControl(output.ToString());

}

/// <summary>
/// RSS feed retrieval worker method. Retrieves the RSS feed
/// and applies the specified XSL document to it before caching
/// a copy to the disk -this should be called after it has been
/// established the cache is out of date.
/// </summary>
/// <param name="xslVPath">The virtual path of the XSL file</param>
/// <param name="rssPath">The RSS feed URL</param>
/// <param name="timeoutSeconds">Number of seconds before the request should timeout</param>
/// <param name="writeCache">Whether to cache a copy on disk</param>
/// <param name="xmlPath">Physical path of the XML file on the disk</param>
/// <param name="debugMode">Output the debug information</param>
/// <returns>An XML document as a string</returns>
private static string delegateRss(
string xslVPath,
string rssPath,
int timeoutSeconds,
bool writeCache,
string xmlPath,
bool debugMode)
{...}

{

StringBuilder output =
new StringBuilder();
bool errorThrown =
false;
string cacheVDir =
"./content/getrsscache/";
string xslPath =
HttpContext.Current.Server.MapPath(xslVPath);

try
{...}

{
//TODO: Replace this with a HttpRequest and timeout to ensure the visitor is not left waiting for the file to load
//Load the XML
System.Xml.XmlDocument xmlDoc =
new System.Xml.XmlDocument();

xmlDoc.Load(rssPath);

//Load the XSL
System.Xml.Xsl.XslTransform xslDoc =
new System.Xml.Xsl.XslTransform();

xslDoc.Load(xslPath);

StringBuilder sb =
new StringBuilder();

StringWriter sw =
new StringWriter(sb);

//Apply the XSL to the XML document
xslDoc.Transform(xmlDoc,
null, sw);

//Append the resulting code to the output file
output.Append(sb.ToString());

}
catch (
Exception ex)
{...}

{

errorThrown =
true;
Debug output
#region Debug output
if (debugMode)
{...}

{
//Log the exception to the dasBlog exception handler
ErrorTrace.Trace(TraceLevel.Error, ex);

output.AppendFormat(
"<ul style=\"\"><li><strong>RSS request failed :(</strong> <br />{0}</li></ul>", ex.ToString());

}
#endregion
}

//Save a cache of the returned RSS feed if no errors occured
if (writeCache & !errorThrown)
{...}

{
//Find the cache's storage directory
DirectoryInfo dir =
new DirectoryInfo(
HttpContext.Current.Server.MapPath(cacheVDir));
//Check it exists
if (!dir.Exists)
{...}

{

dir.Create();
Debug output
#region Debug output
if (debugMode)
{...}

{

output.AppendFormat(
"<strong>just created the directory:</strong> {0}<br />",
HttpContext.Current.Server.MapPath(cacheVDir));

}
#endregion
}
//Create the file
StreamWriter writer1 = File.CreateText(xmlPath);

writer1.Write(output);

writer1.Flush();

writer1.Close();
Debug output
#region Debug output
if (debugMode)
{...}

{

output.Append(
"<strong>just wrote the new cache file</strong><br />");

}
#endregion
}

return output.ToString();

}

/// <summary>
/// Worker method to identify the MD5 checksum of a string
/// in this instance used to ensure the RSS file isn't already
/// cached (based on the URL supplied)
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string GetMd5Sum(
string str)
{...}

{

Encoder encoder1 = Encoding.Unicode.GetEncoder();
byte[] buffer1 =
new byte[str.Length *
2];

encoder1.GetBytes(str.ToCharArray(),
0, str.Length, buffer1,
0,
true);
byte[] buffer2 =
new MD5CryptoServiceProvider().ComputeHash(buffer1);

StringBuilder builder1 =
new StringBuilder();
for (
int minsToCache =
0; minsToCache < buffer2.Length; minsToCache++)
{...}

{

builder1.Append(buffer2[minsToCache].ToString(
"X2"));

}
return builder1.ToString();

}


}

}
To use it on the blog template

<% GetRSS("LastFM.xsl", "http://ws.audioscrobbler.com/1.0/user/timgaunt/recenttracks.xml", 25, false)|tsd %>
This is a pretty crude way of doing it IMHO because the XSL transforms the stream directly, eventually I’ll update the code so it includes a timeout (as John’s did) and having seen the performance implications on my blog, make sure the request is made asynchronously.
FWIW I have set my cache value to 25minutes, I did have it as 1min for fun but it killed the blog, why have I set it to 25mins? Well, most of my tracks I would think are 2-3minutes long, as I list 10 tracks at a time that’s 20-30minutes listening time so it’ll still keep a fairly accurate overview of my tracks without having massive performance issues on my blog :)
Incase you don't want to or know how to create this macro as a DLL I have created it for you :)
dasBlog RSS feed macro" onclick="javascript:urchinTracker('/download/zip/TSDMacros_v1_23-02-07');" href="/tim/files/TSDMacros_v1_23-02-07.zip">Download the complete dasBlog RSS feed macro (4KB - MD5 Hash: e3d7d6320109fd07259e8d246b754f13)
Firefox class name and space idiosyncrasy
Friday, February 23, 2007 6:20:17 AM (GMT Standard Time, UTC+00:00)
We’re currently reworking www.florame.co.uk to improve it’s search engine ranking from virtually non-existent to (hopefully) first page for various inflections of organic aromatherapy, organic essential oils and all sorts of other aromatherapy products.
Despite the on-going debate on whether search engine crawlers prefer pretty XHTML or not, I still believe strongly that having your site’s content as the dominant code on every page MUST be better than having a plethora of tags (aka tag soup) but that’s for another post. So, with my feelings on XHTML (or at least neat HTML) in mind one of our recommendations was to re-work the site’s code –most importantly with the removal of the JavaScript menu at the top which is seriously impeding the site’s ranking. I decided we should opt for a form of CSS menu and those in the know, know there are only a few available options, for reference we used Suckerfish drop down menu.
The Suckerfish CSS drop down menu has been fairly heavily tested but I think I’ve found an issue with Firefox, basically the JavaScript marks up the LI with a hover class (sfhover) which then ensures it works as expected (this isn’t needed in IE7 or FF btw). The catch I’ve found however is that in FF1.5 (will test in 2.0) with scripts enabled, the menus are staying shown.
After a little head scratching the issue was narrowed down to this line of JavaScript:
this.className = this.className.replace(new RegExp(" sfhover\\b"), ""); Thanks to Firebug I was able to step through the code and check out the properties at every stage, in this instance I found that Firefox trims the leading and trailing space from the className so instead of it reading class="sfhover" as it is written, it had class="sfhover" which may be correct in some ways but obviously cocked up the regEx.
The solution is really rather simple, just change the space so it’s optional:
this.className = this.className.replace(new RegExp("\\s?sfhover\\b"), ""); It’s not an ideal fix but in the case of Florame organic aromatherapy it sorted the issue :) I’m going let Mozilla know about this as in some ways I think this is a glitch (though I can see their thinking that the developer didn’t mean to add the leading space) to see what they say. It wouldn't surprise me though if it was something I had done wrong!
For reference, the entire menu script now reads:
<script
type="text/javascript"><!--//--><
sfHover
= function() {...}
sfHover
= function() {
var sfEls
= document.getElementById
("nav").getElementsByTagName
("LI");
for (var i
=0; i
<sfEls
.length; i
++) {...}
for (var i=
0; i
<sfEls
.length; i
++) {

sfEls
[i
].onmouseover=function() {...}
sfEls[i
].onmouseover=function() {
this.className += " sfhover"; 
}

sfEls
[i
].onmouseout=function() {...}
sfEls[i
].onmouseout=function() {
this.className = this.className.replace(new RegExp("\\s?sfhover\\b"), ""); 
}

}

}
if (window.attachEvent
) window.attachEvent
("onload", sfHover
);
//--><!]]></script>
Update 7th May 2007: Darren over at Forma3 has come up with a new an improved version of the SuckerFish menu which includes a number of nice improvements and is well worth checking out: CSS drop down menus with persistent top level menu styling
Business start up advice downloadable PDF
Friday, February 16, 2007 6:54:35 AM (GMT Standard Time, UTC+00:00)
I’m still somewhat shocked at how well the series on business start up advice was received, I was expecting one or two hits on it but so far I’ve had over 1,000 visits to the article which is pretty shocking as this blog in its entirety was only getting that a year(ish)! I’ve also had some fantastic feedback which is very touching so those of you who have got in touch thanks!
Ok, following the posting of my recent business start up advice mini series I was asked by a number of people to post it as a PDF which I’ve finally managed to do. It’s rather long I’m afraid weighing in at around 26 pages so it should keep you busy giving me time to write the additional articles!
Download the PDF version of the complete business start up advice article here (27 printed pages including a 1 page feedback form - 189KB).
WowWee FlyTech DragonFly -awesome!
Friday, February 16, 2007 6:49:41 AM (GMT Standard Time, UTC+00:00)
I was planning on writing about this new gizmo that a friend of mine from the states Doug Setzer has got his hands on but then he asked me to and I didn’t really feel like it after that ;)

Just kidding, Doug’s bought a WowWee FlyTech DragonFly and I have to say it looks great fun. He’s written a review about the WowWee FlyTech DragonFly at www.mydragonfly.info which is worth checking out. As usual he’s pimped the site out with Google AdSense so before you start clicking on his adverts make sure you have a click on mine!!
Now I’ve got to plan a trip over to the states so I can have a play with his WowWee FlyTech DragonFly...
My first heads up (I think)
Wednesday, February 14, 2007 6:28:29 AM (GMT Standard Time, UTC+00:00)
Awesome, I had my first non-friend/family cross-blog comment the other day (very exciting stuff!) so I decided to check out the referring site and there, in all it's glory was a link to by blog! Seeing as one of the primary aims of this blog was to help others -even if it was just with simple information that other's may consider trivial- I'm over the moon! (Yep, it's the simple things in life that please me!)
So thanks to -=DeathToSpam=- whoever you are that made my day :)
If you don't believe me, check out the "Noteworthy Dev-Blogs" list at http://aspnyc.blogspot.com/ personally I don't feel I'm worthy enough to be listed next to Phil Winstanley and Scott Guthrie but it's certainly an honour!
If you've linked to my blog I'd love to hear from you and see what you're saying.
Importing/Referencing DLLs in Visual Studio
Tuesday, February 13, 2007 8:03:03 PM (GMT Standard Time, UTC+00:00)
A couple of people have got stuck on various lists/forums I’m on moving from ASP to ASP.Net and the differences there are, the one first major sticking point I had was referencing DLLs –so don’t worry you’re not the only one! So this is a really simple look at what you need to look at and how you reference DLLs –if you’ve ever added a DLL before you’ll probably find that this is too simplistic for you but read on anyway!
Firstly, referencing a DLL is basically a way of including someone else’s code within your project (or a common codebase that you re-use), this also includes controls, useful/common functions or just additional functionality such as Crystal reports.
Before you can use someone else’s code (i.e. Phil Whinstanley’s error reporting class) within your code you have to include a reference to the relevant DLL. The first thing I would do is create a folder somewhere that’s easily accessible to all machines that may need to reference the DLL i.e. “c:\Useful DLLs\”. Then, within this folder, I would create the following sub folders:
- c:\Useful DLLs\.Net 1.1
- c:\Useful DLLs\.Net 2.0
- c:\Useful DLLs\.Net 3.0
- c:\Useful DLLs\ASP.Net 1.1
- c:\Useful DLLs\ASP.Net 2.0
- c:\Useful DLLs\ASP.Net 3.0
This is something that I’ve only recently started doing after having multiple releases for the same DLL. For each DLL place a copy within the relevant folder.
Next, load your project within Visual Studio, right click the solution (this is the very top of the tree) and select “Add Reference":
A window will then popup that looks something like this:
Depending on what sort of reference this is, the majority of the time I would expect you’ll be needing to use the “Browse” tab –this allows you to navigate the FSO and find the DLL to reference (which should be somewhere in c:\useful dlls\). Once you’ve found it select the DLL and click Add.
Your DLL is now referenced and you should be able to start using it straight away. Depending on what you need to do with it you’ll also need to add Page and/or Codebehind imports. To check that it has imported correctly, in Visual Studio 2003 you should be able to see it in the references folder or in Visual Studio 2005 you will need to click into the "Class View" tab of the Solution explorer:
How do I identify the namespace?
I had someone ask me a while ago why the his code was throwing a compilation error, it turned out that although he had named the DLL MyDLL, the namespaces within the DLL he wanted to reference was MyNamesapce so how can you identify the namespace?
The easiest way to do this is to use something called the Object Explorer, this should list all the referenced DLLs for a given project and allow you to navigate the namespaces, classes and objects within the class. To open the Object Explorer click on the View menu and then “Object Explorer” within the “Windows” menu. Navigating the DLL is easy, you can either search through it using the search box at the top or alternatively navigate using the object tree.
The best way to work out what declaration you need to add is to locate the object, method or control you plan on using either using the tree navigation or searching, then selecting it. Once selected you will notice the bottom pane of the Object Explorer will change and the namespace will be listed, this is what you need to add as your reference. If you need to enter the assembly name, you can identify this easily as it’s the name given to the top node of the tree –this should have a little grey icon next to it.
If the DLL is adding a control to the page
You’ll need to reference the namespace at the top of the page like this:
<%@ Register TagPrefix="TSD" Namespace="TheSiteDoctor.WebControls" Assembly="TheSiteDoctor" %> You can use whatever prefix you like for the control, I tend to keep it between 2 and 4 characters in length for ease i.e. “TSD” but that’s up-to-you. Adding the control is done in the same way you add the standard controls:
<TSD:SuggestionTypeRadioButtonList runat="server" ID="radCategories" CssClass="inputRadio" ValidationGroup="suggestion" /> You’re all set :)
If however this is a control set that you plan on re-using throughout the application I would opt to add a reference within the web.config, this means you don’t need to repeatidly add the reference for each page. To do this you’ll need to add the following to your web.config file:
<system.web>
<pages validateRequest="false">
<controls>
<add tagPrefix="TSD" namespace="TheSiteDoctor.WebControls" assembly="TheSiteDoctor" />
</controls>
</pages>
</system.web> If the DLL is adding functionality to the codebehind or you want to use the control within the codebehind
If you want to use the control or add the control to the page dynamically you will need to include a reference to the namespace within the codebehind –in the same way you do the System namespaces. This is really simple, at the top of the page you should see a few “using” statements or in VB “Imports”, you’ll just need to add the referenced DLLs namespace below (or above –or- in the middle!) of these others, as long as it’s with the other statements you’ll be fine. You can then reference the various methods and properties of the control.
using TheSiteDoctor.WebControls;

public partial
class SuggestionsPage : System.Web.UI.
Page
{...} 
{
protected void btnAddEntry_Click(
object sender,
EventArgs e)
{...} 
}
I hope that helps you getting started with this new way of importing common code, it’s fairly intuitive once you’ve done it once or twice, but those first few “Could not find xyz –Are you missing an Assembly or Reference” messages do drive you nuts ;)
Automatically delete old IIS log files
Saturday, February 10, 2007 4:23:10 PM (GMT Standard Time, UTC+00:00)
This is a really useful little VBS script that I’ve been meaning to post for a while now (along with a couple of other little applications I’ve written for log file analysis). I don’t think I wrote this script but at the same time can’t recall where it came from.
It basically traverses the FSO finding files with the designated extension and assuming the match the standard IIS date format, checks whether they’re older than x days, if they are deletes them. Running it is simple, place somewhere obvious on the server and just double click it. Alternatively if you want to read the output, run it from CMD. For safety’s sake, the first time you run it I would leave it just printing out the files that will be deleted.
Personally I don’t schedule this script as although automation is great, I’ll probably have it delete the logs before I’ve had a chance to download them so what I tend to do is download the logs and then after that (or the next time I’m on RDC) I run it, I find that way I ensure I get all the log files i.e. if I go on holiday.
I’ve got two other applications that I’ll post shortly, one outputs the location of the log files for each domain name within IIS and the other combines the log files into one for analysis –it also takes the exported file/folder locations and names the combined log files with the domain’s name –saves a ton of time!
Download the VBS script as a ZIP file
Option Explicit
Dim intDaysOld
, strObjTopFolderPath
, strLogFIleSuffix
, ObjFS
, ObjTopFolder
Dim ObjDomainFolder
, ObjW3SvcFolder
, ObjSubFolder
, ObjLogFile
, ObjFile


intDaysOld
= 5 'Number of days to retain on the server
strObjTopFolderPath
= "" 'The location of your log files
strLogFIleSuffix
= ".log" 'The suffix of your log files
Set ObjFS
= CreateObject("Scripting.FileSystemObject")
Set ObjTopFolder
= ObjFS
.GetFolder(strObjTopFolderPath
)
For Each ObjDomainFolder in ObjTopFolder
.SubFolders

WScript
.Echo("Folder: " & ObjDomainFolder
.name
)
For Each ObjW3SvcFolder in ObjDomainFolder
.SubFolders

WScript
.Echo(" Folder: " & ObjW3SvcFolder
.name
)
Set ObjSubFolder
= ObjFS
.GetFolder(ObjW3SvcFolder
)
For each ObjLogFile in ObjSubFolder
.files
Set ObjFile
= ObjFS
.GetFile(ObjLogFile
)
If datediff
("d",ObjFile
.DateLastModified,Date()) > intDaysOld and lcase
(right
(ObjLogFile
,4))=strLogFIleSuffix then
'*****************************************************
'DON'T UNCOMMENT THIS UNTIL YOU KNOW IT WORKS PROPERLY!!!
WScript
.Echo(" Will delete " & ObjSubFolder
.name &
"\" & ObjFile
.name
)
'WScript.Echo(" Deleted " & ObjSubFolder.name & "\" & ObjFile.name)
'ObjFile.Delete
'*****************************************************
End If
Set ObjFile
= nothing
Next
Set ObjSubFolder
= nothing
Next
Next
Set ObjTopFolder
= nothing
Set ObjFS
= nothing