File Uploader for Lightbox

File Uploader for Lightbox

 Uploader component

1. Intro

In this article we demonstrate how to add DHTMLX components in the Lightbox of Scheduler .NET. You are assumed to be experienced Microsoft ASP.NET web application developers and DHTMLX Scheduler .NET users. To proceed with this tutorial, you should first get familiar with the following articles:

There is basic knowledge for adding simple components to the Lightbox.

Let’s extend the Lightbox component of Scheduler .NET with uploader functionality.

2. Define a component on client side

Like in the base article, we add a javascript code for Uploader Lightbox Control. At first, we start with base methods. They are all described in the article here.

  • render - to define a container of vault component
  • set_value - to initialize DHTMLX Vault and all logic
scheduler.form_blocks["vault"] = {
    render: function (sns) { //sns - section configuration object
        return "<div id='" + sns.type + "' style='height:" + (sns.height || 40) + "px;' ></div>";
    },
    set_value: function (node, value, ev, config) {
        //node - html object related to html defined above
        //value - value defined by map_to property
        //ev - event object
        scheduler.form_blocks.vault.upload(config.name, ev.id);
 
    },
    get_value: function (node, ev) {
        //node - html object related to html defined above
        //event object
        return;
    },
    focus: function (node) {
        //node - html object related to html defined above
 
    },
    /*for initializing of vault*/
    upload: function (name, event_id) {
	//name - container name
	//event_id - id of event
    }
}

Now we focus on custom methods for the vault. At first, we include the following files:

  1. dhtmlxvault.css
  2. dhtmlxvault.js
<link rel="stylesheet" type="text/css" href="dhtmlxvault.css"/>
<script src="dhtmlxvault.js"></script>

In the code below we initialize the vault component:

if (!scheduler._vaultInst) {
    scheduler._vaultInst = new dhtmlXVaultObject({
        container: "vault",
        uploadUrl: scheduler._vaultConfig.uploadUrl
    });
	scheduler._vaultInst.setAutoStart(true);
	}
return scheduler._vaultInst;

where

  • container - id of html container element for DHTMLX Vault component
  • uploadUrl - this url will be called for uploading file

Then we set some configurations:

  • the number of files allowed to be added list. In this example only 5 files are allowed:
vault.setFilesLimit(5);
  • initialize the height of DHTMLX Vault component. In this example it is 200 px:
vault.setHeight(200);
  • initialize the width of DHTMLX Vault component. In this example it is 610 px:
vault.setWidth(610);
  • apply some skin for the component. For this example, we choose “terrace” skin:
vault.setSkin("dhx_terrace");

Also, the following skins are available:

  • dhx_skyblue (default)
  • dhx_web

To use some events of DHTMLX Vault Component, you need to use the method attachEvent, like in the following code:

 vault.attachEvent(string name,function handler);

For upload example, we use the following events:

  • onFileRemove occurs when user removes a single file from the file list
vault.attachEvent("onFileRemove", function (file) {
...
});
  • onBeforeFileAdd occurs before the user adds a file or files to the upload queue. The number of calls in this method is equal to the amount of files. This method is used for validation in our example:
vault.attachEvent("onBeforeFileAdd", function (file) {
...
});

To destruct a vault instance, we use Unload method:

 vault.unload();

The full code of this chapter is the following:

