Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
<?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;
}
}