Download complete code samples on EPiServer World

Introduction

The following sample shows how to work with the following areas:

The project is a custom file manager that allows a logged in user to upload files, create, rename and delete folders, change summaries on files and change author on files.

The sample also includes a preview feature on image files as well as direct linking to allow a user to open server files directly from the browser. The following is an image preview of what the page will look like if using the style sheet used in this sample.

The sample will not be best practice at all times since this is merely an introduction to some of the concepts in the EPiServer Framework, but some considerations will be mentioned (and at times demonstrated).

FileSystemDataSource

To start things off we create a new .aspx template that we choose to name FileManager.aspx.

On the following question "Do you want to register a PageType for FileManager.aspx" we choose no.

In this sample we will assume that you are using a Masterpage called MasterPage.master and that it is placed in a "MasterPages" folder. Remove the comments after the initial declarations in the markup so that FileManager.aspx looks as follows.

Add an EPiServer.Web.WebControls.FileSystemDataSource to the markup (within the content section as with all the remaining markup except scripts that go into the <script> section). You can do this in design mode by dragging and dropping the control from the toolbox. The DataSource will read files and folders from the public section of your site's folder structure. We will later hook up a TreeView to this datasource to display the structure.

Modify the markup of the data source to match the following.

Copy 
<EPiServer:FileSystemDataSource runat="server" ID="treeDataSource" IncludeRoot="true">
</EPiServer:FileSystemDataSource>

Reading File and Folder information

Start off by adding two labels to your page. The first label will be used to indicate whether the currently clicked item in the FileTree is a file or a folder and the second label will be used to print the virtual path of the selected folder. See the top of example image for an indication on how this will look.

Copy 
<asp:label ID="ItemType" runat="server" ForeColor="Red" />&nbsp;
<asp:Label ID="SelectedFolderLabel" Text="" runat="server" /><br />

Create, Rename and Delete Folders

Let's add the controls for manipulating folders. We are going to need the following.

Control Name Description
Label ToolsHeading A simple lable to tell the user what this section of the GUI does.
Button CreateButton When clicked, this button will call a script method who in turn will call the code-behind that creates a new folder.
TextBox NewFolderName An edit field to enter the name of the folder to create.
Button RenameBtn When clicked, will call the script method who in turn calls the code-behind responsible for renaming the selected folder.
Edit NewName An edit field to enter the new name of the folder to be renamed.
Button DeleteBtn When clicked, will call the script method who in turn calls the code-behind responsible for dealing with deletion of a folder.
Label UploadLabel A simple description label.
FileUpload uploadFile A file browsing control for uploading local files to the server.
Button UploadBtn When clicked, will call the script method who in turn calls the code-behind responsible for handling uploading of files.

And here is the markup.

Copy 
<div class="tools">
    <asp:Label ID="toolsheading" runat="server" Text="Folder actions" /><br />
    <hr />
    <asp:Button 
        ID="CreateButton" width="90" 
        runat="server" Text="New" 
        OnClick="InsertFolder" />&nbsp;
    <asp:TextBox ID="NewFolderName" runat="server" Width="100" /><br />

    <asp:Button ID="RenameBtn" enabled="false" width="90" runat="server" 
        Text="Rename" OnClick="RenameFolder" />&nbsp;

    <asp:TextBox ID="NewName" runat="server" Width="100"/><br />

    <asp:Button ID="DeleteBtn" enabled="false" width="90" runat="server" 
    Text="Delete" OnClick="DeleteFolder" /><br />
    <br />
    <asp:label ID="UploadLabel" runat="server" Text="Upload file" />
    <br />
    <asp:FileUpload ID="uploadFile" enabled="false" runat="server" Width="180" />&nbsp;
    <asp:Button 
        ID="UploadBtn" 
        Height="20" 
        enabled="false" 
        width="90" 
        OnClick="FileUpload" 
        runat="server" 
        Text="Upload" />
</div>

The Scripts

In order to interact with the controls of the page and the code-behind I've chosen to place a few scripts in the header section. Most of these scripts will call methods in the code-behind but some are just for enabling and disabling controls depending on what item is clicked in the TreeView.