scheduler.form_blocks["vault"] = {
    render: function (sns) { //sns - section configuration object
        return "<div id='" + sns.type + "' style='height:" + (sns.height || 40) + "px;' ></div>";
    },
 
    set_value: function (node, value, ev, config) {
        //node - html object related to html defined above
        //value - value defined by map_to property
        //ev - event object
        scheduler.form_blocks.vault.setConfig(config.name);
        scheduler.form_blocks.vault.upload(config.name, ev.id);
    },
 
    get_value: function (node, ev) {
        //node - html object related to html defined above
        //event object
        return;
    },
 
    focus: function (node) {
        //node - html object related to html defined above
    },
 
    getVault: function () {
        if (!scheduler._vaultInst) {
            scheduler._vaultInst = new dhtmlXVaultObject({
                container: "vault",
                uploadUrl: scheduler._vaultConfig.uploadUrl
            });
            scheduler._vaultInst.setAutoStart(true);
        }        
        return scheduler._vaultInst;
    },
 
    setConfig: function (name) {
        var scs = scheduler.config.lightbox.sections;
        for (var i = 0; i < scs.length; i++) {
            if (scs[i].name == name)
                scheduler._vaultConfig = scs[i];
        }
    },
 
    getConfig: function () {
        return scheduler._vaultConfig;
    },
 
    initUploader: function (config) {
        var obj = scheduler.form_blocks["vault"];
 
        var vault = obj.getVault();
 
        /*allows uploading only of 5 files*/
        vault.setFilesLimit(config.filesLimit);
        vault.setHeight(config.Height);
        vault.setWidth(config.Width);
        vault.setSkin(config.skin);
 
        /*occurs when the user removes a single file from the list of files*/
        /*occurs when the user removes a single file from the list of files*/
        vault.attachEvent("onFileRemove", function (file) {
            window.dhx4.ajax.get(config.removeUrl + file.serverName.toString());
        });
 
        scheduler.attachEvent("onAfterLightbox", function () {
            var obj = scheduler.form_blocks["vault"];
            var vault = obj.getVault();
            if (vault) {
                if (vault.unload != null) {
                    vault.unload();
                    scheduler._vaultInst = null;
                }
            }
        });
 
        /*occurs before the user adds a file to the upload queue*/
        vault.attachEvent("onBeforeFileAdd", function (file) {
            var obj = scheduler.form_blocks["vault"];
            config = obj.getConfig();
            var ext = this.getFileExtension(file.name);
            return config.ext.indexOf(ext) >= 0 || file.size > config.sizeLimit;
        });
 
    },
 
    upload: function (name, event_id) {
        var obj = scheduler.form_blocks["vault"];
        var config = obj.getConfig();
 
        obj.initUploader(config);
        obj.changeSession(event_id);
        var vault = obj.getVault();
 
        if (!scheduler.getState().new_event) {
            if (!vault.v_container) {
                vault.v_container = document.getElementById('vault');
            } else {
                var cont = document.getElementById('vault');
                cont.parentNode.replaceChild(vault.v_container, cont);
            }
            vault.setDownloadURL(config.dwnUrl);
            /*load list of files from server*/
            vault.load(config.fileList + event_id.toString(), function () {
 
            });
        }
    },
 
    changeSession: function (event_id) {
        var obj = scheduler.form_blocks["vault"];
        var config = obj.getConfig();
        window.dhx4.ajax.get(config.session + event_id.toString());
    }
}

3. Define a component on server side

In this chapter we add a wrapper for LightboxVault panel to add this component to the Scheduler.NET. Vault is a one of the components, for which basic properties are not enough. But it’s not a problem for Scheduler.NET as far as it is extendable.

Everything you need is to add some property to your wrapper and the component will render this property in javascript.

For example, for the Vault component in this example we need the following custom properties:

  • removeUrl - functionality for removing of file
  • uploadUrl - functionality for uploading a file
  • dwnUrl - functionality for showing document in a browser
  • fileList - functionality for loading list of files to the DHTMLX Component
  • changeSession - functionality for changing of session
  • filesLimit - amount of files which can be downloaded
  • width - width of Vault component
  • sizeLimit - max size of downloaded file
  • ext - file extensions which can be downloaded
public class LightboxVault : LightboxField
{
        /// <summary>
        /// Define Vault skins
        /// </summary>
        public enum Skins
        {
            dhx_skyblue,
            dhx_web,
            dhx_terrace
        };
 
