Introduction
CKFinder uses two types of plugins:
- JavaScript plugins, used by the CKFinder frontend, which can be used to alter and extend CKFinder UI.
- ASP.NET connector plugins that can be used to change and extend the behavior of the server-side connector.
Below you can find information and examples for CKFinder ASP.NET connector plugins. For details about JavaScript plugins please refer to the Creating CKFinder 3.x JavaScript Plugins documentation.
Plugin Installation
Manual installation requires downloading plugin source and unpacking it inside the CKFinder plugins
directory. The directory structure needs to follow the following pattern:
plugins
└── PluginName
├── PluginName.dll
└── PluginDependency.dll
Configuration
After installation the plugin has to be enabled in the CKFinder configuration file. See Plugins for details.
Plugins usually offer a few configuration options that can be set in the main CKFinder configuration file. Please check plugin documentation for details.
Plugin Development
Default CKFinder behavior can be changed and extended with custom plugins. There are a few constraints that a plugin must meet in order to be recognized as valid:
- The plugin name should be unique.
- The plugin class needs to implement the IPlugin interface.
- It has to be exported using ExportAttribute.
Below is an example of a properly defined plugin:
using System.Collections.Generic;
using System.ComponentModel.Composition;
[Export(typeof(IPlugin))]
public class MyPlugin : IPlugin
{
public void Initialize(
IComponentResolver componentResolver,
IReadOnlyDictionary<string, IReadOnlyCollection<string>> options)
{
}
}
Note: A plugin may implement the IDisposable interface if it needs to clean up resources during unload.
Plugin Interface
Each CKFinder plugin has to implement the IPlugin interface:
public interface IPlugin
{
void Initialize(
IComponentResolver componentResolver,
IReadOnlyDictionary<string, IReadOnlyCollection<string>> options);
}
It has one method that is invoked at the application start. In this method you should register all your event handlers and custom commands.
Plugin Configuration
Plugins are configured in their Initialize method.
Options for plugins are passed in the ConnectorBuilder.AddPlugin method or as a collection of <option />
elements in the <plugins />
section of the configuration file.
If you are using XML configuration and need to supply multiple values for a key, just add options with the same key.
The plugin receives a dictionary with a collection of values as a parameter in the Initialize method: IReadOnlyDictionary<string, IReadOnlyCollection<string>> options
. It is up to the developer to check if options contain valid values. If an option is not provided at all, the dictionary will return an empty collection of values.
Plugin Structure Example
For example, let us assume you want to create a plugin named ImageWatermark
. The plugin should work in the following way:
- Listen for the file upload event, after the file is validated (and if it is an image — also resized), and just before it is saved.
- If the file is a supported image type, get uploaded file content and add a watermark.
- Set the watermarked image as uploaded file content.
The basic code structure of the ImageWatermark
plugin class can look as below:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using ImageProcessor;
using ImageProcessor.Common.Exceptions;
using ImageProcessor.Imaging;
[Export(typeof(IPlugin))]
public class WatermarkPlugin : IPlugin, IDisposable
{
private Image _watermarkImage;
private object _fileUploadSubscription;
private IEventAggregator _eventAggregator;
public void Initialize(
IComponentResolver componentResolver,
IReadOnlyDictionary<string, IReadOnlyCollection<string>> options)
{
var watermarkImagePath = options["watermarkPath"].First();
_watermarkImage = Image.FromFile(watermarkImagePath);
_eventAggregator = componentResolver.Resolve<IEventAggregator>();
_fileUploadSubscription = _eventAggregator.Subscribe<FileUploadEvent>(
next => async messageContext => await OnFileUpload(messageContext, next));
}
public void Dispose()
{
_eventAggregator.Unsubscribe(_fileUploadSubscription);
_watermarkImage.Dispose();
}
private async Task OnFileUpload(
MessageContext<FileUploadEvent> messageContext,
EventHandlerFunc<FileUploadEvent> next)
{
var stream = messageContext.Message.Stream;
var imageSettings = messageContext.ComponentResolver.Resolve<IImageSettings>();
var originalPosition = stream.Position;
using (var imageFactory = new ImageFactory())
{
try
{
imageFactory.Load(stream);
stream.Position = originalPosition;
}
catch (ImageFormatException)
{
await next(messageContext);
return;
}
imageFactory.Overlay(new ImageLayer
{
Image = _watermarkImage, Opacity = 50, Size = imageFactory.Image.Size
});
var format = ImageFormats.GetFormatForFile(messageContext.Message.File);
format.Quality = imageSettings.Quality;
imageFactory.Format(format);
imageFactory.Save(stream);
}
await next(messageContext);
}
}
You can find a complete working example of the ImageWatermark
plugin on GitHub.