CopyC#
// Upload the selected file to the selected folder on the server. The file will be sent
// as a stream containing the contents of the file, a filename and a path to the folder.
void FileUpload(object sender, EventArgs e)
{
    UploadFile(uploadFile.FileContent, uploadFile.FileName, FileTree.SelectedNode.DataPath);
}

void SaveSummaryClicked(object sender, EventArgs e)
{
    SaveSummary();
}

// Enable and disable buttons and controls depending on if the selected item is a folder or file
// The method for checking if it is a folder resides in the code-behind.
void FileTree_SelectedNodeChanged(object sender, EventArgs e)
{
    SelectedFolderLabel.Text = FileTree.SelectedNode.DataPath;

    string a = FileTree.SelectedValue;
    if (!IsFolder(FileTree.SelectedNode.DataPath))
    {
        ItemType.Text = "File";
        RenameBtn.Enabled = false;
        DeleteBtn.Enabled = false;
        CreateButton.Enabled = false;
        UploadBtn.Enabled = false;
        uploadFile.Enabled = false;
        LnkBtn.Visible = true;
        OpenLabel.Visible = true;
        HandleFile(FileTree.SelectedNode.DataPath);
    }
    else
    {
        ItemType.Text = "Folder";
        LnkBtn.Visible = false;
        OpenLabel.Visible = false;
        RenameBtn.Enabled = true;
        DeleteBtn.Enabled = true;
        CreateButton.Enabled = true;
        UploadBtn.Enabled = true;
        uploadFile.Enabled = true;
    }
}

// Inserts a folder with the path indicated by the selected folder in the 
// TreeView.
void InsertFolder(object sender, EventArgs e)
{
    CreateFolder(FileTree.SelectedNode.DataPath, NewFolderName.Text);
}

// Deletes the folder with the indicated by the selected folder in the 
// TreeView.
void DeleteFolder(object sender, EventArgs e)
{
    DeleteFolder(FileTree.SelectedNode.DataPath);
}

// Renames the folder selected in the treeview to the name in the Edit field
// named "NewName".
void RenameFolder(object sender, EventArgs e)
{
    if (NewName.Text.Length < 1)
        return;
    RenameFolder(FileTree.SelectedNode.DataPath, FileTree.SelectedValue, NewName.Text);
}

Code-Behind

When a control event is raised the script method defined in the control declaration will be called. For example, when CreateButton is clicked, the method "InsertFolder" is called. Following is the code-behind called by the scripts as well as a few helper methods.

CopyC#
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
        LoadUsers();
}

// Helper function that returns a UnifiedDirectory instance from the given path. The directory
// is tested and an Exception is thrown if no directory was found.
// string path - The virtual path to the directory.
/// <returns></returns>
protected UnifiedDirectory GetUnifiedDirectory(string path)
{
    UnifiedDirectory directory = HostingEnvironment.VirtualPathProvider.GetDirectory(path) as UnifiedDirectory;
    if (directory == null)
        throw new ArgumentException("Given path is not a Unified Directory");
    return directory;
}

// Returns true if the supplied path points to an existing file folder.
// string path - The virtual path to the folder.
protected bool IsFolder(string path)
{
    return HostingEnvironment.VirtualPathProvider.DirectoryExists(path);
}


// A file is received as a Stream from the client. The Stream is copied to a new stream, created
// with the FileMode.CreateNew flag set. Note the use of EPiServer.BaseLibrary.IO.StreamConsumer
// to copy the stream.
protected void UploadFile(Stream fileContent, string filePath, string destination)
{
    UnifiedDirectory dir = GetUnifiedDirectory(destination);
    UnifiedFile uFile = dir.CreateFile(filePath);
    Stream s = uFile.Open(FileMode.CreateNew);
    StreamConsumer.CopyToEnd(fileContent, s);

    s.Close();

    fileContent.Close();
}

// Simply creates a folder with the name [string name] at the location [string path].
protected void CreateFolder(string path, string name)
{
    UnifiedDirectory.CreateDirectory(path + name);
}

// Checks that the suplied path points to a folder and if true,deletes it.
protected void DeleteFolder(string path)
{
    if (IsFolder(path))
    {
        UnifiedDirectory directory = GetUnifiedDirectory(path);
        directory.Delete();
    }
}

// Since there is no Rename method on directories we have to call MoveTo instead. What we do is move
// the folder to the same path but with a new name.
protected void RenameFolder(string path, string oldName, string name)
{
    if (IsFolder(path))
    {
        UnifiedDirectory directory = GetUnifiedDirectory(path);

        int e = -1;
        while (path.IndexOf(oldName, ++e) > -1) ;

        StringBuilder sb = new StringBuilder();
        sb.Append(path.Substring(0, e - 1));
        sb.Append(name);
        sb.Append("/");

        directory.MoveTo(sb.ToString());
    }
}
// Checks what type of object we have and calls the appropriate methods depending on
// if we have a file or folder. Also checks if we have an image and if that is the case; 
// enabled the ImageHolder control and sets the ImageUrl.
public void HandleFile(string path)
{

    if (IsFolder(path))
    {
        ClearSummaryFields();
        return;
    }

    UnifiedFile file = HostingEnvironment.VirtualPathProvider.GetFile(path) as UnifiedFile;
    string[] extensions = { ".png", ".jpg", ".bmp", ".gif" };

    LnkBtn.NavigateUrl = path;
    ImageHolder.Visible = false;
    foreach (string a in extensions)
    {
        if (file.Extension.CompareTo(a) == 0)
        {
            ImageHolder.ImageUrl = path;
            ImageHolder.Visible = true;
            ClearSummaryFields();
        }
    }
    ShowFileSummary(file);
}

Showing and Modifying File Summaries

In the initial sample Web site image there is a section for file summaries as well as an "Open File" button. The button is actually just an asp:HyperLink with an image. The link is set each time the selection is changed in the FileTree.

To access and read file information such as author, description and document type we use the Dictionary of the Summary property on a UnifiedFile object. If we wish to change these values we simply assign our new values and call Summary.SaveChanges().

You will also notice that there is a DropDownList showing registered users as well as letting us choose a user as well as setting one of these users as the "Author" of the document. In this sample I simplified things by calling System.Web.Security.Membership.GetAllUsers() a list with perhaps a thousand users is not optimal and all users might not be in roles where you can actually be the Author of a document.

CopyC#
// * Adds an empty slot to the Users DropDown list
// * Checks the DropDownList for the correct user and selects it
// * Reads the Summary dictionary and assigns the correct values to the designated labels on the page
private void ShowFileSummary(UnifiedFile file)
{
    if (file.Summary.Dictionary["Author"] == null)
        UsersDropDown.SelectedIndex = 0;
    else
    {

        for (int a = 0; a < UsersDropDown.Items.Count; a++)
        {
            if (UsersDropDown.Items[a].Value == file.Summary.Dictionary["Author"].ToString())
            {
                UsersDropDown.SelectedIndex = a;
                break;
            }
        }
    }
    CategoryLabel.Text = file.Summary.Dictionary["Categories"] as string;
    DocTypeLabel.Text = file.Summary.Dictionary["DocumentType"] as string;
    SummaryEdit.Text = file.Summary.Dictionary["Description"] as string;
    SaveSummaryBtn.Enabled = true;
}

// Clears the labels, disables the save button and sets selectedindex to 0 on the dropdownlist
private void ClearSummaryFields()
{
    UsersDropDown.SelectedIndex = 0;
    CategoryLabel.Text = "";
    DocTypeLabel.Text = "";
    SummaryEdit.Text = "";
    SaveSummaryBtn.Enabled = false;
}

// Saves the summary
protected void SaveSummary()
{
    if (IsFolder(FileTree.SelectedNode.DataPath))
        return;
    UnifiedFile file = HostingEnvironment.VirtualPathProvider.GetFile(FileTree.SelectedNode.DataPath) as UnifiedFile;
    file.Summary.Dictionary["Description"] = SummaryEdit.Text;
    file.Summary.Dictionary["Author"] = UsersDropDown.SelectedValue;

    if (file.Summary.CanPersist)
        file.Summary.SaveChanges();
}