        /// <summary>
        /// Vault skin
        /// </summary>
        public Skins Skin
        {
            get
            {
                var skin = Skins.dhx_web;
                if (Enum.IsDefined(typeof(Skins), Get("skin")))
                    skin = (Skins)Enum.Parse(typeof(Skins), Get("skin"));
                return skin;
            }
            set
            {
                Set("skin", value.ToString());
            }
        }
        /// <summary>
        /// Url for upload file
        /// </summary>
        public string UploadUrl
        {
            get { return Get("uploadUrl"); }
            set { Set("uploadUrl", value); }
        }
        /// <summary>
        /// Url for removing file
        /// </summary>
        public string RemoveUrl
        {
            get { return Get("removeUrl"); }
            set { Set("removeUrl", value); }
        }
        /// <summary>
        /// Url for opening file in a browser
        /// </summary>
        public string DwnUrl
        {
            get { return Get("dwnUrl"); }
            set { Set("dwnUrl", value); }
        }
        /// <summary>
        /// Url for getting list of files
        /// </summary>
        public string FileList
        {
            get { return Get("fileList"); }
            set { Set("fileList", value); }
        }
        /// <summary>
        /// SVault session
        /// </summary>
        public string ChangeSession
        {
            get { return Get("session"); }
            set { Set("session", value); }
        }
        /// <summary>
        /// Amount of files which can be dowloaded
        /// </summary>
        public int FilesLimit
        {
            get { return Convert.ToInt32(Get("filesLimit")); }
            set { Set("filesLimit", value); }
        }
        /// <summary>
        /// Width of the component
        /// </summary>
        public int Width
        {
            get { return Convert.ToInt32(Get("width")); }
            set { Set("width", value); }
        }
        /// <summary>
        /// Max size of file which can be downloaded
        /// </summary>
        public int FileSizeLimit
        {
            get { return Convert.ToInt32(Get("sizeLimit")); }
            set { Set("sizeLimit", value); }
        }
        /// <summary>
        /// Allow extensions
        /// </summary>
        public string AllowsExt
        {
            get { return Get("ext"); }
            set { Set("ext", value); }
        }
 
        public LightboxVault(string name, string label = null)
            : base(name, label)
        {
            Height = 150;
            Type = "vault";
        }
 
        /// <summary>
        /// Determines skin and returns necessary files for this
        /// </summary>
        /// <returns>List of styles for vault component</returns>
        public override Dictionary<string, FileType> GetCSS()
        {
            string styles;
            switch (Skin)
            {
                case Skins.dhx_skyblue:
                    styles = "dhtmlxVault/skins/skyblue/dhtmlxvault.css";
                    break;
                case Skins.dhx_terrace:
                    styles = "dhtmlxVault/skins/terrace/dhtmlxvault.css";
                    break;
                default:
                    styles = "dhtmlxVault/skins/web/dhtmlxvault.css";
                    break;
 
            }
            Dictionary<string, FileType> files = new Dictionary<string, FileType>();
            files.Add("dhtmlxVault/dhtmlxvault.css", FileType.Local);
            files.Add(styles, FileType.Local);
            return files;
        }
        /// <summary>
        /// Returns necessary javascript file for the component
        /// </summary>
        /// <returns>List of javascript files for vault component</returns>
        public override Dictionary<string, FileType> GetJS()
        {
            return new Dictionary<string, FileType>() { { "dhtmlxVault/dhtmlxvault.js", FileType.Local } };
        }
}

As you see, we ovveride GetJS and GetCss methods for Vault component. It means, that all we need it’s define skin and needed files will be loaded. In our example Vault folder is inside dhtmlxScheduler folder:

 File structure

And finally, add this component to the Scheduler.NET:

….
var calendar = new DHXScheduler();
…
calendar.Lightbox.Add(new LightboxVault("vault", "")
{
      ThemeCss = "SKIN_FOLDER",
      uploadUrl = "UPLOAD_FILE",
      removeUrl = "REMOVE_FILE",
      dwnUrl = "DOWNLOAD_URL",
      fileList = "GET_FILES",
      changeSession = "CHANGE_SESSION",
      Width = 610,
      FilesLimit = 5,
      FileSizeLimit = 20000,
      AllowsExt = "EXTENSIONS"
});
...

 Lightbox with vault component

