Tuesday, August 16, 2005

Recursive File List Control v2 (ASP.NET)

Much like the original Recursive File List Control, this user control lists files and folders within a virtual web folder. Improvements include - excludes directories ending in '_file' (i.e. those created by Internet Explorer when you download a web page), excludes files beginning with '~$' (i.e. temporary documents generated by Microsoft Office applications), multiple instances can be placed on a web page, postback does not end up causing duplication of list and it takes into account other querystring parameters that may be passed on to the page.

Control (filelist.ascx)

<%@ Control Language="C#" src="filelist.ascx.cs" Inherits="WebDeveloperBlog.RecursiveFileList" %>

Code behind (filelist.ascx.cs)

using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebDeveloperBlog
{
 public class RecursiveFileList : UserControl
 {
  protected HyperLink lnkReset;
  protected Literal FileList;
  
  /// <summary>
  ///  The virtual folder is the initial folder to start with, default root of application, but can be overridden by calling page
  /// </summary>
  public string VirtualFolder
  {
   get 
   {
    if (ViewState["virtualFolder"]==null)
    {
     ViewState["virtualFolder"] = "~/";
    }
    return ViewState["virtualFolder"].ToString();
   }
   set
   {
    ViewState["virtualFolder"] = value;
   }
  }
  /// <summary>
  ///  List of file types to show. Set up in the Control_Load function
  /// </summary>
  private ArrayList fileTypes = new ArrayList();
  
  /// <summary>
  ///  For internal use only
  /// </summary>
  private ArrayList folders;
   
  /// <summary>
  ///  List of folders to be show files/folders in
  /// </summary>
  protected ArrayList Folders
  {
   get
   {
    if (folders == null)
    {
     if (UrlPathInfo != null)
     {
      folders = new ArrayList(UrlPathInfo.Split(';'));
     }
     else
     {
      folders = new ArrayList();
     }
    }
    return folders;
   }
   set
   {
    folders = value;
   }
  }
  
  /// <summary>
  ///  The path to the aspx page calling this control
  /// </summary>
  private string UrlPath
  {
   get
   {
    return HttpContext.Current.Request.Path;
   }
  }
  
  /// <summary>
  ///  The folders that are to be filtered. Used getting the folders from the Folders array list
  /// </summary>
  private string UrlPathInfo
  {
   get
   {
    return HttpContext.Current.Request.QueryString[FilterFolderParameter];
   }
  }
  
  /// <summary>
  ///  The query parameter to use when checking which folders to display the folder/file contents of
  /// </summary>
  private string FilterFolderParameter
  {
   get
   {
    return this.ClientID.ToString() + "folders";
   }
  }
  
  /// <summary>
  ///  Builds a list of folders from the Folders array list using ; as the deliminator
  /// </summary>
  private string BuildFolderList
  {
   get
   {
    string strList = string.Empty;
    for(int i=0; i<Folders.Count; i++)
    {
     strList += HttpContext.Current.Server.UrlEncode(Folders[i].ToString()) + ";";
    }
    if (strList != string.Empty)
    {
     strList = strList.Substring(0, strList.LastIndexOf(';'));
    }
    return strList;
   }
  }
  #region Web Form Designer generated code
  override protected void OnInit(EventArgs e)
  {
   //
   // CODEGEN: This call is required by the ASP.NET Web Form Designer.
   //
   InitializeComponent();
  }
  /// <summary>
  ///  Required method for Designer support - do not modify
  ///  the contents of this method with the code editor.
  /// </summary>
  private void InitializeComponent()
  {
   this.Load += new System.EventHandler(this.Control_Load);
  }
  #endregion
  private void Control_Load(object sender, EventArgs e)
  {
   // add file types
   fileTypes.Add("doc");
   fileTypes.Add("rtf");
   fileTypes.Add("xls");
   fileTypes.Add("pdf");
   // add reset list link if not already added
   if (lnkReset == null)
   {
    lnkReset = new HyperLink();
    lnkReset.Text = "Reset list '" + VirtualFolder + "'";
    this.Controls.Add(lnkReset);
   }
   // set result link to path of current page
   string resetUrl = UrlPath;
   NameValueCollection q = new NameValueCollection(HttpContext.Current.Request.QueryString);
   q.Remove(FilterFolderParameter);
   for (int i = 0; i <= q.Count - 1; i++)
   {
    if (i != 0)
    {
     resetUrl += "&amp;";
    }
    else
    {
     resetUrl += "?";
    }
    resetUrl += q.GetKey(i) + "=" + q.Get(i);
   }
   lnkReset.NavigateUrl = resetUrl;
   // add file list Literal control if not on page
   if (FileList == null)
   {
    FileList = new Literal();
    this.Controls.Add(FileList);
   }
   // recurse folder starting with the supplied virtual folder
   if (!Page.IsPostBack)
   {
    RecurseFolders(VirtualFolder);
   }
  }
  
  /// <summary>
  ///  Check if in Folders array list
  /// </summary>
  private bool InFolders(string s)
  {
   for(int i=0; i<Folders.Count; i++)
   {
    if(Folders[i].ToString() == s)
    {
     return true;
    }
   }
   return false;
  }
  
  /// <summary>
  ///  Check if file is one that is allowed
  /// </summary>
  private bool AllowedFileType(string fileExt)
  {
   for(int i=0; i<fileTypes.Count; i++)
   {
    if(fileTypes[i].ToString() == fileExt.ToLower())
    {
     return true;
    }
   }
   return false;
  }
  
  /// <summary>
  ///  Recurse folders in path, excluding those that aren't in the Folders array list
  /// </summary>
  private void RecurseFolders(string path)
  {
   // Open the list
   FileList.Text += "<ul>";
   string mappedRoot = HttpContext.Current.Server.MapPath(path);
   HttpContext.Current.Trace.Write("WebDeveloperBlog.RecursiveFileList", "Folder: " + mappedRoot);
   string[] folders = null;
   try
   {
    folders = Directory.GetDirectories(mappedRoot);
    // Iterate through each folder
    foreach (string folder in folders)
    {
     // ignore folders ending in _files (these are folders used when saving web pages from Internet Explorer)
     if (folder.Substring(folder.Length - 6) == "_files")
     {
      continue;
     }
     string virtfolder = folder.Replace(mappedRoot, string.Empty);
     string subFolder = path + virtfolder + "/";
     string[] subfolders = Directory.GetDirectories(folder);
     string[] files = Directory.GetFiles(folder);
     string whichfolders = string.Empty;
     HttpContext.Current.Trace.Write("WebDeveloperBlog.RecursiveFileList", folder + "\n Sub folder count: " + subfolders.Length + "\n File count: " + files.Length);
     if (InFolders(subFolder))
     {
      Folders.Remove(subFolder);
      whichfolders = BuildFolderList;
      Folders.Add(subFolder);
     }
     else
     {
      Folders.Add(subFolder);
      whichfolders = BuildFolderList;
      Folders.Remove(subFolder);
     }
     NameValueCollection q = new NameValueCollection(HttpContext.Current.Request.QueryString);
     q[FilterFolderParameter] = whichfolders;
     if (q[FilterFolderParameter] == string.Empty)
     {
      q.Remove(FilterFolderParameter);
     }
     FileList.Text += "<li><a href=\"" + UrlPath;
     for (int i = 0; i <= q.Count - 1; i++)
     {
      if (i != 0)
      {
       FileList.Text += "&amp;";
      }
      else
      {
       FileList.Text += "?";
      }
      FileList.Text += q.GetKey(i) + "=" + q.Get(i);
     }
     FileList.Text += "\">" + HttpContext.Current.Server.HtmlEncode(virtfolder) + "</a>";
     if (InFolders(subFolder))
     {
      // recurse sub folder
      FileList.Text += Environment.NewLine;
      RecurseFolders(subFolder);
     }
     FileList.Text += "</li>" + Environment.NewLine;
    } 
   }
   catch (System.Exception ex)
   {
    HttpContext.Current.Trace.Warn("WebDeveloperBlog.RecursiveFileList", ex.Source, ex);
   }
   try
   {
    string[] files = Directory.GetFiles(mappedRoot);
    // Iterate through the files in this folder
    if(files.Length == 0)
    {
     if (folders.Length == 0)
     {
      FileList.Text += "<li>No files</li>" + Environment.NewLine;
     }
    }
    else
    {
     foreach (string file in files)
     {
      string fileName = file.Replace(mappedRoot, string.Empty);
      // ignore files beginning with ~$ (these are files generated by Microsoft Office applications)
      if (fileName.Substring(0,2) == "~$")
      {
       continue;
      }
      if (!AllowedFileType(GetExt(fileName))) continue;
      // link to file
      FileList.Text += "<li> File: " + "<a href=\"" + path + fileName + "\">" + HttpContext.Current.Server.HtmlEncode(RemoveExt(fileName)) + "</a></li>";
      // Put the fileName in the textbox
      FileList.Text += Environment.NewLine;
     }
    }
   }
   catch (System.Exception ex)
   {
    HttpContext.Current.Trace.Warn("WebDeveloperBlog.RecursiveFileList", ex.Source, ex);
   }
   // Close the list
   FileList.Text += "</ul>" + Environment.NewLine;
  }
  
  /// <summary>
  ///  Remove file extension from the supplied file name
  /// </summary>
  private string RemoveExt(string fileName)
  {
   return fileName.Substring(0,fileName.LastIndexOf('.'));
  }
  
  /// <summary>
  ///  Get the extension from the supplied file name
  /// </summary>
  private string GetExt(string fileName)
  {
   int dot = fileName.LastIndexOf('.') + 1;
   return fileName.Substring(dot, fileName.Length - dot);
  }
 }
}

Sample page (filelist.aspx)

<%@ Page Language="C#" Trace="False" ContentType="text/html" ResponseEncoding="iso-8859-1" %>
<%@ Register TagPrefix="Files" TagName="List" Src="filelist.ascx" %>
<html>
<head>
<title>File listing</title>
</head>
<body>
<form runat="server">
 <Files:List runat="server" VirtualFolder="/virtualdir/" />
 <Files:List runat="server" VirtualFolder="/virtualdir2/" />
</form>
</body>
</html>

Tags: , ,

No comments: