Archive for July 2007

How to automatically and recursively delete empty directories

Years ago I made a serious mistake when I installed, and briefly used, iTunes — I gave it the ability to reorganize my music folders!  Since then, all my nicely organized and sorted directories of (legally obtained) digital music have been disorganized and confused.

For example, I used to have a directory named “U2″ where all my music for the group U2 was stored.  However, iTunes decided that my organization structure was too simple, and broke it out into folders derived from the contributing artists.  Now I have “Bono & The MDH Band”, “Bono_Daniel Lanois”, “Bono_Gavin Friday_Maurice Seezer”, etc. It’s a real mess.  Additionally, it left the folder “U2″ with a bunch of empty subdirectories!  How rude!

I’ve made an effort, as of late, to try and clean-up this mess.  In doing so, I’ve ended up with a lot of empty directories that previously contained music and/or subdirectories.  After manually deleting a couple directories, I decided I needed to automate the process.  I was surprised to find that this process is not nearly as straightforward as I thought it would.

In going about this process, I decided to do the following:

  • Allow it to scan the directories first, and tell me which folders are empty (allows me to double-check!)
  • Ignore all hidden files (this is because Windows Media Player and iTunes create hidden album art which I don’t want to preserve)
  • Log empty directories, deleted directories and errors
  • Scan first, delete second
  • Ignore read-only attributes (all my music is marked as read-only to prevent someone from doing what I’m doing)
  • Delete a directory if it is made empty because it’s subdirectories are deleted

Let me break down how I accomplished these things.

To scan for empty directories, I created a method that starts with a predefined directory, and recursively iterates through all the subdirectories.  During this iteration, I scan the folder to see if it contains any files using the GetFiles method.  Rather than specify files with a specific file extension (like .mp3 or .wma) I decided to get all files by using the “*.*” pattern (directory happens to be DirectoryInfo object).

using System.IO;

...

FileInfo[] files;
files = directory.GetFiles("*.*");

However, many of the files are hidden (because WMP and iTunes creates album art that’s hidden), so I decided to exclude hidden files.  I did this by checking the hidden attribute of the file, and if it was turned on I ignored the file.  If non-hidden files were found, I marked a local boolean value as true, indicating the folder contains files.