On this step we have DHTMLX Vault Component. In the next chapter we’ll add functionality for loading a list to the Vault control, removing files from vault and the ability to open files in a browser.

3.1 Define a logic for Vault component

Before defining a logic for vault component, we initialize an auxiliary class for the file manager:

public static class FileManager
{
    public static void CreateDirectory(string path)
    {
        var exists = Directory.Exists(path);
        if (!exists)
        {
            Directory.CreateDirectory(path);
        }
    }
 
    public static void RemoveFile(string name)
    {
        File.Delete(name);
    }
 
    public static string GetVPath(string param)
    {
        return string.Format("{0}{1}",
            System.Configuration.ConfigurationManager.AppSettings["UploadPath"],
            param);
    }
 
    public static string GetVPath(string p1, string p2)
    {
        return string.Format("{0}{1}/{2}",
            System.Configuration.ConfigurationManager.AppSettings["UploadPath"],
            p1, p2);
    }
 
    public static void MoveFile(string from, string to)
    {
        Directory.Move(from, to);
    }
}

Here are the methods to create a directory if it doesn't exist, remove file and return the path of file or folders.

Also, we need some classes for json responses:

public class VaultFile
{
    public bool state;
    public string name;
    public Extra extra;
}
 
public class Extra
{
    public string info;
    public string param;
}
 
public class vFileInfo
{
    public string name;
    public string serverName;
    public long size;
}

Finally, we need to initialize methods for reading the list of files, uploading and removing files. In our example this code is in LightboxUploader.Control library.

Upload

The Upload method should return json format, which has the following structure:

  • state - true/false - status of dowloading
  • name - name of the downloading file
  • extra -optional. has the following field : info - message of dowload process
public VaultFile Upload(string name, HttpPostedFile file)
{
    VaultFile vFile = null;
    try
    {
        byte[] bytes;
        using (var br = new BinaryReader(file.InputStream))
        {
            bytes = br.ReadBytes(file.ContentLength);
        }
        using (var stream = File.Open(name + "/" + file.FileName, FileMode.OpenOrCreate))
        {
            stream.Write(bytes, 0, bytes.Length);
        }
 
        vFile = new VaultFile
        {
            state = true,
            name = file.FileName,
            extra = new Extra
            {
                info = string.Format("{0} has been downloaded.", file.FileName)
            }
        };
    }
    catch (Exception exception)
    {
 
        vFile = new VaultFile
        {
            state = false,
            name = file.FileName,
            extra = new Extra
            {
                info = exception.Message
            }
        };
 
        return vFile;
    }
    return vFile;
}

Set maximum size

While DHTMLX Vault component is initializing, vault tries to set a maxFileSize from the server:

public object SetMaxFize()
{
    return new { maxFileSize = System.Configuration.ConfigurationManager.AppSettings["maxSize"] };
}

Get files list from server

To get files for the event, follow this code:

public List<vFileInfo> GetAll(string id, string folder)
{
    FileManager.CreateDirectory(folder);
    var pathFiles = Directory.GetFiles(folder);
 
    var fileInfos = new List<vFileInfo>();
 
    foreach (var path in pathFiles)
    {
        var fi = new FileInfo(path);
        fileInfos.Add(new vFileInfo()
        {
            name = fi.Name,
            serverName = fi.Name,
            size = fi.Length
        });
    }
 
    return fileInfos;
}

Where

  • id - id of the event
  • folder - path of the folder

Returned json format should have the following fields:

  • name - file name which wil be displayed
  • serverName - file name on the server
  • size - size of file

Remove file

To remove file use the following code:

public void Remove(string fname)
{
    File.Delete(fname);
}

Where

  • name - path of file

3.1.1 MVC

In web configurations we define the upload path for downloaded files:

