Commit 14b92488 authored by sun's avatar sun
Browse files

Initial commit of wysiwyg module skeleton. Heavy documentation. Lots of...

Initial commit of wysiwyg module skeleton. Heavy documentation. Lots of considerations. Everyone welcome to join discussions at http://drupal.org/project/wysiwyg
parents
<?php
// $Id$
/**
* @file
* Implements a WYSIWYG API/framework/controller for Drupal.
* Implements a generic WYSIWYG editor (wrapper) module for Drupal.
*/
/**
* Implementation of hook_perm().
*
* Considerations:
* - Allow to access an editor.
* - Allow to access a particular editor profile.
*/
function wysiwyg_perm() {
// Implement common permissions.
$permissions = array('use wysiwyg editor', 'administer wysiwyg settings');
// Fetch editors or editor profiles.
return $permissions;
}
/**
* Implementation of hook_menu().
*/
function wysiwyg_menu($may_cache) {
$items = array();
if ($may_cache) {
// General wysiwyg settings, including editor selection, editor/user roles
// mapping.
// Editor profile setup and custom wysiwyg controller overrides.
// Note: Depends on resulting implementation of wysiwyg_controller()
// Enable/disable default editor plugins and Drupal plugins.
// Configure editor-specific layout, including layout of plugins.
// Generic Drupal plugin wrapper callback (AJAX).
}
return $items;
}
/**
* Handle an editor profile.
*
* This function should adopt the implementation of node objects in Drupal. It
* should at least allow to load, save and duplicate an editor profile. Since
* not all editors support the same settings, the array of settings probably
* needs to be stored serialized in the database.
*
* Considerations:
* - Will Drupal editor plugins need to hook into profile operations?
* - Discuss moving profile rules into render.module.
*
* @param string $op
* An operation to perform, one of 'load', 'save', 'duplicate'.
* @param string $profile
* An editor profile object containing at least an id, title, array of plugins
* and an array of settings.
*
* @todo '_profile' is an ambigious hook in Drupal. Needs a unique name.
*/
function wysiwyg_profile() {
}
/**
* Wysiwyg editor execution controller.
*
* Disables an editor for certain fields that were never intended to be edited
* with an editor.
* @see http://drupal.org/node/81297
*
* Considerations:
* - Take D6 FAPI3 / widgets into account (see #138706 and #86535).
* - Run registration either in hook_elements() or hook_form_alter().
* form_alter() might also allow to load needed JS/CSS.
* - Use an attribute or class name or both (editor code analysis needed). Or
* alter field #type 'textarea' to 'html' (IIRC, suggested by moshe).
* - Disable an editor for fields using the PHP input format.
* - D6: Perhaps depend on the input format to *enable* a field.
* - Allow to take the current path into account.
* - Allow to force an editor for certain CCK fields (f.e. user input).
* - Disable core's #resizable if an editor does not support it.
* - Allow contrib modules to extend this definition.
* - Allow to manually extend this list via wysiwyg settings page.
* - Include an editor profile or user role to limit/enhance available plugins.
* - Take other input formats (e.g. bbcode) into account.
*
* @todo Rename function to appropriate/chosen Drupal hook.
*/
function wysiwyg_controller() {
}
/**
* hook_wysiwyg_plugin(). Return an array of editor plugins.
*
* Each wysiwyg editor as well as each contrib module implementing an editor
* plugin has to return an associative array of available plugins. Each module
* can add one or more plugins and editor buttons.
*
* Notes for TinyMCE:
* A module is able to override almost all TinyMCE initialization settings.
* However, modules should only make use of that if a plugin really needs to,
* because customized configuration settings may clash with overrides by another
* module. TinyMCE automatically assigns the baseURL of your plugin to the plugin
* object. If you need to load or access additional files from your plugin
* directory, retrieve the path via this.baseURL. tinyMCE.baseURL returns the
* path of TinyMCE and not your module. For example:
* @code
* initInstance: function(inst) {
* tinyMCE.importCSS(inst.getDoc(), this.baseURL + '/myplugin.css');
* },
* @endcode
*
* @param string $editor
* An (lowercase) editor name to return plugins for.
* @return array
* An associative array having internal plugin names as keys, an array of
* plugin meta-information as values:
* - type: 'external' (optional); if omitted, wysiwyg editors will likely
* search for the plugin in their own plugins folder.
* - title: A human readable title of the plugin.
* - description: A (one-line) description of the plugin.
* - path: The patch to the javascript plugin.
* - callback: A Drupal menu callback returning the plugin UI. A plugin
* should return a callback *or* a path.
* - icon: An icon (usually 16x16 pixels) for the plugin button (optional).
* - ... Any other custom editor settings (optional).
*
* @todo Move this template into hooks.php.
*/
function hook_wysiwyg_plugin($editor) {
switch ($editor) {
case 'tinymce':
return array(
'myplugin' => array(
'type' => 'external',
'title' => t('My plugin title'),
'description' => t('My plugin title'),
// Regular callback URL for external TinyMCE plugins.
'path' => drupal_get_path('module', 'mymodule') .'/myplugin',
// Wysiwyg wrapper plugin AJAX callback.
'callback' => url('myplugin/browse'),
'icon' => drupal_get_path('module', 'mymodule') .'/myplugin/myplugin.png',
'extended_valid_elements' => array('tag[attribute1|attribute2=default_value]'),
// Might need to be set later on; after retrieving customized editor
// layout.
'theme_advanced_buttons1' => array(t('Button title (optional)') => 'myplugin'),
),
);
}
}
/**
* Implementation of hook_wysiwyg_plugin().
*
* Implements a generic wrapper plugin for Drupal (module-based) editor plugins.
*
* Considerations:
* - By comparing the javascript of available editor plugins, most of them are
* based on img_assist.
* - An editor plugin basically consists of a title, icon (button), description
* (localized) and menu path returning the actual Drupal module. This
* meta-information is available via hook_wysiwyg_plugin().
* - Each editor implements its own plugin API, but the JS-PHP-JS communication
* is always the same.
* - Since Drupal 5, jQuery is always available and allows to perform AJAX/HTTP
* requests at any time.
* - Each editor plugin returns HTML via Javascript to the editor.
* - Do we really require each Drupal editor plugin to write their own
* javascript or are we able to supply Drupal editor plugins to all editors
* through a wrapper module?
* - We can transform a regular editor plugin template into a wrapper plugin or
* we can use a Drupal menu path to serve a editor plugin template which
* (optionally) already has additional plugin code injected.
*/
function wysiwyg_wysiwyg_plugin($editor) {
switch ($editor) {
case 'tinymce':
// Define generic plugin properties.
$path = drupal_get_path('module', 'wysiwyg') .'/wrapper/tinymce'
// Load all Drupal editor plugins into an array.
$plugins = module_invoke_all('wysiwyg_plugin', $editor);
// Define all Drupal editor plugins by looping through the array,
// omitting all non-'external' plugins; assigning type, title, icon,
// custom editor settings and most important:
// wrapper plugin path followed by callback path (if JS: use query string).
return $plugins;
}
}
/**
* Return an array of editor plugins.
*
* @param string $op
* A performed action:
* - 'list': Plugins are about to be listed or registered.
* - 'load': Plugins are about to be loaded.
* @param string $editor
* An (lowercase) editor name to retrieve plugins for.
*
* @see hook_wysiwyg_plugin()
*
* @todo Implement TinyMCE-specific code as hook into wysiwyg_tinymce.inc.
*/
function wysiwyg_get_plugins($op, $editor) {
static $plugins;
if (!isset($plugins)) {
$plugins = module_invoke_all('wysiwyg_plugin', $editor);
}
switch ($editor) {
case 'tinymce':
if ($op == 'list') {
// Do not alter cached array.
$plugin_list = $plugins;
foreach ($plugin_list as $name => $plugin) {
if ($plugin['type'] == 'external') {
$plugin_list[$name] = _wysiwyg_plugin_name('add', $name);
}
}
return $plugin_list;
}
else if ($op == 'load') {
static $plugins_added;
if (!isset($plugins_added)) {
$plugins_added = TRUE;
$init_plugins = '';
foreach ($plugins as $name => $plugin) {
// Ensure there is no leading hiven in external plugin names.
$name = _wysiwyg_plugin_name('remove', $name);
$init_plugins .= "tinyMCE.loadPlugin('$name', '". base_path() . $plugin['path'] ."');\n";
}
if (!empty($init_plugins)) {
drupal_add_js($init_plugins, 'inline');
}
}
}
break;
}
}
/**
* Add or remove leading hiven to/of (external) plugin names.
*
* Externally loaded TinyMCE plugins need to be prefixed with a hiven. TinyMCE
* will not try to add and load external plugins from the default plugins
* folder.
*
* @param string $op
* Operation to perform, 'add' or 'remove'.
* @param string $editor
* An editor name.
* @param string $name
* A plugin name.
*
* @todo Implement TinyMCE-specific code as hook into wysiwyg_tinymce.inc.
*/
function _wysiwyg_plugin_name($op, $editor, $name) {
switch ($editor) {
case 'tinymce':
if ($op == 'add') {
if (strpos($name, '-') !== 0) {
return '-'. $name;
}
return $name;
}
else {
if (strpos($name, '-') === 0) {
return substr($name, 1);
}
return $name;
}
break;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment