Line 174: | Line 174: | ||
XmlUtil.SetAttribute( oFileSize, "size", fileSize.ToString() ); | XmlUtil.SetAttribute( oFileSize, "size", fileSize.ToString() ); | ||
</source> | </source> | ||
+ | |||
+ | ==== Step7: Build solution ==== | ||
+ | Buils solution and copy generated .dll file to the "bin" folder as explained in [[CKFinder_2.x/Developers_Guide/ASP.NET/Plugins|Enabling ASP.NET Plugins]] and adjust the configuration file. | ||
==== Full source code ==== | ==== Full source code ==== |
Revision as of 15:28, 25 May 2010
Contents
- 1 Creating a plugin
- 2 ASP.NET plugin system
- 3 Sample plugin: FileSize (adding new server side command - complete example)
- 3.1 Introduction
- 3.2 The project
- 3.3 Step1: Register an event handler
- 3.4 Step2: Create an event handler
- 3.5 Step3: Register the "FileSize" command
- 3.6 Step4: Create a class to send the XML response
- 3.7 Step5: Create the buildXml method
- 3.8 Step6: Construct the XML response
- 3.9 Step7: Build solution
- 3.10 Full source code
The main advantages of plugins are:
- Upgrades are much easier.
- The plugin code is stored in a single place.
- Plugins can be easily disabled when they are not needed anymore.
Common use cases:
- Adding a new server-side command (i.e.
fileditor
andimageresize
plugin). - Working with uploaded files (i.e.
watermark
plugin). - Extending information returned by the
Init
command (i.e.imageresize
plugin).
Creating a plugin
Step1: Create plugin folder
Create a directory for your plugin inside of the "plugins" directory (by default CKFinder comes with three plugins: dummy, fileeditor, imagreresize). Let's use "myplugin" as the plugin name and use the same name for our new folder.
Step2: Create JavaScript plugin
Inside of your plugin's folder ("myplugin") create an empty file named plugin.js.
(If your plugin is a pure server side plugin, you may skip that step)
Step3: Create .dll file with your plugin
// Let's skip that step for now
Step4: Enable plugin
- Copy .dll file with a plugin to the "bin" folder of your application
- In config.ascx add your plugin to the Plugins list:
Plugins = new string[] { // class name, assembly name "CKFinder.Plugins.FileSize, CKFinder_FileSize" };
ASP.NET plugin system
Hooks
CKFinder provides several hooks that can be used to extend the functionality of the CKFinder application. Assigning a function (also known as an event handler) to a hook will cause that function to be called at the appropriate point in the main CKFinder code, to perform whatever additional task(s) the developer thinks would be useful at that point. Each hook can have multiple handlers assigned to it, in which case it will call the functions in the order that they are assigned.
Registering an event handler to a hook is very simple:
// MyBeforeExecuteCommandEventHandler is our custom event handler (method) CKFinderEvent.BeforeExecuteCommand += new CKFinder.Connector.CKFinderEvent.Hook( this.MyBeforeExecuteCommandEventHandler );
Available hooks (events)
Hook | Since | Description |
---|---|---|
AfterFileUpload | 2.0 | Executed after successful file upload. |
BeforeExecuteCommand | 2.0 | Executed before a server side command is executed. |
InitCommand | 2.0 | Executed straight before sending the result of the Init command. |
Enabling JavaScript plugin
Sometimes a server side plugin might come with a client side (JavaScript) plugin, that should be automatically enabled when the server side plugin is enabled.
The JavascriptPlugins
property is a special variable that does that.
To enable a plugin, simply define it's name (to add multiple plugins, separate their names with a comma).
public string JavascriptPlugins { get { return "myplugin"; } }
This code is virtually equal to specyfying in config.js:
config.extraPlugins = 'myplugin';
but the advantage of defining it in a plugin is that it will be enabled only when the server side plugin is also enabled.
Sample plugin: FileSize (adding new server side command - complete example)
Introduction
Probably the best way to learn new things is to write a code, so let's write a plugin that adds a new server side command to the connector. We'll create a command that returns a size of a given file.
The FileSize plugin should provide a new command named "FileSize", in other words, calling:
/ckfinder/core/connector/aspx/connector.aspx?command=FileSize&type=Files¤tFolder=%2F&fileName=foobar.jpg
(assuming that foobar.jpg exists) should return a valid XML response:
<Connector resourceType="Files"> <Error number="0"/> <CurrentFolder path="/" url="/ckfinder/userfiles/files/" acl="255"/> <FileSize size="5647"/> </Connector>
The project
Let's create a C# project now to generate a .dll file.
- Create a new C# project (Class Library).
- Add reference to CKFinder.dll (right-click "References" in the "Solution Explorer", use "Browse" to select CKFinder.dll from the directory you have saved it).
- In Project Properties change Assembly name to
CKFinder_FileSize
and Default namespace toCKFinder.Plugins
- Rename the default file created by VS (Class1.cs) into FileSize.cs.
The whole code will be in a single file (FileSize.cs). For the sake of simplicity, let's take a look at Filesize.cs that just does exactly what we need:
namespace CKFinder.Connector.CommandHandlers { // Since we want to send a XML response, we'll extend the XmlCommandHandlerBase class public class FileSizeCommandHandler : XmlCommandHandlerBase { public FileSizeCommandHandler() : base() {} // (5) The buildXml method is used to construct an XML response protected override void BuildXml() { string fileName = Request["FileName"]; string filePath = System.IO.Path.Combine( this.CurrentFolder.ServerPath, fileName ); long fileSize = new System.IO.FileInfo( filePath ).Length; // (6) Adding a <FileSize> element to the XML response. XmlNode oFileSize = XmlUtil.AppendElement( this.ConnectorNode, "FileSize" ); XmlUtil.SetAttribute( oFileSize, "size", fileSize.ToString() ); } } } namespace CKFinder.Plugins { public class FileSize : CKFinder.CKFinderPlugin { public string JavascriptPlugins { get { return "myplugin"; } } public void Init( CKFinder.Connector.CKFinderEvent CKFinderEvent ) { // (1) Register our custom BeforeExecuteCommand event handler (MyBeforeExecuteCommandEventHandler function). CKFinderEvent.BeforeExecuteCommand += new CKFinder.Connector.CKFinderEvent.Hook( this.MyBeforeExecuteCommandEventHandler ); } // (2) Our event handler protected void MyBeforeExecuteCommandEventHandler( object sender, CKFinder.Connector.CKFinderEventArgs args ) { String command = (String)args.data[0]; // (3) Register the "FileSize" command if ( command == "FileSize" ) { HttpResponse Response = (HttpResponse)args.data[1]; // (4) Our custom class (FileSizeCommandHandler) will do the rest CKFinder.Connector.CommandHandlers.CommandHandlerBase commandHandler = new CKFinder.Connector.CommandHandlers.FileSizeCommandHandler(); // The sendResponse method is defined in XmlCommandHandlerBase, it creates // a basic XML response and calls the buildXml()method commandHandler.SendResponse( Response ); } } } }
Step1: Register an event handler
CKFinderEvent.BeforeExecuteCommand += new CKFinder.Connector.CKFinderEvent.Hook( this.MyBeforeExecuteCommandEventHandler );
Step2: Create an event handler
protected void MyBeforeExecuteCommandEventHandler( object sender, CKFinder.Connector.CKFinderEventArgs args )
Step3: Register the "FileSize" command
The BeforeExecuteCommand
hook is used to define your own server side command
if ( command == "FileSize" ) // ...
Step4: Create a class to send the XML response
If your plugin returns an XML response, you'll usually just extend the XmlCommandHandlerBase class to reduce the amount of code to write, however it is not a requirement.
public class FileSizeCommandHandler : XmlCommandHandlerBase
Step5: Create the buildXml method
In the MyBeforeExecuteCommandEventHandler
method we have called sendResponse
. The sendResponse method is defined
in XmlCommandHandlerBase class, it creates a basic XML response and calls the buildXml
method, which we need to define.
protected override void BuildXml() // ...
Step6: Construct the XML response
The main task of the buildXml method is to construct an XML response, so we're doing it below:
XmlNode oFileSize = XmlUtil.AppendElement( this.ConnectorNode, "FileSize" ); XmlUtil.SetAttribute( oFileSize, "size", fileSize.ToString() );
Step7: Build solution
Buils solution and copy generated .dll file to the "bin" folder as explained in Enabling ASP.NET Plugins and adjust the configuration file.
Full source code
FileSize.cs
The full source code of a plugin with all necessary security checks: (save the code as FileSize.cs in your C# project)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Xml; using System.Web; using System.Web.UI; using CKFinder; namespace CKFinder.Connector.CommandHandlers { // Since we will send a XML response, we'll extend the XmlCommandHandlerBase class public class FileSizeCommandHandler : XmlCommandHandlerBase { public FileSizeCommandHandler() : base(){} // (5) The buildXml method is used to construct an XML response protected override void BuildXml() { if ( !this.CurrentFolder.CheckAcl( AccessControlRules.FileView ) ) { ConnectorException.Throw( Errors.Unauthorized ); } string fileName = Request["FileName"]; if ( !Connector.CheckFileName( fileName ) || Config.Current.CheckIsHiddenFile( fileName ) ) { ConnectorException.Throw( Errors.InvalidRequest ); return; } if ( !this.CurrentFolder.ResourceTypeInfo.CheckExtension( System.IO.Path.GetExtension( fileName ) ) ) { ConnectorException.Throw( Errors.InvalidRequest ); return; } string filePath = System.IO.Path.Combine( this.CurrentFolder.ServerPath, fileName ); if ( !System.IO.File.Exists( filePath ) ) ConnectorException.Throw( Errors.FileNotFound ); try { long fileSize = new System.IO.FileInfo( filePath ).Length; // (6) Adding a <FileSize> element to the XML response. XmlNode oFileSize = XmlUtil.AppendElement( this.ConnectorNode, "FileSize" ); XmlUtil.SetAttribute( oFileSize, "size", fileSize.ToString() ); } catch ( OutOfMemoryException ) { ConnectorException.Throw( Errors.InvalidName ); } catch ( System.UnauthorizedAccessException ) { ConnectorException.Throw( Errors.AccessDenied ); } catch ( System.Security.SecurityException ) { ConnectorException.Throw( Errors.AccessDenied ); } catch ( System.ArgumentException ) { ConnectorException.Throw( Errors.FileNotFound ); } catch ( System.IO.PathTooLongException ) { ConnectorException.Throw( Errors.FileNotFound ); } catch ( Exception ) { ConnectorException.Throw( Errors.Unknown ); } } } } namespace CKFinder.Plugins { public class FileSize : CKFinder.CKFinderPlugin { // (Optional) Register a javascript plugin named "myplugin" public string JavascriptPlugins { get { return "myplugin"; } } public void Init( CKFinder.Connector.CKFinderEvent CKFinderEvent ) { // (1) Register our custom BeforeExecuteCommand event // handler (MyBeforeExecuteCommandEventHandler function). CKFinderEvent.BeforeExecuteCommand += new CKFinder.Connector.CKFinderEvent.Hook( this.MyBeforeExecuteCommandEventHandler ); } // (2) Our event handler protected void MyBeforeExecuteCommandEventHandler( object sender, CKFinder.Connector.CKFinderEventArgs args ) { String command = (String)args.data[0]; // (3) Register the "FileSize" command if ( command == "FileSize" ) { HttpResponse Response = (HttpResponse)args.data[1]; // (4) Our custom class (FileSizeCommandHandler) will do the rest CKFinder.Connector.CommandHandlers.CommandHandlerBase commandHandler = new CKFinder.Connector.CommandHandlers.FileSizeCommandHandler(); // The sendResponse method is defined in XmlCommandHandlerBase, it creates // a basic XML response and calls the buildXml()method commandHandler.SendResponse( Response ); } } } }
plugin.js
.. and the client side (JavaScript) plugin that will call the FileSize command: (save the code as plugin.js in "plugins/myplugin" folder)
CKFinder.addPlugin( 'myplugin', function( api ) { api.addFileContextMenuOption( { label : 'File Size', command : "FileSize" } , function( api, file ) { api.connector.sendCommand( 'FileSize', { fileName : api.getSelectedFile().name }, function( xml ) { if ( xml.checkError() ) return; var size = xml.selectSingleNode( 'Connector/FileSize/@size' ); api.openMsgDialog( "", "The exact size of a file is: " + size.value + " bytes"); } ); }); });