<configuration>
	<appSettings>
		<add key="UploadPath" value="~/upload/" />
		<add key="extensions" value="pdf, jpg, jpeg, png, zip, txt" />
 	 </appSettings>
</configuration>

Also we need in the function, which will be rename folder name after changing of event id. The view will be contain the following code:

<script type="text/javascript">
    function beforeInit() {
        scheduler.attachEvent("onEventIdChange", function (old_id, new_id) {
            var params = "cur_id=" + old_id + "&new_id=" + new_id;
            window.dhx4.ajax.get("Home/ChangeFolderName?"+params);
        });
    }
</script>
 
<div style="height: 600px;">
    @Html.Raw(Model.Render())
</div>

Then we should add as extension client-side and connect the Vault Component to the scheduler calendar:

calendar.Lightbox.Add(
	new LightboxVault("vault", "")
    {
        Skin = LightboxVault.Skins.dhx_terrace,
        UploadUrl = "Vault/UploadFile/",
        RemoveUrl = "Vault/RemoveFile?filename=",
        DwnUrl = "Vault/GetFile?fileName={serverName}",
        FileList = "Vault/GetFiles?id=",
        ChangeSession = "Home/ChangeSession?id=",
        Width = 610,
        FilesLimit = 5,
        FileSizeLimit = 20000000,
        AllowsExt = ConfigurationManager.AppSettings["extensions"]
     }
);
 
calendar.Extensions.Add("../dhtmlxscheduler_vault.js");
calendar.BeforeInit.Add("beforeInit();");

For the vault component we need the following action methods:

  • UploadFile - upload file to the server
  • GetFiles - return list of files inside the Vault component
  • GetFile - returns data file for displaying it in the browser

For this purpose we use setDownloadURL method of DHTMLX Vault component. This method allows setting a link for the file inside the component.

  • RemoveFile - removes file by id

The full code of the DHTMLX Vault Controller should be as follows:

public class VaultController : Controller
{
   /*
   * UploadFile file
   */
   public JsonResult UploadFile()
   {
      var context = System.Web.HttpContext.Current;
      var vm = new VaultManager();
 
      if (context.Request.QueryString["mode"] == "conf")
      {
         return Json(vm.SetMaxFize(), JsonRequestBehavior.AllowGet);
      }
      var file = context.Request.Files["file"];
      var eventid = Session["sessionId"].ToString();
      var path = Server.MapPath(FileManager.GetVPath(eventid));
 
      FileManager.CreateDirectory(path);
 
      if (file != null)
      {
         var result = vm.Upload(path, file);
         return Json(result, JsonRequestBehavior.AllowGet);
      }
      return Json(new VaultFile()
      {
         state = false,
         extra = new Extra()
           {
              info = "File is null"
           }
       }, JsonRequestBehavior.AllowGet);
   }
 
   /*
   * Get list of files by event_id
   */
   public JsonResult GetFiles(string id)
   {
      var folder = FileManager.GetVPath(id);
      folder = Server.MapPath(folder);
 
      var vm = new VaultManager();
      var result = vm.GetAll(id, folder);
 
      return Json(result, JsonRequestBehavior.AllowGet);
    }
 
    /*
     * Open file in a browser
     */
     public void GetFile()
     {
        var context = System.Web.HttpContext.Current;
        var filename = context.Request.Params["fileName"];
        var id = Session["sessionId"].ToString();
        var folder = FileManager.GetVPath(id, filename);
        folder = VirtualPathUtility.ToAbsolute(folder);
        Response.Write("<script>");
        Response.Write("window.open('" + Request.UrlReferrer.ToString().TrimEnd('/') + folder + "', '_newtab');");
        Response.Write("</script>");
 
   }
 
   /*
    * Remove file
    */
    public void RemoveFile(string filename)
    {
       var eventId = Session["sessionId"].ToString();
       var folder = FileManager.GetVPath(eventId);
       folder = Server.MapPath(folder);
       VaultManager vm = new VaultManager();
       vm.Remove(folder + "/" + filename);
     }
}

