Line 281: | Line 281: | ||
{ | { | ||
api.connector.sendCommand( 'FileSize', { fileName : api.getSelectedFile().name }, function( xml ) | 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"); | |
− | + | } ); | |
− | |||
}); | }); | ||
}); | }); | ||
</source> | </source> | ||
{{#CUSTOMTITLE:Writing PHP Plugins}} | {{#CUSTOMTITLE:Writing PHP Plugins}} |
Revision as of 21:41, 17 May 2010
CKFinder functionality can be extended with server side plugins. Althought the full source code of CKFinder server connector is available and it can be changed in any way, a much better way of enhancing CKFinder connector is to create a plugin.
The main advantages of plugins are:
- Upgrades are much easier.
- The plugin code is in a single place.
- Plugins can be easily turned off when are not needed anymore.
Common use cases:
- Adding new server side command (i.e. fileditor and imageresize plugin).
- Working with uploaded file (i.e. watermark plugin).
- Extending the information returned by the Init command (i.e. imageresize plugin).
Contents
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 plugin file
Inside of your plugin's folder ("myplugin") create an empty file named plugin.php.
Step3: Add plugin definition
// Let's create an empty PHP file for now
Step4: Enable plugin
Add an include_once/require_once statement in the PHP configuration file (config.php):
include_once "plugins/myplugin/plugin.php";
PHP 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.
Hooks should be assigned in a plugin file. The easiest way to assign a function to a hook is:
$config['Hooks']['event'][] = 'function';
However, usually you'll provide a class instance and a method name.
// Resulting function call: $object->someMethod($param1, $param2); $config['Hooks']['event'][] = array($object, 'someMethod');
Available hooks
Hook | Since | Description |
---|---|---|
AfterFileUpload | 2.0 | Executed after successfull 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 $config['Plugins']
variable is a special array that does that.
To enable a plugin, simply add it's name to this array.
$config['Plugins'][] = 'myplugin';
This code is virtually equal to specyfying in config.js:
config.extraPlugins = 'myplugin';
but the advantage of defining it in plugin.php 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/php/connector.php?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>
For the sake of simplicity, we'll first create a simple plugin.php that just does exactly what we need:
<?php // (2) Include base XML command handler require_once CKFINDER_CONNECTOR_LIB_DIR . "/CommandHandler/XmlCommandHandlerBase.php"; // Since we will send a XML response, we'll extend the XmlCommandHandlerBase class CKFinder_Connector_CommandHandler_FileSize extends CKFinder_Connector_CommandHandler_XmlCommandHandlerBase { // (4) The buildXml method is used to construct an XML response function buildXml() { $fileName = $_GET["fileName"]; $filePath = CKFinder_Connector_Utils_FileSystem::combinePaths($this->_currentFolder->getServerPath(), $fileName); // (5) Adding a <FileSize> element to the XML response. $oNode = new Ckfinder_Connector_Utils_XmlNode("FileSize"); $oNode->addAttribute("size", filesize($filePath)); $this->_connectorNode->addChild($oNode); } // (3) Register the "FileSize" command function onBeforeExecuteCommand( &$command ) { if ( $command == 'FileSize' ) { // The sendResponse method is defined in XmlCommandHandlerBase, it creates // a basic XML response and calls the buildXml()method $this->sendResponse(); // false = stop further execution. return false; } return true ; } } $CommandHandler_FileSize = new CKFinder_Connector_CommandHandler_FileSize(); // (1) Register the onBeforeExecuteCommand method to be called by the BeforeExecuteCommand hook. $config['Hooks']['BeforeExecuteCommand'][] = array($CommandHandler_FileSize, "onBeforeExecuteCommand");
Step1: Register an event handler
$config['Hooks']['BeforeExecuteCommand'][] = array($CommandHandler_FileSize, "onBeforeExecuteCommand");
Step2: Create a class
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.
// (2) Include base XML command handler require_once CKFINDER_CONNECTOR_LIB_DIR . "/CommandHandler/XmlCommandHandlerBase.php"; // Since we will send a XML response, we'll extend the XmlCommandHandlerBase class CKFinder_Connector_CommandHandler_FileSize extends CKFinder_Connector_CommandHandler_XmlCommandHandlerBase
Step3: Create an event handler
Our event handler defined in (1) is called "onBeforeExecuteCommand", so let's define such function.
function onBeforeExecuteCommand( &$command ) // ...
Step4: Create the buildXml method
In the onBeforeExecuteCommand
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.
function buildXml() // ...
Step5: Construct the XML response
The main task of the buildXml method is to construct an XML response, so we're doing it below:
$oNode = new Ckfinder_Connector_Utils_XmlNode("FileSize"); $oNode->addAttribute("size", $size); $this->_connectorNode->addChild($oNode);
Full source code
The full source code of a plugin with all necessary security checks:
<?php // A simple protection against calling this file directly. if (!defined('IN_CKFINDER')) exit; // Include base XML command handler require_once CKFINDER_CONNECTOR_LIB_DIR . "/CommandHandler/XmlCommandHandlerBase.php"; // Since we will send a XML response, we'll reuse the XmlCommandHandler class CKFinder_Connector_CommandHandler_FileSize extends CKFinder_Connector_CommandHandler_XmlCommandHandlerBase { // The buildXml method is used to construct an XML response function buildXml() { // A "must have", checking whether the connector is enabled and the basic parameters (like current folder) are safe. $this->checkConnector(); $this->checkRequest(); // Checking ACL permissions, we're just getting an information about a file, so FILE_VIEW permission seems to be ok. if (!$this->_currentFolder->checkAcl(CKFINDER_CONNECTOR_ACL_FILE_VIEW)) { $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_UNAUTHORIZED); } // Make sure we actually received a file name if (!isset($_GET["fileName"])) { $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_NAME); } $fileName = CKFinder_Connector_Utils_FileSystem::convertToFilesystemEncoding($_GET["fileName"]); $resourceTypeInfo = $this->_currentFolder->getResourceTypeConfig(); // Use the resource type configuration object to check whether the extension of a file to check is really allowed. if (!$resourceTypeInfo->checkExtension($fileName)) { $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_EXTENSION); } // Make sure that the file name is really ok and has not been sent by a hacker if (!CKFinder_Connector_Utils_FileSystem::checkFileName($fileName) || $resourceTypeInfo->checkIsHiddenFile($fileName)) { $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_INVALID_REQUEST); } $filePath = CKFinder_Connector_Utils_FileSystem::combinePaths($this->_currentFolder->getServerPath(), $fileName); if (!file_exists($filePath) || !is_file($filePath)) { $this->_errorHandler->throwError(CKFINDER_CONNECTOR_ERROR_FILE_NOT_FOUND); } $size = filesize($filePath); // *** The main part of this plugin **** // Adding a <FileSize> element to the XML response. $oNode = new Ckfinder_Connector_Utils_XmlNode("FileSize"); $oNode->addAttribute("size", $size); $this->_connectorNode->addChild($oNode); } // Register the "FileSize" command function onBeforeExecuteCommand( &$command ) { if ( $command == 'FileSize' ) { // The sendResponse method is defined in XmlCommandHandlerBase, it creates // a basic XML response and calls the buildXml()method $this->sendResponse(); // false = stop further execution. return false; } return true ; } } $CommandHandler_FileSize = new CKFinder_Connector_CommandHandler_FileSize(); // Register the onBeforeExecuteCommand method to be called by the BeforeExecuteCommand hook. $config['Hooks']['BeforeExecuteCommand'][] = array($CommandHandler_FileSize, "onBeforeExecuteCommand"); // (Optional) Register a javascript plugin named "myplugin" $config['Plugins'][] = 'myplugin';
.. and the client side (JavaScript) plugin that will call the FileSize command: (save the code as plugin.js in "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"); } ); }); });