DRUPAL CKEDITOR AUTOCOMPLETE PLUGIN
CKEDITOR
Ckeditor is a contributed module in drupal providing more html markups to the existing text editor in drupal. It has more plugins/styles compared to the drupal default rich text editor.
Ckeditor allows us to create any number of profiles which can be used for a text editor. The plugins/buttons for the profile can be customized inorder to differentiate the profiles.
CKEDITOR PLUGINS
Ckeditor allows to create plugins for extending the functionality of ckeditor and helping the editors for easy insertion of codes into the editor.
I am elaborating with an example.
I have a field "Description" field for a content type "Article" and i want the ability to embed videos in the description. Video is a content type in drupal and contents of video can be embedded into the description field of article. The videos should populate while user is typing anything on the ckeditor plugin (autocomplete functionality).
Imagine my video is present in a text field "field_video" in video content which stores the youtube video paths.
I am writing a module "mymodule" for this functionality.
Step 1:
We need to hook for registering the plugin in the ckeditor using hook_ckeditor_plugin().
/**
* Implements hook_ckeditor_plugin().
*/
function mymodule_ckeditor_plugin() {
return array(
'videoembed' => array(
'name' => 'video_embed',
'path' => drupal_get_path('module', 'mymodule') . '/plugins/video_embed',
'desc' => 'Plugin to insert videos in drupal',
'load' => TRUE,
)
)
}
Step 2:
Register a path for autcomplete in drupal.
/**
* Implementation of hook_menu().
*/
function mymodule_menu() {
$items['mymodule/autocomplete'] = array(
'page callback' => 'mymodule_autocomplete',
'access callback' => 'user_is_logged_in',
'type' => MENU_CALLBACK,
);
return $items;
}
The access callback is set for logged in users. The access callback definition should contain the logic for fetching all the videos. You can use a db_query or entity field query for fetching all the videos. $matches variable should contain the key value pairs for the autocompletion of video informations.
I have written the logic to fetch videos based on titles.
Step 2.1
Define the autcomplete function.
function mymodule_autocomplete($string = '') {
$matches = array();
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'article')
->propertyCondition('status', NODE_PUBLISHED)
->fieldCondition('field_video', 'value', 'NULL', '!=')
->propertyCondition('title', "%".$string."%","LIKE");
->range(0,10);
$results = $query->execute();
if (!empty($results['node'])) {
$nodes = $results['node'];
foreach ($nodes as $key => $node) {
$current_node = node_load($key);
$headline = $current_node->title;
$youtube = $current_node->field_video['und'][0]['value'];
$matches[$youtube] = '<div class="my-autcomplete-class">'. $headline. '-' . $key .'</div>';
}
}
drupal_json_output($matches);
}
Step 3
Implement the hook_element_info_alter for adding the js after the form has rendered.
/**
* Implementation of hook_element_info_alter().
*/
function mymodule_element_info_alter(&$type) {
$new_article = FALSE;
if ((!is_null(arg(0)) && arg(0) == 'node') && (!is_null(arg(2)) && arg(2) == 'edit')) {
$node = menu_get_object();
}
if ((!is_null(arg(1)) && arg(1) == 'add') && (!is_null(arg(2)) && arg(2) == 'article')) {
$new_article = TRUE;
}
if ((isset($node->type) && $node->type == 'article') || ($new_article)) {
$type['form']['#post_render'][] = 'mymodule_form_post_render';
}
}
function mymodule_form_post_render($content, $element) {
static $added;
if (!isset($added) && ($js = drupal_add_js()) && isset($js['settings']['data'])) {
$settings = call_user_func_array('array_merge_recursive', $js['settings']['data']);
if (isset($settings['ckeditor']) || isset($settings['wysiwyg']['configs']['ckeditor'])) {
$added = TRUE;
drupal_add_js('misc/autocomplete.js');
drupal_add_js(array('ckeditor_properties' => array(
'module_path' => base_path() . drupal_get_path('module', 'mymodule'),
'autocomplete_path' => url('mymodule/autocomplete'),
)), 'setting');
}
}
return $content;
}
Code Explanation for step 3
1. The code is only needed in edit pages.
2. "mymodule_form_post_render" will include the required js. Autcomplete path is passed to the drupal settings so that it can be retrieved in the js.
Code Explanation for step 3
1. The code is only needed in edit pages.
2. "mymodule_form_post_render" will include the required js. Autcomplete path is passed to the drupal settings so that it can be retrieved in the js.
step4
Implement the plugin.
Now the plugin code should be written in file path /plugins/video_embed/plugin.js .
Plugin.js
(function () {
// Handle the autocomplete functionality of the ckeditor.
// Handle the autocomplete functionality of the ckeditor.
var initAutocomplete = function(input, uri) {
input.setAttribute('autocomplete', 'OFF');
new Drupal.jsAC(jQuery(input), new Drupal.ACDB(uri));
};
// Plugin for inserting video embed code.
CKEDITOR.plugins.add('videoembed', {
init : function(editor) {
// Register the dialog. The actual dialog definition is below.
CKEDITOR.dialog.add('videoembed_dialog', videoDialogDefinition);
// Register the commands.
editor.addCommand('insertvideo', new CKEDITOR.dialogCommand('videoembed_dialog'));
// Register the toolbar buttons.
editor.ui.addButton('Video', {
label : 'Add Videos into the editor',
icon : this.path + '/logo.png',
command : 'insertvideo'
});
}
});
// dialog specifications.
var videoDialogDefinition = function(editor) {
var dialogDefinition = {
title : 'Video embed',
minWidth : 390,
minHeight : 130,
contents : [
{
id : '',
label : 'Settings',
title : 'Settings',
expand : true,
padding : 0,
elements : [
{
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dialog.definition.vbox.html
type : 'vbox',
id: 'drupalOptions',
children : [
{
// Div width.
// http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dialog.definition.textInput.html
type : 'text',
id: 'video_embed',
label : 'Video Embed (Autocomplete)',
style : 'margin-top:5px;',
onLoad: function() {
this.getInputElement().addClass('form-autocomplete');
initAutocomplete(this.getInputElement().$, Drupal.settings.ckeditor_properties.autocomplete_path);
},
validate : function() {
},
commit: commitValue
}
]
}
]
}
],
// Add the standard OK and Cancel Buttons
buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ],
// A "submit" handler for when the OK button is clicked.
onOk : function() {
var data = {};
// Commit the field data to our data object
// This function calls the commit function of each field element
this.commitContent(data);
if (data.info) {
data.info.code = '[Video:' + data.info.video_embed + ']';
editor.insertHtml(CKEDITOR.tools.trim(data.info.code) + '<p> </p>');
}
}
};
return dialogDefinition;
};
// Little helper function to commit field data to an object that is passed in:
var commitValue = function(data) {
var id = this.id;
if (!data.info)
data.info = {};
data.info[id] = this.getValue();
};
})();
Code explanation for step4
1. Drupal.jsAC constructs an autocomplete object. The parameter is the input to the dialogue.
Reference : http://drupalapi.attiks.com/api/js-samples/autocomplete.js/class/Drupal.jsAC/7
2. Drupal.ACDB represents an autocomplete database object. The parameter is the uri of the autocomplete
Reference : http://drupalapi.attiks.com/api/js-samples/autocomplete.js/function/Drupal.ACDB/7
3. on Load event adds a class "form-autocomplete" for populating the results.
4. initAutocomplete function will be invoked with parameters input and autocomplete path.
Before using this plugin, you need to enable the new plugin in your ckeditor profile.Once enabled, the autcomplete plugin will work for the text editor which is using the ckeditor profile!!!!!!!!