Wednesday, May 30, 2007

SharePoint Recycle Bin

Can you believe it? SharePoint Windows SharePoint Services and SharePoint Portal Server 2003 do NOT come with a way to recover files deleted from the document libraries except restore the entire WSS or SPS site(s) depending how you backed up SharePoint. This requires an installation of SharePoint. Worse yet, this can be extremely time consuming. As it turns out Microsoft heard the cry and came up with a very nice solution. They felt it was needed so much that they published it via gotdotnet workspaces ( The solution is called Recycle Bin. There is even a very detailed installation guide. The installation is tedious, but straight forward and actually takes under 45 minutes from start to finish. The solution does have a way for end users to retrieve the deleted files, but this can be done using a web part or just have your SharePoint administrator get the file from the windows directory (this is where the files end up when they are deleted once you install this). There are some other solutions, but I think this is the best for the money (free) and it doesn't void any support with Microsoft since it doesn't modify the database directly. Here are some other options to look at though:

Tuesday, May 29, 2007

Resending sysmail emails

Oops, mail server went down you determine. Then you think to yourself, that is not good for my sysmail I use with SQL. You realize sysmail in SQL 2005 retries and you feel better. Oh, it gets worse sysmail gave up after so many tries. Users didn't receive the emails as expect. Below is a handy stored proc to send them out again using the information in the sysmail_log table. Warning: This stored procedure does NOT include all fields. It only include recipients, subject, body, and uses the original mail profile that was used to send the email. Things like attachments, bcc, and other items are not included, but could easily be added. The stored procedure below takes the MailItemID from the msdb.dbo.sysmail_mailItems table as a parameter. Calling this stored procedure will resend the email with the specified MailItemID. create proc ResendEmailItem(@MailItemID as int) as Declare @MyMailProfile as nvarchar(128) Declare @MyRecipients as varchar(4000) Declare @MySubject as nvarchar(255) Declare @MyBody as nvarchar(4000) select distinct @MyMailProfile =, @MyRecipients = i.recipients, @MySubject = i.subject, @MyBody = i.body from msdb.dbo.sysmail_log l join msdb.dbo.sysmail_mailitems i on (l.mailitem_id = i.mailitem_id) join msdb.dbo.sysmail_profile p on (i.profile_id = p.profile_id) where l.mailitem_id = @MailItemID EXEC msdb.dbo.sp_send_dbmail @profile_name = @MyMailProfile, @recipients = @MyRecipients, @subject = @MySubject, @body = @MyBody Example Usage: ResendEmailItem 3698 -- 3698 is the MailItemID

Thursday, May 24, 2007

Getting contains to work for the Generic List

In .Net 2.0, Generics have been introduced. I won't try to explain them here. I will provide an idiots guide to getting the contains method on List<> to work. Generics are quite powerful. They allow us to get all the functionality of a List without writing nearly any code. Let pretend I have an object (class) called MyCustomObject as defined below. public class MyCustomObject { string Title; string Description; public override bool Equals(object obj) { if (obj is MyCustomObject) { MyCustomObject other = obj as MyCustomObject; if (other == null) { return false; } else { bool same = other.Title == Title && other.Description == Description; return same; } } else { return false; } } } As you can see I have implemented the Equals method to only return two objects to be equal if they are of the same type and the Title and Description are the same. I consider this to be an expected behavior, but .Net wants to compare references to the objects, not the data members. The problem shows up when you want to use MyCustomObject in a List of MyCustomObjects. To get all the benefits of a List in .Net and write no code we can use Generics. In this case the List<> is what we will use. public class MyCustomObjectList : List<MyCustomObject> { } That is it. To use the List you could do something like: MyCustomObject obj1 = new MyCustomObject(); obj1.Title = "Test1"; obj1.Description = "Desc 1"; MyCustomListObjectList list = new MyCustomObjectList(); list.add(obj1); MyCustomObject obj2 = new MyCustomObject(); obj2.Title = "Test1"; obj2.Description = "Desc 1"; bool containsItem = list.contains(obj2); Notice the two objects have the same values. If we did not implement the Equals() method in MyCustomObject containsItem would be false. Since we changed what equals means, containsItem is now true because the values of the objects are the same regardless of the object itself.

Drop Down List ignores zIndex

As you may know HTML Drop Down List (FORM INPUT TYPE=SELECT) don't play nice with all the other HTML elements. Specifically, the drop down list does render like any other element. It is actually rendered on top of the other HTML because it is rendered using the Operating System's native Drop Down List. This is why it looks different on a Mac vs. a PC vs. UNIX, etc. This causes problems when you want to put an absolutely positioned DIV on top of the drop down list to hide it, or fade the page, etc. The solution I came up with is a bit of smoke and mirrors approach. Basically, use JavaScript to hide the Drop Down List, then get its parent element and add a dynamically created DIV in its place. I use CSS to make the DIV look like a Drop Down List. This solution could be improved to have different styles for each of the Operating Systems. Also, note I was not able to get an accurate size of the Drop Down List in FireFox (it works fine in IE). So, just get the size of the parent node and set the width of the DIV to that. It is not perfect for FireFox, but it is reasonable for what I was doing. Here is the CSS entry you will need to make the DIV look like a Drop Down List. .ReadOnlyDDL { font-family: Tahoma, Arial, Helvetica; font-size: 12px; font-weight: normal; text-align:left; background: white; border: 1px solid #000; padding-left: 5px; padding-right: 5px; border-color: #7f9db9; border-width: 1px; background: right no-repeat url(images/ddl.png); } The remainder of this page is the JavaScript requires to hide the Drop Down List, create a DIV dynamically. function HideDDL(ddl) { var roid = + "RO"; // make sure elements don't alread exist before creating a new one if (document.getElementById(roid) == null) { = "none"; var parent = ddl.parentNode; // for some reason the clientWidth is 10px different than expected var width = ddl.clientWidth - 10; // makes it look like the standard height for DDL var height = "17px"; // needed for FireFox if (width == null width == 0) { width = parent.offsetWidth; } var ro = CreateDIV(roid, width, height); // copy the selected value to the div text ro.innerHTML = GetSelectedText(ddl); //format div to look like a drop down list ro.className = "ReadOnlyDDL"; parent.appendChild(ro); } } function CreateDIV(id, width, height) { var newdiv = document.createElement('div'); newdiv.setAttribute('id', id); // set location and dimensions = width; = height; return newdiv; } function GetSelectedText(ddl) { return ddl.options[ddl.selectedIndex].text; } This is not strictly required, but is nice if you want to hide all Drop Down Lists on the page. function HideAllDropDownLists() { var elements = document.forms[0].elements; for (var i=0; i<elements.length; i++) { if (elements[i].type == "select-one") { HideDDL(elements[i]); } } }

Monday, May 21, 2007

Make Enter Key submit form

Here is a simplified example of how to make a form submit when the enter key is pressed while the cursor is in a particular textfield. NOTE: It is necessary to use the onkeypress event and NOT the onkeydown event of the textfield. This is because returning false in the onkeydown event does NOT stop the form from submitting. Returning false in the onkeypress event DOES stop the form submitting which is what we need it to do. <html> <body> <form> <input type="text" name="q" > <input type="submit" name="btnG" value="Submit"> <script language="JavaScript"> function handleEnterKey() { if ((event.which && event.which == 13) (event.keyCode && event.keyCode == 13)) { document.forms[0].elements('btnG').click(); return false; } else return true; } document.forms[0].elements('q').onkeypress = handleEnterKey; </script> </form> </body> </html>

Wednesday, May 16, 2007

Server 2003 Admin Tools on Windows XP

Ever need any of the tools that Windows Server 2003 has under the Admin Tools menu? Well, you can download it here It is great for Active Directory queries and Terminal Services Management (see who is using the two Terminal Services sessions on Windows Server 2003 you are trying to Remote Desktop into).

Tuesday, May 15, 2007

PDF Creater is awesome!

Need to create PDF from any problem for FREE. There is a great Open Source called PDFCreator. It can be downloaded from SourceForge.Net ( If you can print you can use this tool. It also does EPS, Postscript, and a bunch of other formats. So it is a great tool to share Visio or other formats that not everyone has installed on their computer. With that said, if you are doing something like a user manual where you need the images to be high quality you can adjust the image quality. By default it uses an automatic setting which is probably good for most things, but I recommend changing it to Zip Compression for images. To set the compression on images, go ahead an start printing like you would any document. Select the PDFCreator as the printer and click OK button on the print dialog. After a period of time the PDFCreator dialog will show. Click the Options button. Click the PDF link on the left under Formats. Next click the Compression tab. In each of the drop down lists select Zip as the compression method. It is truly amazing what you can get for FREE!

Friday, May 4, 2007

Re-connect users to logins

The stored proc built into SQL 2000 and SQL 2005 allows you to re-connect logins to their database users. The database user and SQL Server logins get disconnected you restore a database from a backup. One symptom of this is you can see that the database has a user that you expect, and SQL Server has a login with that name that you expect, but you can't login. Note that you must be connected to the current database that the database user resides. The example below reconnect them. You will need to do this for each user that is not connected anymore. use MyRestoredDB go sp_change_users_login 'Update_One', 'MyUserName', 'MyLogin'