1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* Provide a tool for creating UIs for exportable objects.
|
6
|
*
|
7
|
* See Advanced Help for documentation.
|
8
|
*/
|
9
|
|
10
|
/**
|
11
|
* Process an export-ui plugin to provide it with defaults.
|
12
|
*/
|
13
|
function ctools_export_ui_process(&$plugin, $info) {
|
14
|
ctools_include('export');
|
15
|
|
16
|
$plugin += array(
|
17
|
'has menu' => TRUE,
|
18
|
'title' => $plugin['name'],
|
19
|
'export' => array(),
|
20
|
'allowed operations' => array(),
|
21
|
'menu' => array(),
|
22
|
'redirect' => array(),
|
23
|
'form' => array(),
|
24
|
'strings' => array(),
|
25
|
'list' => NULL,
|
26
|
'access' => 'administer site configuration',
|
27
|
);
|
28
|
|
29
|
// Provide CRUD access defaults based on the base 'access' setting:
|
30
|
$plugin += array(
|
31
|
'create access' => $plugin['access'],
|
32
|
'delete access' => $plugin['access'],
|
33
|
);
|
34
|
|
35
|
if (empty($plugin['has menu'])) {
|
36
|
return;
|
37
|
}
|
38
|
|
39
|
// The following keys are required and the plugin cannot be processed
|
40
|
// without them.
|
41
|
$keys = array(
|
42
|
'title singular',
|
43
|
'title plural',
|
44
|
'title singular proper',
|
45
|
'title plural proper',
|
46
|
'schema',
|
47
|
);
|
48
|
|
49
|
foreach ($keys as $key) {
|
50
|
if (empty($plugin[$key])) {
|
51
|
drupal_set_message(t('The plugin definition of @plugin is missing the %key key.', array('%key' => $key, '@plugin' => $plugin['name'])), 'error');
|
52
|
}
|
53
|
}
|
54
|
|
55
|
// If we're on the modules page and building a menu, there is a design flaw
|
56
|
// in Drupal core that causes modules to be installed but the schema does
|
57
|
// not become available until AFTER menu rebuild. This helps smooth that
|
58
|
// out. This is a HACK but it should work:
|
59
|
$schema = ctools_export_get_schema($plugin['schema']);
|
60
|
|
61
|
if (empty($schema)) {
|
62
|
// If we're updating the schema may not have been read yet, so don't report this error in that case.
|
63
|
if (!defined('MAINTENANCE_MODE')) {
|
64
|
drupal_set_message(t('The plugin definition of @plugin cannot locate schema %schema.', array('%schema' => $plugin['schema'], '@plugin' => $plugin['name'])), 'error');
|
65
|
}
|
66
|
return;
|
67
|
}
|
68
|
|
69
|
if (empty($schema['export'])) {
|
70
|
drupal_set_message(t('The plugin definition of @plugin uses %schema, but it has no export section.', array('%schema' => $plugin['schema'], '@plugin' => $plugin['name'])), 'error');
|
71
|
return;
|
72
|
}
|
73
|
$plugin['export'] += $schema['export'];
|
74
|
|
75
|
$plugin['export'] += array(
|
76
|
// Add the identifier key from the schema so we don't have to call
|
77
|
// ctools_export_get_schema() just for that.
|
78
|
'key' => $schema['export']['key'],
|
79
|
);
|
80
|
|
81
|
// Add some default fields that appear often in exports
|
82
|
// If these use different keys they can easily be specified in the
|
83
|
// $plugin.
|
84
|
if (empty($plugin['export']['admin_title']) && !empty($schema['fields']['admin_title'])) {
|
85
|
$plugin['export']['admin_title'] = 'admin_title';
|
86
|
}
|
87
|
if (empty($plugin['export']['admin_description']) && !empty($schema['fields']['admin_description'])) {
|
88
|
$plugin['export']['admin_description'] = 'admin_description';
|
89
|
}
|
90
|
|
91
|
// Define allowed operations, and the name of the operations.
|
92
|
$plugin['allowed operations'] += array(
|
93
|
'edit' => array('title' => t('Edit')),
|
94
|
'enable' => array('title' => t('Enable'), 'ajax' => TRUE, 'token' => TRUE),
|
95
|
'disable' => array('title' => t('Disable'), 'ajax' => TRUE, 'token' => TRUE),
|
96
|
'revert' => array('title' => t('Revert')),
|
97
|
'delete' => array('title' => t('Delete')),
|
98
|
'clone' => array('title' => t('Clone')),
|
99
|
'import' => array('title' => t('Import')),
|
100
|
'export' => array('title' => t('Export')),
|
101
|
);
|
102
|
|
103
|
$plugin['menu'] += array(
|
104
|
'menu item' => str_replace(' ', '-', $plugin['name']),
|
105
|
'menu prefix' => 'admin/structure',
|
106
|
'menu title' => $plugin['title'],
|
107
|
'menu description' => '',
|
108
|
);
|
109
|
$base_path = ctools_export_ui_plugin_base_path($plugin);
|
110
|
$prefix_count = count(explode('/', $plugin['menu']['menu prefix']));
|
111
|
|
112
|
$plugin['menu'] += array(
|
113
|
// Default menu items that should be declared.
|
114
|
'items' => array(),
|
115
|
);
|
116
|
|
117
|
$plugin['menu']['items'] += array(
|
118
|
'list callback' => array(),
|
119
|
'list' => array(),
|
120
|
'add' => array(),
|
121
|
'edit callback' => array(),
|
122
|
'edit' => array(),
|
123
|
);
|
124
|
|
125
|
$plugin['menu']['items']['list callback'] += array(
|
126
|
'path' => '',
|
127
|
// Menu items are translated by the menu system.
|
128
|
// TODO: We need more flexibility in title. The title of the admin page
|
129
|
// is not necessarily the title of the object, plus we need
|
130
|
// plural, singular, proper, not proper, etc.
|
131
|
'title' => $plugin['menu']['menu title'],
|
132
|
'description' => $plugin['menu']['menu description'],
|
133
|
'page callback' => 'ctools_export_ui_switcher_page',
|
134
|
'page arguments' => array($plugin['name'], 'list'),
|
135
|
'access callback' => 'ctools_export_ui_task_access',
|
136
|
'access arguments' => array($plugin['name'], 'list'),
|
137
|
'type' => MENU_NORMAL_ITEM,
|
138
|
);
|
139
|
|
140
|
$plugin['menu']['items']['list'] += array(
|
141
|
'path' => 'list',
|
142
|
'title' => 'List',
|
143
|
'page callback' => 'ctools_export_ui_switcher_page',
|
144
|
'page arguments' => array($plugin['name'], 'list'),
|
145
|
'access callback' => 'ctools_export_ui_task_access',
|
146
|
'access arguments' => array($plugin['name'], 'list'),
|
147
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
148
|
'weight' => -10,
|
149
|
);
|
150
|
|
151
|
$plugin['menu']['items']['add'] += array(
|
152
|
'path' => 'add',
|
153
|
'title' => 'Add',
|
154
|
'page callback' => 'ctools_export_ui_switcher_page',
|
155
|
'page arguments' => array($plugin['name'], 'add'),
|
156
|
'access callback' => 'ctools_export_ui_task_access',
|
157
|
'access arguments' => array($plugin['name'], 'add'),
|
158
|
'type' => MENU_LOCAL_ACTION,
|
159
|
);
|
160
|
|
161
|
$plugin['menu']['items']['edit callback'] += array(
|
162
|
'path' => 'list/%ctools_export_ui',
|
163
|
'page callback' => 'ctools_export_ui_switcher_page',
|
164
|
'page arguments' => array($plugin['name'], 'edit', $prefix_count + 2),
|
165
|
'load arguments' => array($plugin['name']),
|
166
|
'access callback' => 'ctools_export_ui_task_access',
|
167
|
'access arguments' => array($plugin['name'], 'edit', $prefix_count + 2),
|
168
|
'type' => MENU_CALLBACK,
|
169
|
);
|
170
|
|
171
|
$plugin['menu']['items']['edit'] += array(
|
172
|
'path' => 'list/%ctools_export_ui/edit',
|
173
|
'title' => 'Edit',
|
174
|
'page callback' => 'ctools_export_ui_switcher_page',
|
175
|
'page arguments' => array($plugin['name'], 'edit', $prefix_count + 2),
|
176
|
'load arguments' => array($plugin['name']),
|
177
|
'access callback' => 'ctools_export_ui_task_access',
|
178
|
'access arguments' => array($plugin['name'], 'edit', $prefix_count + 2),
|
179
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
180
|
'weight' => -10,
|
181
|
);
|
182
|
|
183
|
if ($plugin['allowed operations']['import']) {
|
184
|
$plugin['menu']['items'] += array('import' => array());
|
185
|
$plugin['menu']['items']['import'] += array(
|
186
|
'path' => 'import',
|
187
|
'title' => 'Import',
|
188
|
'page callback' => 'ctools_export_ui_switcher_page',
|
189
|
'page arguments' => array($plugin['name'], 'import'),
|
190
|
'access callback' => 'ctools_export_ui_task_access',
|
191
|
'access arguments' => array($plugin['name'], 'import'),
|
192
|
'type' => MENU_LOCAL_ACTION,
|
193
|
);
|
194
|
}
|
195
|
|
196
|
if ($plugin['allowed operations']['export']) {
|
197
|
$plugin['menu']['items'] += array('export' => array());
|
198
|
$plugin['menu']['items']['export'] += array(
|
199
|
'path' => 'list/%ctools_export_ui/export',
|
200
|
'title' => 'Export',
|
201
|
'page callback' => 'ctools_export_ui_switcher_page',
|
202
|
'page arguments' => array($plugin['name'], 'export', $prefix_count + 2),
|
203
|
'load arguments' => array($plugin['name']),
|
204
|
'access callback' => 'ctools_export_ui_task_access',
|
205
|
'access arguments' => array($plugin['name'], 'export', $prefix_count + 2),
|
206
|
'type' => MENU_LOCAL_TASK,
|
207
|
);
|
208
|
}
|
209
|
|
210
|
if ($plugin['allowed operations']['revert']) {
|
211
|
$plugin['menu']['items'] += array('revert' => array());
|
212
|
$plugin['menu']['items']['revert'] += array(
|
213
|
'path' => 'list/%ctools_export_ui/revert',
|
214
|
'title' => 'Revert',
|
215
|
'page callback' => 'ctools_export_ui_switcher_page',
|
216
|
// Note: Yes, 'delete' op is correct.
|
217
|
'page arguments' => array($plugin['name'], 'delete', $prefix_count + 2),
|
218
|
'load arguments' => array($plugin['name']),
|
219
|
'access callback' => 'ctools_export_ui_task_access',
|
220
|
'access arguments' => array($plugin['name'], 'revert', $prefix_count + 2),
|
221
|
'type' => MENU_CALLBACK,
|
222
|
);
|
223
|
}
|
224
|
|
225
|
if ($plugin['allowed operations']['delete']) {
|
226
|
$plugin['menu']['items'] += array('delete' => array());
|
227
|
$plugin['menu']['items']['delete'] += array(
|
228
|
'path' => 'list/%ctools_export_ui/delete',
|
229
|
'title' => 'Delete',
|
230
|
'page callback' => 'ctools_export_ui_switcher_page',
|
231
|
'page arguments' => array($plugin['name'], 'delete', $prefix_count + 2),
|
232
|
'load arguments' => array($plugin['name']),
|
233
|
'access callback' => 'ctools_export_ui_task_access',
|
234
|
'access arguments' => array($plugin['name'], 'delete', $prefix_count + 2),
|
235
|
'type' => MENU_CALLBACK,
|
236
|
);
|
237
|
}
|
238
|
|
239
|
if ($plugin['allowed operations']['clone']) {
|
240
|
$plugin['menu']['items'] += array('clone' => array());
|
241
|
$plugin['menu']['items']['clone'] += array(
|
242
|
'path' => 'list/%ctools_export_ui/clone',
|
243
|
'title' => 'Clone',
|
244
|
'page callback' => 'ctools_export_ui_switcher_page',
|
245
|
'page arguments' => array($plugin['name'], 'clone', $prefix_count + 2),
|
246
|
'load arguments' => array($plugin['name']),
|
247
|
'access callback' => 'ctools_export_ui_task_access',
|
248
|
'access arguments' => array($plugin['name'], 'clone', $prefix_count + 2),
|
249
|
'type' => MENU_CALLBACK,
|
250
|
);
|
251
|
}
|
252
|
|
253
|
if ($plugin['allowed operations']['enable']) {
|
254
|
$plugin['menu']['items'] += array('enable' => array());
|
255
|
$plugin['menu']['items']['enable'] += array(
|
256
|
'path' => 'list/%ctools_export_ui/enable',
|
257
|
'title' => 'Enable',
|
258
|
'page callback' => 'ctools_export_ui_switcher_page',
|
259
|
'page arguments' => array($plugin['name'], 'enable', $prefix_count + 2),
|
260
|
'load arguments' => array($plugin['name']),
|
261
|
'access callback' => 'ctools_export_ui_task_access',
|
262
|
'access arguments' => array($plugin['name'], 'enable', $prefix_count + 2),
|
263
|
'type' => MENU_CALLBACK,
|
264
|
);
|
265
|
}
|
266
|
|
267
|
if ($plugin['allowed operations']['disable']) {
|
268
|
$plugin['menu']['items'] += array('disable' => array());
|
269
|
$plugin['menu']['items']['disable'] += array(
|
270
|
'path' => 'list/%ctools_export_ui/disable',
|
271
|
'title' => 'Disable',
|
272
|
'page callback' => 'ctools_export_ui_switcher_page',
|
273
|
'page arguments' => array($plugin['name'], 'disable', $prefix_count + 2),
|
274
|
'load arguments' => array($plugin['name']),
|
275
|
'access callback' => 'ctools_export_ui_task_access',
|
276
|
'access arguments' => array($plugin['name'], 'disable', $prefix_count + 2),
|
277
|
'type' => MENU_CALLBACK,
|
278
|
);
|
279
|
}
|
280
|
|
281
|
// Define some redirects that should happen after edit/add/clone/delete operations.
|
282
|
$plugin['redirect'] += array(
|
283
|
'add' => $base_path,
|
284
|
'clone' => $base_path,
|
285
|
'edit' => $base_path,
|
286
|
'delete' => $base_path,
|
287
|
'revert' => $base_path,
|
288
|
'import' => $base_path,
|
289
|
);
|
290
|
|
291
|
// Define form elements.
|
292
|
$plugin['form'] += array(
|
293
|
'settings' => function_exists($plugin['name'] . '_form') ? $plugin['name'] . '_form' : '',
|
294
|
'validate' => function_exists($plugin['name'] . '_form_validate') ? $plugin['name'] . '_form_validate' : '',
|
295
|
'submit' => function_exists($plugin['name'] . '_form_submit') ? $plugin['name'] . '_form_submit' : '',
|
296
|
);
|
297
|
|
298
|
// Define strings.
|
299
|
// For all strings, %title may be filled in at a later time via str_replace
|
300
|
// since we do not know the title now.
|
301
|
$plugin['strings'] += array(
|
302
|
'title' => array(),
|
303
|
'confirmation' => array(),
|
304
|
'help' => array(),
|
305
|
'message' => array(),
|
306
|
);
|
307
|
|
308
|
// Strings used in drupal_set_title().
|
309
|
$plugin['strings']['title'] += array(
|
310
|
'add' => t('Add a new @plugin', array('@plugin' => $plugin['title singular'])),
|
311
|
// The "%title" will be replaced in ctools_export_ui_form(), as in this
|
312
|
// stage we dont have the specific exportable object.
|
313
|
'edit' => t('Edit @plugin %title', array('@plugin' => $plugin['title singular'])),
|
314
|
'clone' => t('Clone @plugin %title', array('@plugin' => $plugin['title singular'])),
|
315
|
|
316
|
'import' => t('Import @plugin', array('@plugin' => $plugin['title singular'])),
|
317
|
'export' => t('Export @plugin %title', array('@plugin' => $plugin['title singular'])),
|
318
|
);
|
319
|
|
320
|
// Strings used in confirmation pages.
|
321
|
$plugin['strings']['confirmation'] += array(
|
322
|
'revert' => array(),
|
323
|
'delete' => array(),
|
324
|
'add' => array(),
|
325
|
'edit' => array(),
|
326
|
);
|
327
|
|
328
|
$plugin['strings']['confirmation']['revert'] += array(
|
329
|
'question' => t('Are you sure you want to revert %title?'),
|
330
|
'information' => t('This action will permanently remove any customizations made to this item.'),
|
331
|
'success' => t('The item has been reverted.'),
|
332
|
);
|
333
|
|
334
|
$plugin['strings']['confirmation']['delete'] += array(
|
335
|
'question' => t('Are you sure you want to delete %title?'),
|
336
|
'information' => t('This action will permanently remove this item from your database.'),
|
337
|
'success' => t('The item has been deleted.'),
|
338
|
);
|
339
|
|
340
|
$plugin['strings']['confirmation']['add'] += array(
|
341
|
'success' => t('%title has been created.'),
|
342
|
'fail' => t('%title could not be created.'),
|
343
|
);
|
344
|
|
345
|
$plugin['strings']['confirmation']['edit'] += array(
|
346
|
'success' => t('%title has been updated.'),
|
347
|
'fail' => t('%title could not be updated.'),
|
348
|
);
|
349
|
|
350
|
// Strings used in $forms.
|
351
|
$plugin['strings']['help'] += array(
|
352
|
'import' => t('You can import an exported definition by pasting the exported object code into the field below.'),
|
353
|
);
|
354
|
|
355
|
// Strings used in drupal_set_message().
|
356
|
$plugin['strings']['message'] += array(
|
357
|
'enable' => t('@plugin %title was enabled.', array('@plugin' => $plugin['title singular proper'])),
|
358
|
'disable' => t('@plugin %title was disabled.', array('@plugin' => $plugin['title singular proper'])),
|
359
|
'no items' => t('There are no @titles to display.', array('@titles' => $plugin['title plural'])),
|
360
|
);
|
361
|
}
|
362
|
|
363
|
/**
|
364
|
* Get the class to handle creating a list of exportable items.
|
365
|
*
|
366
|
* If a plugin does not define a lister class at all, then the default
|
367
|
* lister class will be used.
|
368
|
*
|
369
|
* @return
|
370
|
* Either the lister class or FALSE if one could not be had.
|
371
|
*/
|
372
|
function ctools_export_ui_get_handler($plugin) {
|
373
|
$cache = &drupal_static(__FUNCTION__, array());
|
374
|
if (empty($cache[$plugin['name']])) {
|
375
|
// If a list class is not specified by the plugin, fall back to the
|
376
|
// default ctools_export_ui plugin instead.
|
377
|
if (empty($plugin['handler'])) {
|
378
|
$default = ctools_get_export_ui('ctools_export_ui');
|
379
|
$class = ctools_plugin_get_class($default, 'handler');
|
380
|
}
|
381
|
else {
|
382
|
$class = ctools_plugin_get_class($plugin, 'handler');
|
383
|
}
|
384
|
|
385
|
if ($class) {
|
386
|
$cache[$plugin['name']] = new $class();
|
387
|
$cache[$plugin['name']]->init($plugin);
|
388
|
}
|
389
|
}
|
390
|
return !empty($cache[$plugin['name']]) ? $cache[$plugin['name']] : FALSE;
|
391
|
}
|
392
|
|
393
|
/**
|
394
|
* Get the base path from a plugin.
|
395
|
*
|
396
|
* @param $plugin
|
397
|
* The plugin.
|
398
|
*
|
399
|
* @return
|
400
|
* The menu path to the plugin's list.
|
401
|
*/
|
402
|
function ctools_export_ui_plugin_base_path($plugin) {
|
403
|
return $plugin['menu']['menu prefix'] . '/' . $plugin['menu']['menu item'];
|
404
|
}
|
405
|
|
406
|
/**
|
407
|
* Get the path to a specific menu item from a plugin.
|
408
|
*
|
409
|
* @param $plugin
|
410
|
* The plugin name.
|
411
|
* @param $item_id
|
412
|
* The id in the menu items from the plugin.
|
413
|
* @param $export_key
|
414
|
* The export key of the item being edited, if it exists.
|
415
|
*
|
416
|
* @return
|
417
|
* The menu path to the plugin's list.
|
418
|
*/
|
419
|
function ctools_export_ui_plugin_menu_path($plugin, $item_id, $export_key = NULL) {
|
420
|
$path = $plugin['menu']['items'][$item_id]['path'];
|
421
|
if ($export_key) {
|
422
|
$path = str_replace('%ctools_export_ui', $export_key, $path);
|
423
|
}
|
424
|
return ctools_export_ui_plugin_base_path($plugin) . '/' . $path;
|
425
|
}
|
426
|
|
427
|
/**
|
428
|
* Helper function to include CTools plugins and get an export-ui exportable.
|
429
|
*
|
430
|
* @param $plugin_name
|
431
|
* The plugin that should be loaded.
|
432
|
*/
|
433
|
function ctools_get_export_ui($plugin_name) {
|
434
|
ctools_include('plugins');
|
435
|
return ctools_get_plugins('ctools', 'export_ui', $plugin_name);
|
436
|
}
|
437
|
|
438
|
/**
|
439
|
* Helper function to include CTools plugins and get all export-ui exportables.
|
440
|
*/
|
441
|
function ctools_get_export_uis() {
|
442
|
ctools_include('plugins');
|
443
|
return ctools_get_plugins('ctools', 'export_ui');
|
444
|
}
|
445
|
|
446
|
/**
|
447
|
* Main page callback to manipulate exportables.
|
448
|
*
|
449
|
* This simply loads the object defined in the plugin and hands it off to
|
450
|
* a method based upon the name of the operation in use. This can easily
|
451
|
* be used to add more ops.
|
452
|
*/
|
453
|
function ctools_export_ui_switcher_page($plugin_name, $op) {
|
454
|
$args = func_get_args();
|
455
|
$js = !empty($_REQUEST['js']);
|
456
|
|
457
|
// Load the $plugin information.
|
458
|
$plugin = ctools_get_export_ui($plugin_name);
|
459
|
if (!$plugin) {
|
460
|
return t('Configuration error. No plugin found: %plugin_name.', array('%plugin_name' => $plugin_name));
|
461
|
}
|
462
|
|
463
|
$handler = ctools_export_ui_get_handler($plugin);
|
464
|
if (!$handler) {
|
465
|
return t('Configuration error. No handler found.');
|
466
|
}
|
467
|
|
468
|
$method = $op . '_page';
|
469
|
if (method_exists($handler, $method)) {
|
470
|
// Replace the first two arguments:
|
471
|
$args[0] = $js;
|
472
|
$args[1] = $_POST;
|
473
|
return call_user_func_array(array($handler, $method), $args);
|
474
|
}
|
475
|
}
|