// Loads all registered users into the dropdownlist
protected void LoadUsers()
{
    MembershipUserCollection mColl = System.Web.Security.Membership.GetAllUsers();
    UsersDropDown.Items.Add("");
    foreach (MembershipUser m in mColl)
    {
        UsersDropDown.Items.Add(m.UserName);
    }
}
Copy 
<div class="summary" style="background-color:#e5e5e5;" >
    <asp:HyperLink 
        ID="LnkBtn" 
        runat="server" 
        ImageUrl="~/Images/document_view.png" 
        Visible="false"  />

    <asp:Label ID="OpenLabel" runat="server" Visible="false" Text="Open this file" />
    <hr />
    <table>
        <tr>
            <td>Type</td>
            <td>-</td>
            <td><asp:Label ID="DocTypeLabel" runat="server" Text="" /></td>
        </tr>
        <tr>
            <td>Summary</td>
            <td>-</td>
            <td>
                <asp:TextBox 
                    ID="SummaryEdit" 
                    runat="server" 
                    Height="80" 
                    Width="170" 
                    TextMode="MultiLine" 
                    Rows="10" />  
            </td>
        </tr>
        <tr>
            <td>Author</td>
            <td>-</td>
            <td><asp:DropDownList ID="UsersDropDown" width="175" runat="server"  /></td>
        </tr>
        <tr>
            <td>Category</td>
            <td>-</td>
            <td><asp:Label ID="CategoryLabel" runat="server" Text="" /></td>
        </tr>
    </table>
    <asp:Button 
        ID="SaveSummaryBtn" 
        runat="server" 
        Text="Save Summary" 
        OnClick="SaveSummaryClicked" 
        Enabled="false" />
</div>

Hooking up a FileTree to Our DataSource

Connecting a FileTree to the FileSystemDatasource is a simple process. Just add an aspnet FileTree control and set the DataSourceId to our FileSystemDataSource and you are done (also remember to set the OnSelectedNodeChanged attribute to point to the script method defined earlier).

Copy 
<asp:TreeView 
        CssClass="Tree" 
        runat="server" 
        ID="FileTree" 
        OnSelectedNodeChanged="FileTree_SelectedNodeChanged"
        DataSourceID="treeDataSource" 
        MaxDataBindDepth="6" 
        ShowExpandCollapse="true"
        ExpandDepth="0" 
        SelectedNodeStyle-BackColor="#e5e5e5">
    <DataBindings>
        <asp:TreeNodeBinding TextField="Name" ToolTipField="Title" ImageUrlField="ImageUrl" />
    </DataBindings>
</asp:TreeView>

The Image Preview

In this sample I created a small Image preview control. Whenever a file is clicked in the FileTree, the code checks if this file is an image. If it is, the image is shown in a placeholder control at the bottom of the page. The code could be extended to show a thumbnail of the image, or to perhaps show all files in a directory as a thumbnails when the directory is clicked. The code-behind is covered in the topic Showing and Modifying File Summaries in the HandleFile method. The following is the markup for the image placeholder.

Copy 
<div style="float:left;">
    <br />
    Image Preview<br />
    <hr />
    <asp:Image ID="ImageHolder" runat="server" Visible="false" />
</div>

Checking Access Rights

If a user with insufficient access rights clicks the delete button for a folder the site will report an error. A better way to deal with this is to check access rights and handle this in code. In this sample we will simply add a method that is called everytime the selected node changes in the TreeView. The method will check the access rights of the current user and disable and enable buttons accordingly.

CopyC#
// Checks access rights of the current user and enables/disables buttons
// depending on those permissions
protected void CheckAccessLevels(string path)
{
    // Only check for folders
    if (!IsFolder(path))
        return;

    // Create an instance of the folder to check permissions for
    UnifiedDirectory dir = GetUnifiedDirectory(path);

    if (dir.QueryDistinctAccess(AccessLevel.Create))
        CreateButton.Enabled = true;
    else
        CreateButton.Enabled = false;

    if (dir.QueryDistinctAccess(AccessLevel.Edit))
        RenameBtn.Enabled = true;
    else
        RenameBtn.Enabled = false;

    if (dir.QueryDistinctAccess(AccessLevel.Delete))
        DeleteBtn.Enabled = true;
    else
        DeleteBtn.Enabled = false;
}