foreach (FileInfo file in files)
{
     // only look for files that are not marked as hidden
     if (!((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden))
     {
         notEmpty = true;
     }
}

Next, I want to check to see if the directory contains subdirectories, and if those subdirectories are empty.  To do this I get all the subdirectories and call the same method that is currently executing.  This provides a level of recursion that allows me to iteratively go through every subdirectory in a directory.  Since the existence of a subdirectory means that the directory is not empty, I automatically mark the current directory as not empty.

// look through directories recursively
DirectoryInfo[] dirs = directory.GetDirectories("*.*");
foreach (DirectoryInfo dir in dirs)
{
     scanForEmptyDirectories(dir);
     notEmpty = true;
}

Next, I add the directory path to a generic string collection so that I can later iterate through and delete the directories.  The code here is very simple:

private static List<string> directoriesToDelete;
directoriesToDelete = new List<string>();

...

if (!notEmpty)
{
    // add folder to collection
    directoriesToDelete.Add(directory.FullName);
}

This accomplished, I needed to write a method that logs empty directories, the deletion of directories, and errors.  I created a method that uses the StreamWriter and appends text to a file.  Very simple.

public static void logMessage(string logMessage)
{
    using (StreamWriter writer = File.AppendText(logFile))
    {
        writer.WriteLine(logMessage);
        writer.Flush();
    }
}

This method is called throughout the application, and writes any pertinent information to a log file (as you’ll see below).

Now, to delete the directories, I iterate through my generic string collection.  Since some of my directories are marked as read-only, I elected to change the attributes of all directories marked for deletion to normal.  This removes the read-only flag, if it exists.  Next, I performed a similar operation on the files left in the directory (e.g. hidden files) and removed the read-only flag.  Once these two steps are complete, I can then delete the folder using the Delete method on the Directory object.

// iterate through the empty directories
foreach (string directory in directoriesToDelete)
{
    try
    {
        DirectoryInfo dir = new DirectoryInfo(directory);
        // set the attributes to normal; you cannot delete a readonly folder
        dir.Attributes = FileAttributes.Normal;

        string[] files = Directory.GetFiles(directory);
        // iterate through the files
        foreach (string file in files)
        {
            FileAttributes attributes = File.GetAttributes(file);
            if ((attributes & FileAttributes.ReadOnly) != 0)
            {
                // remove the readonly attribute from the files
                File.SetAttributes(file, ~FileAttributes.ReadOnly);
            }
        }

        // make sure to make the delete as recursive
        Directory.Delete(directory, true);
        // log the deletion
        logMessage(string.Format("Deleted: {0} ", directory));
    }
    catch (Exception e)
    {
        // log the error
        logMessage(string.Format("Error deleting: {0} ", directory));
        logMessage(string.Format("Error message: {0} ", e.Message));
        logMessage("Exiting application");
        // exit the console application
        Environment.Exit(0);
    }
}

Notice that I catch errors, log them, and then exit the application.  Also, the try/catch is embedded in the directory loop, so that the directory path can be included in the log (if it were the other way around, the directory path would be out of scope and unavailable).

Finally, given the above code, it is possible to make a parent directory empty by deleting its child directories.  To resolve this, I wrapped the execution of my code in a while statement that only exists when a given condition returns false.  This condition value returns false only if there hasn’t been a single directory marked as empty.  If any directory is marked as empty, the application performs an additional scan following the deletion of the directories it found empty.  (Note: the runScan boolean value is a class-level variable that is set to true elsewhere in the code.)

while (runScan)
{
     // assume that we won't have to run again
     runScan = false;

     // if the value returns true, then run it again
     // it would return true 
     scanForEmptyDirectories(new DirectoryInfo(startingDirectory));
}

All in all, this was a very quick and efficient way to eliminate my empty music folders.  Your needs may be slightly different, so make changes accordingly.

I’ve gone ahead and included a zip file with the full source code for this console appliation.  Let me know if you have thoughts and/or suggestions!

EmptyFolders.zip (20.81 KB)

Sharing photos with family and friends

My cousin Kai just got married yesterday in Maine.  I wasn’t able to make it, but my parents and sister were able to fly up and attend.  By all accounts, it was a fantastic wedding and I wish we could have made it, but with a month old baby (not to mention her two-year-old sister) we just didn’t feel like we could handle the trip.  And, given the multiple layovers and missed flights my parents encountered along the way, I think we made the right choice.

Still up in Maine, my dad called and said that he wanted to know the best way for all the attendees to share the great multitude of pictures that were taken.  He offered to use his corporate server to host the pictures, but I suggested that it would take a lot of work to write an interface to these pictures that was slick, intuitive, and made it easy for people to post, share, and view pictures.  Plus, there are a lot of great services out there, such as flickr, photobucket, and webshots (incidentally, doesn’t anyone believe in capitalizing any more?).

Since we want to share both pictures AND video, I decided to go with webshots.  I can’t say that I really love the interface, but it does seem to have all the necessary bells and whistles.

One thing I immediately noticed is that there does not appear to be any Web site that allows a community of users to post and manipulate the pictures of an album.  For instance, given my cousin’s wedding as an example, I don’t know of a Web site that would allow multiple people to register for the same album so that they could all upload and share pictures together.  The best solution I can think of is to create a new account, share the username and password with those interested in posting pictures, and distribute the URL to those that simply wish to view the pictures.

That said, I was asked to create said community, and share some simple instructions on how to both view the pictures, as well as upload new pictures.

View the pictures

  1. Open up your browser of choice.
  2. Browse to: http://community.webshots.com/user/kaisky2007/
  3. Select your album of choice (e.g. Wedding).

Upload new pictures

  1. Open up your browser of choice.
  2. Browse to: http://www.webshots.com/
  3. Enter the username and password in the upper right-hand corner, and click the “log in” button.  Feel free to  contact me if you need the login information.

    image

  4. After you are logged in, click the “upload” menu button.

    image

  5. Select an upload destination (or create a new album).  Personally, I’d recommend just using the existing “Wedding” album to keep it simple.

    image

  6. Click the “select files” button.  This will open a file dialog box.

    image

  7. Browse to the location of your pictures and/or videos.  Select the files you want to upload, and click the “Open” button.  You should see the selected files added to your file list.

    image

  8. Repeat steps 6 and 7 to add additional files.
  9. Once have added all your files, click the “upload” button.

    image

  10. You can now add a title and description to the pictures and videos you have uploaded.  The more information the better, so add some comments!  If you don’t, then people that missed the wedding (like me!) won’t know what I’m looking at.  Click the “save” button to save the title and description (or click “save all changes” below).

    image

  11. When you are done, you can click “view album” to look at your handiwork!

Pretty simple stuff!  I hope that someone in the family finds this information useful!

Congratulations, Kai and Sky!

Commerce Server in the headlines: 07/08/2007 – 07/14/2007

It was a slower-than-normal week in the world of Commerce Server; that is, slow for everyone except Max.  The man is a Commerce Server animal!

The general slowness and lack of blog posts can probably be attributed to the Worldwide Partner Conference in Denver, CO., this past week.  Lots of activity.  Unfortunately, I was unable to attend.  I’ve been swamped with BizTalk projects and dirty diapers.

Here’s what I read regarding Commerce Server this past week …

Blogs (posts you should read)

07/10/2007 – Max Akbar - Endeavor Commerce to unveil SmartCatalog TM for CRM 6.1 at Microsoft Worldwide Partner Conference – Max mentioned that Endeavor Commerce announced version 6.1 of their SmartCatalog for CRM application.  You can learn more and SmartCatalog, and all the great stuff they’re doing at Endeavor Commerce, here.

07/11/2007 – Max Akbar - Microsoft Commerce Server Webcasts on TechNet – Max has put together two new webcasts: How to Configure Commerce Server 2007 and Schemas for Commerce Server 2007 BizTalk Adapters.  His first webcast shows specific instructions on how to configure Commerce Server applications, and the second provides detailed information how how to use the BizTalk schemas for the Commerce Server adapters.  Be sure and take a look at these two screen casts.  As with most everything Max does, these webcasts are great.

07/13/2007 - CS Team Blog [MSFT]“Invalid Viewstate” With Load Balancers – Joe Wasson, a member of the Commerce Server product team, provides a solution to a problem that may occur when a client goes through the checkout process.  If you receive the error”Viewstate verification failed. Reason: Viewstate was invalid.” during the checkout process, be sure and take a look at this post.

07/14/2007 – Max Akbar - Commerce Server 2007 SP1 Partner SDK – Max reminded us all that the Partner SDK has also been updated: Commerce Server 2007 SP1 Partner SDK.

07/14/2007 – Max Akbar - Catalog Web Service and SetJoin API Configuration - Max discovered a problem with the Set Join API documentation, with regards to the Catalog Web Server web.config file.  Along with the product group, Max was able to find a solution to the issue.

Forum / Newsgroup (answered questions that you might find useful to peruse)

07/09/2007 – CS2007 now fails – An additional tip on how to resolve the “DirectMailer already exists” problem that occurs when configuring a renamed Commerce Server computer.

07/09/2007 – Error while using the Site Connection – Jared Hodges provides two steps that must be taken to make sure that the management applications can connect to the orders service.  These both relate to whether or not your certificate is trusted.

07/10/2007 – Migrating profile data from CS2002 to CS2007 – Max provides two useful links describing how to migrate profile data from Commerce Server 2002 to Commerce Server 2007.

07/10/2007 – Unit Test failing for Starter Site – William was having problems with the BasketHelperTest and CatalogHelperTest for the starter site.  He resolved the problem by installing Service Pack 1 for Visual Studio 2005.

07/10/2007 – HTTP 500 Error When Launching Business User Application - David Lott provides tips on how to configure Commerce Server 2007 on Windows Vista.

07/10/2007 – Product Relationships Question – Describes how to link the product definition data to the product relationships returned from the CatalogRelationshipsDataTable.

07/11/2007 – Error in Getting Profile – Some guidance on how to retrieve profile information.

07/11/2007 – How to determine is user authenticated in current session [sic] – Again, William answered his own question, and discovered the HttpContext.Current.User.Identity.IsAuthenticated property.

Have a great week!