To change the folder name after saving of event use the following code:

public void ChangeFolderName(string cur_id, string new_id)
{
    FileManager.MoveFile(Server.MapPath(FileManager.GetVPath(cur_id)),
      Server.MapPath(FileManager.GetVPath(new_id)));
}

3.1.2 Web Forms

In web configurations we define the upload path for downloaded files:

<configuration>
	<appSettings>
		<add key="UploadPath" value="~/upload/" />
		<add key="extensions" value="pdf, jpg, jpeg, png, zip, txt" />
 	 </appSettings>
</configuration>

We need to define function for renaming folder of the event after changing id. The page will contain the following code:

<script type="text/javascript">
        function beforeInit() {
            scheduler.attachEvent("onEventIdChange", function (old_id, new_id) {
                var params = "cur_id=" + old_id + "&new_id=" + new_id;
                window.dhx4.ajax.get("RenameFolder.ashx?" + params);
            });
        }
    </script>
    <div style="height: 600px">
        <%= this.scheduler.Render() %>
    </div>

Then we connect our Vault Component to the scheduler calendar:

calendar.Lightbox.Add(new LightboxVault("vault", "")
    {
        Skin = LightboxVault.Skins.dhx_terrace,
        UploadUrl = "Upload.ashx",
        RemoveUrl = "RemoveFile.ashx?fname=",
        DwnUrl = "GetFile.ashx?fileName={serverName}",
        FileList = "GetFiles.ashx?id=",
        ChangeSession = "ChangeSession.ashx?id=",
        Width = 610,
        FilesLimit = 5,
        FileSizeLimit = 20000000,
        AllowsExt = ConfigurationManager.AppSettings["extensions"].ToString()
    }
);
 
calendar.BeforeInit.Add("beforeInit();");
calendar.Extensions.Add("../dhtmlxscheduler_vault.js");

Upload

For uploading the file use following code:

public class Upload : IRequiresSessionState, IHttpHandler
{
 
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "application/json";
 
        var js = new JavaScriptSerializer();
        var vm = new VaultManager();
 
        if (context.Request.QueryString["mode"] == "conf")
        {
            context.Response.Write(js.Serialize(vm.SetMaxFize()));
        }
        else
        {
            var file = context.Request.Files["file"];
            var eventid = context.Session["sessionId"].ToString();
            var path = context.Server.MapPath(FileManager.GetVPath(eventid));
 
            FileManager.DirectoryCreate(path);
 
            if (file != null)
            {
                var result = vm.Upload(path, file);
                    context.Response.Write(js.Serialize(result));
            }
        }
 
    }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Remove file

The following code removes file by id:

public class RemoveFile : IRequiresSessionState, IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var filename = context.Request.Params["fname"];
        var eventId = context.Session["sessionId"].ToString();
        var folder = FileManager.GetVPath(eventId);
        folder = context.Server.MapPath(folder);
        VaultManager vm = new VaultManager();
        vm.Remove(folder + "/" + filename);
    }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

File list

The following code load list of files inside Vault component:

public class GetFiles : IRequiresSessionState, IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string id = context.Request.Params["id"];
        var js = new JavaScriptSerializer();
 
        var folder = FileManager.GetVPath(id);
        folder =context.Server.MapPath(folder);
 
        var vm = new VaultManager();
        var result = vm.GetAll(id, folder);
 
        context.Response.ContentType = "application/json";
        context.Response.Write(js.Serialize(result));
    }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

Rename folder

The following code renames name of the directory after changing an id:

public class RenameFolder : IHttpHandler
{
	public void ProcessRequest(HttpContext context)
    {
        var cur_id = context.Request.Params["cur_id"];
        var new_id = context.Request.Params["new_id"];
 
        FileManager.MoveFile(context.Server.MapPath(FileManager.GetVPath(cur_id)),
            context.Server.MapPath(FileManager.GetVPath(new_id)));
        }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

comments powered by Disqus