1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* Core functionality for the Panels engine.
|
6
|
*/
|
7
|
|
8
|
define('PANELS_REQUIRED_CTOOLS_API', '2.0.9');
|
9
|
|
10
|
/**
|
11
|
* The current working panels version.
|
12
|
*
|
13
|
* In a release, it should be 7.x-3.x, which should match what drush make will
|
14
|
* create. In a dev format, it should be 7.x-3.(x+1)-dev, which will allow
|
15
|
* modules depending on new features in panels to depend on panels > 7.x-3.x.
|
16
|
*
|
17
|
* To define a specific version of Panels as a dependency for another module,
|
18
|
* simply include a dependency line in that module's info file, e.g.:
|
19
|
* ; Requires Panels v7.x-3.4 or newer.
|
20
|
* dependencies[] = panels (>=3.4)
|
21
|
*/
|
22
|
define('PANELS_VERSION', '7.x-3.8');
|
23
|
|
24
|
|
25
|
// Hide title use to be TRUE/FALSE. So FALSE remains old behavior.
|
26
|
define('PANELS_TITLE_FIXED', 0);
|
27
|
// And TRUE meant no title.
|
28
|
define('PANELS_TITLE_NONE', 1);
|
29
|
// And this is the new behavior, where the title field will pick from a pane.
|
30
|
define('PANELS_TITLE_PANE', 2);
|
31
|
|
32
|
/**
|
33
|
* Returns the API version of Panels. This didn't exist in 1.
|
34
|
*
|
35
|
* @todo -- this should work more like the CTools API version.
|
36
|
*
|
37
|
* @return array
|
38
|
* An array with the major and minor versions
|
39
|
*/
|
40
|
function panels_api_version() {
|
41
|
return array(3, 1);
|
42
|
}
|
43
|
|
44
|
/**
|
45
|
* Implements hook_theme().
|
46
|
*/
|
47
|
function panels_theme() {
|
48
|
// Safety: go away if CTools is not at an appropriate version.
|
49
|
if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
|
50
|
return array();
|
51
|
}
|
52
|
|
53
|
$theme = array();
|
54
|
$theme['panels_layout_link'] = array(
|
55
|
'variables' => array(
|
56
|
'title' => NULL,
|
57
|
'id' => NULL,
|
58
|
'image' => NULL,
|
59
|
'link' => NULL,
|
60
|
'class' => NULL,
|
61
|
'options' => NULL,
|
62
|
),
|
63
|
);
|
64
|
$theme['panels_layout_icon'] = array(
|
65
|
'variables' => array(
|
66
|
'id' => NULL,
|
67
|
'image' => NULL,
|
68
|
'title' => NULL,
|
69
|
),
|
70
|
);
|
71
|
$theme['panels_pane'] = array(
|
72
|
'variables' => array(
|
73
|
'content' => array(),
|
74
|
'pane' => array(),
|
75
|
'display' => array(),
|
76
|
),
|
77
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
78
|
'template' => 'panels-pane',
|
79
|
);
|
80
|
$theme['panels_common_content_list'] = array(
|
81
|
'variables' => array('display' => NULL),
|
82
|
'file' => 'includes/common.inc',
|
83
|
);
|
84
|
$theme['panels_render_display_form'] = array(
|
85
|
'render element' => 'element',
|
86
|
);
|
87
|
|
88
|
$theme['panels_dashboard'] = array(
|
89
|
'variables' => array(),
|
90
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
91
|
'file' => '../includes/callbacks.inc',
|
92
|
'template' => 'panels-dashboard',
|
93
|
);
|
94
|
|
95
|
$theme['panels_dashboard_link'] = array(
|
96
|
'variables' => array('link' => array()),
|
97
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
98
|
'file' => '../includes/callbacks.inc',
|
99
|
'template' => 'panels-dashboard-link',
|
100
|
);
|
101
|
|
102
|
$theme['panels_dashboard_block'] = array(
|
103
|
'variables' => array('block' => array()),
|
104
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
105
|
'file' => '../includes/callbacks.inc',
|
106
|
'template' => 'panels-dashboard-block',
|
107
|
);
|
108
|
|
109
|
$theme['panels_add_content_modal'] = array(
|
110
|
'variables' => array(
|
111
|
'renderer' => NULL,
|
112
|
'categories' => array(),
|
113
|
'region' => NULL,
|
114
|
'category' => NULL,
|
115
|
'column_count' => 2,
|
116
|
),
|
117
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
118
|
'file' => '../includes/add-content.inc',
|
119
|
'template' => 'panels-add-content-modal',
|
120
|
);
|
121
|
|
122
|
$theme['panels_add_content_link'] = array(
|
123
|
'variables' => array(
|
124
|
'renderer' => NULL,
|
125
|
'region' => NULL,
|
126
|
'content_type' => NULL,
|
127
|
),
|
128
|
'path' => drupal_get_path('module', 'panels') . '/templates',
|
129
|
'file' => '../includes/add-content.inc',
|
130
|
'template' => 'panels-add-content-link',
|
131
|
);
|
132
|
|
133
|
// Register layout and style themes on behalf of all of these items.
|
134
|
ctools_include('plugins', 'panels');
|
135
|
|
136
|
// No need to worry about files; the plugin has to already be loaded for us
|
137
|
// to even know what the theme function is, so files will be auto included.
|
138
|
$layouts = panels_get_layouts();
|
139
|
foreach ($layouts as $name => $data) {
|
140
|
foreach (array('theme', 'admin theme') as $callback) {
|
141
|
if (!empty($data[$callback])) {
|
142
|
$theme[$data[$callback]] = array(
|
143
|
'variables' => array(
|
144
|
'css_id' => NULL,
|
145
|
'content' => NULL,
|
146
|
'settings' => NULL,
|
147
|
'display' => NULL,
|
148
|
'layout' => NULL,
|
149
|
'renderer' => NULL,
|
150
|
),
|
151
|
'path' => $data['path'],
|
152
|
'file' => $data['file'],
|
153
|
);
|
154
|
|
155
|
// If no theme function exists, assume template.
|
156
|
if (!function_exists("theme_$data[theme]")) {
|
157
|
$theme[$data[$callback]]['template'] = str_replace('_', '-', $data[$callback]);
|
158
|
// For preprocess.
|
159
|
$theme[$data[$callback]]['file'] = $data['file'];
|
160
|
}
|
161
|
}
|
162
|
}
|
163
|
}
|
164
|
|
165
|
$styles = panels_get_styles();
|
166
|
foreach ($styles as $name => $data) {
|
167
|
if (!empty($data['render pane'])) {
|
168
|
$theme[$data['render pane']] = array(
|
169
|
'variables' => array(
|
170
|
'content' => NULL,
|
171
|
'pane' => NULL,
|
172
|
'display' => NULL,
|
173
|
'style' => NULL,
|
174
|
'settings' => NULL,
|
175
|
),
|
176
|
'path' => $data['path'],
|
177
|
'file' => $data['file'],
|
178
|
);
|
179
|
}
|
180
|
if (!empty($data['render region'])) {
|
181
|
$theme[$data['render region']] = array(
|
182
|
'variables' => array(
|
183
|
'display' => NULL,
|
184
|
'owner_id' => NULL,
|
185
|
'panes' => NULL,
|
186
|
'settings' => NULL,
|
187
|
'region_id' => NULL,
|
188
|
'style' => NULL,
|
189
|
),
|
190
|
'path' => $data['path'],
|
191
|
'file' => $data['file'],
|
192
|
);
|
193
|
}
|
194
|
|
195
|
if (!empty($data['hook theme'])) {
|
196
|
if (is_array($data['hook theme'])) {
|
197
|
$theme += $data['hook theme'];
|
198
|
}
|
199
|
elseif (function_exists($data['hook theme'])) {
|
200
|
$data['hook theme']($theme, $data);
|
201
|
}
|
202
|
}
|
203
|
}
|
204
|
return $theme;
|
205
|
}
|
206
|
|
207
|
/**
|
208
|
* Implements hook_menu().
|
209
|
*/
|
210
|
function panels_menu() {
|
211
|
// Safety: go away if CTools is not at an appropriate version.
|
212
|
if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
|
213
|
return array();
|
214
|
}
|
215
|
$items = array();
|
216
|
|
217
|
// Base AJAX router callback.
|
218
|
$items['panels/ajax'] = array(
|
219
|
'access arguments' => array('access content'),
|
220
|
'page callback' => 'panels_ajax_router',
|
221
|
'theme callback' => 'ajax_base_page_theme',
|
222
|
'delivery callback' => 'ajax_deliver',
|
223
|
'type' => MENU_CALLBACK,
|
224
|
);
|
225
|
|
226
|
$admin_base = array(
|
227
|
'file' => 'includes/callbacks.inc',
|
228
|
'access arguments' => array('use panels dashboard'),
|
229
|
);
|
230
|
// Provide a nice location for a panels admin panel.
|
231
|
$items['admin/structure/panels'] = array(
|
232
|
'title' => 'Panels',
|
233
|
'page callback' => 'panels_admin_page',
|
234
|
'description' => 'Get a bird\'s eye view of items related to Panels.',
|
235
|
) + $admin_base;
|
236
|
|
237
|
$items['admin/structure/panels/dashboard'] = array(
|
238
|
'title' => 'Dashboard',
|
239
|
'page callback' => 'panels_admin_page',
|
240
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
241
|
'weight' => -10,
|
242
|
) + $admin_base;
|
243
|
|
244
|
$items['admin/structure/panels/settings'] = array(
|
245
|
'title' => 'Settings',
|
246
|
'page callback' => 'drupal_get_form',
|
247
|
'page arguments' => array('panels_admin_settings_page'),
|
248
|
'type' => MENU_LOCAL_TASK,
|
249
|
) + $admin_base;
|
250
|
|
251
|
$items['admin/structure/panels/settings/general'] = array(
|
252
|
'title' => 'General',
|
253
|
'page callback' => 'drupal_get_form',
|
254
|
'page arguments' => array('panels_admin_settings_page'),
|
255
|
'access arguments' => array('administer page manager'),
|
256
|
'type' => MENU_DEFAULT_LOCAL_TASK,
|
257
|
'weight' => -10,
|
258
|
) + $admin_base;
|
259
|
|
260
|
if (module_exists('page_manager')) {
|
261
|
$items['admin/structure/panels/settings/panel-page'] = array(
|
262
|
'title' => 'Panel pages',
|
263
|
'page callback' => 'panels_admin_panel_context_page',
|
264
|
'type' => MENU_LOCAL_TASK,
|
265
|
'weight' => -10,
|
266
|
) + $admin_base;
|
267
|
}
|
268
|
|
269
|
ctools_include('plugins', 'panels');
|
270
|
$layouts = panels_get_layouts();
|
271
|
foreach ($layouts as $name => $data) {
|
272
|
if (!empty($data['hook menu'])) {
|
273
|
if (is_array($data['hook menu'])) {
|
274
|
$items += $data['hook menu'];
|
275
|
}
|
276
|
elseif (function_exists($data['hook menu'])) {
|
277
|
$data['hook menu']($items, $data);
|
278
|
}
|
279
|
}
|
280
|
}
|
281
|
return $items;
|
282
|
}
|
283
|
|
284
|
/**
|
285
|
* Menu loader function to load a cache item for Panels AJAX.
|
286
|
*
|
287
|
* This load all of the includes needed to perform AJAX, and loads the
|
288
|
* cache object and makes sure it is valid.
|
289
|
*/
|
290
|
function panels_edit_cache_load($cache_key) {
|
291
|
ctools_include('display-edit', 'panels');
|
292
|
ctools_include('plugins', 'panels');
|
293
|
ctools_include('ajax');
|
294
|
ctools_include('modal');
|
295
|
ctools_include('context');
|
296
|
|
297
|
return panels_edit_cache_get($cache_key);
|
298
|
}
|
299
|
|
300
|
/**
|
301
|
* Implements hook_init().
|
302
|
*/
|
303
|
function panels_init() {
|
304
|
// Safety: go away if CTools is not at an appropriate version.
|
305
|
if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
|
306
|
if (user_access('administer site configuration')) {
|
307
|
drupal_set_message(t('Panels is enabled but CTools is out of date. All Panels modules are disabled until CTools is updated. See the status page for more information.'), 'error');
|
308
|
}
|
309
|
return;
|
310
|
}
|
311
|
|
312
|
ctools_add_css('panels', 'panels');
|
313
|
}
|
314
|
|
315
|
/**
|
316
|
* Implements hook_permission().
|
317
|
*
|
318
|
* @todo Almost all of these need to be moved into pipelines.
|
319
|
*/
|
320
|
function panels_permission() {
|
321
|
return array(
|
322
|
'use panels dashboard' => array(
|
323
|
'title' => t("Use Panels Dashboard"),
|
324
|
'description' => t('Allows a user to access the <a href="@url">Panels Dashboard</a>.', array('@url' => url('admin/structure/panels'))),
|
325
|
),
|
326
|
// @todo
|
327
|
'view pane admin links' => array(
|
328
|
'title' => t("View administrative links on Panel panes"),
|
329
|
'description' => "",
|
330
|
),
|
331
|
// @todo should we really have a global perm for this, or should it be moved into a pipeline question?
|
332
|
'administer pane access' => array(
|
333
|
'title' => t("Configure access settings on Panel panes"),
|
334
|
'description' => t("Access rules (often also called visibility rules) can be configured on a per-pane basis. This permission allows users to configure those settings."),
|
335
|
),
|
336
|
'use panels in place editing' => array(
|
337
|
'title' => t("Use the Panels In-Place Editor"),
|
338
|
'description' => t("Allows a user to utilize Panels' In-Place Editor."),
|
339
|
),
|
340
|
'change layouts in place editing' => array(
|
341
|
'title' => t("Change layouts with the Panels In-Place Editor"),
|
342
|
'description' => t("Allows a user to change layouts with the IPE."),
|
343
|
),
|
344
|
'bypass access in place editing' => array(
|
345
|
'title' => t("Bypass access checks when using Panels In-Place Editor"),
|
346
|
'description' => t("Allows using IPE even if user does not have additional permissions granted by other modules."),
|
347
|
'restrict access' => TRUE,
|
348
|
),
|
349
|
'administer advanced pane settings' => array(
|
350
|
'title' => t("Configure advanced settings on Panel panes"),
|
351
|
'description' => "",
|
352
|
),
|
353
|
'administer panels layouts' => array(
|
354
|
'title' => t("Administer Panels layouts"),
|
355
|
'description' => t("Allows a user to administer exported Panels layout plugins & instances."),
|
356
|
),
|
357
|
'administer panels styles' => array(
|
358
|
'title' => t("Administer Panels styles"),
|
359
|
'description' => t("DEPRECATED: Modules using this permission should use specific style permissions. See Issue #2329419 for more info."),
|
360
|
),
|
361
|
'administer panels display styles' => array(
|
362
|
'title' => t("Administer Panels display styles"),
|
363
|
'description' => t("Allows a user to administer the styles of Panel displays."),
|
364
|
),
|
365
|
'administer panels pane styles' => array(
|
366
|
'title' => t("Administer Panels pane styles"),
|
367
|
'description' => t("Allows a user to administer the styles of Panel panes."),
|
368
|
),
|
369
|
'administer panels region styles' => array(
|
370
|
'title' => t("Administer Panels region styles"),
|
371
|
'description' => t("Allows a user to administer the styles of Panel regions."),
|
372
|
),
|
373
|
'use panels caching features' => array(
|
374
|
'title' => t("Configure caching settings on Panels"),
|
375
|
'description' => t("Allows a user to configure caching on Panels displays and panes."),
|
376
|
),
|
377
|
'use panels locks' => array(
|
378
|
'title' => t('Use panel locks'),
|
379
|
'description' => t('Allows a user to lock and unlock panes in a panel display.'),
|
380
|
),
|
381
|
'use ipe with page manager' => array(
|
382
|
'title' => t("Use the Panels In-Place Editor with Page Manager"),
|
383
|
'description' => t('Allows users with access to the In-Place editor to administer page manager pages. This permission is only needed for users without "use page manager" access.'),
|
384
|
),
|
385
|
);
|
386
|
}
|
387
|
|
388
|
/**
|
389
|
* Implements hook_flush_caches().
|
390
|
*/
|
391
|
function panels_flush_caches() {
|
392
|
if (db_table_exists('cache_panels')) {
|
393
|
return array('cache_panels');
|
394
|
}
|
395
|
}
|
396
|
|
397
|
/**
|
398
|
* CTools hook implementations.
|
399
|
*
|
400
|
* These aren't core Drupal hooks but they are just as important.
|
401
|
*/
|
402
|
|
403
|
/**
|
404
|
* Implements hook_ctools_plugin_directory().
|
405
|
*/
|
406
|
function panels_ctools_plugin_directory($module, $plugin) {
|
407
|
// To let the system know we implement task and task_handler plugins.
|
408
|
// Safety: go away if CTools is not at an appropriate version.
|
409
|
if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
|
410
|
return;
|
411
|
}
|
412
|
|
413
|
// We don't support the 'ctools' 'cache' plugin and pretending to causes
|
414
|
// errors when they're in use.
|
415
|
if ($module == 'ctools' && $plugin == 'cache') {
|
416
|
// If we did we'd make a plugin/ctools_cache or something.
|
417
|
return;
|
418
|
}
|
419
|
|
420
|
if ($module == 'page_manager' || $module == 'panels' || $module == 'ctools' || $module == 'stylizer') {
|
421
|
// Panels and CTools both implement a 'cache' plugin but we don't implement
|
422
|
// the CTools version.
|
423
|
if ($module == 'ctools' && $plugin == 'cache') {
|
424
|
return;
|
425
|
}
|
426
|
return 'plugins/' . $plugin;
|
427
|
}
|
428
|
}
|
429
|
|
430
|
/**
|
431
|
* Implements hook_ctools_plugin_type().
|
432
|
*
|
433
|
* Register layout, style, cache, and display_renderer plugin types, declaring
|
434
|
* relevant plugin type information as necessary.
|
435
|
*/
|
436
|
function panels_ctools_plugin_type() {
|
437
|
return array(
|
438
|
'layouts' => array(
|
439
|
// We can define layouts in themes.
|
440
|
'load themes' => TRUE,
|
441
|
'process' => 'panels_layout_process',
|
442
|
'child plugins' => TRUE,
|
443
|
),
|
444
|
'styles' => array(
|
445
|
'load themes' => TRUE,
|
446
|
'process' => 'panels_plugin_styles_process',
|
447
|
'child plugins' => TRUE,
|
448
|
),
|
449
|
'cache' => array(),
|
450
|
'display_renderers' => array(
|
451
|
'classes' => array('renderer'),
|
452
|
),
|
453
|
'panels_storage' => array(),
|
454
|
);
|
455
|
}
|
456
|
|
457
|
/**
|
458
|
* Ensure a layout has a minimal set of data.
|
459
|
*/
|
460
|
function panels_layout_process(&$plugin) {
|
461
|
$plugin += array(
|
462
|
'category' => t('Miscellaneous'),
|
463
|
'description' => '',
|
464
|
);
|
465
|
}
|
466
|
|
467
|
/**
|
468
|
* Implements hook_ctools_plugin_api().
|
469
|
*/
|
470
|
function panels_ctools_plugin_api($owner, $api) {
|
471
|
// Inform CTools about version information for various plugins implemented by
|
472
|
// panels.
|
473
|
if ($owner == 'panels' && $api == 'styles') {
|
474
|
// As of 6.x-3.6, Panels has a slightly new system for style plugins.
|
475
|
return array('version' => 2.0);
|
476
|
}
|
477
|
|
478
|
if ($owner == 'panels' && $api == 'pipelines') {
|
479
|
return array(
|
480
|
'version' => 1,
|
481
|
'path' => drupal_get_path('module', 'panels') . '/includes',
|
482
|
);
|
483
|
}
|
484
|
}
|
485
|
|
486
|
/**
|
487
|
* Implements hook_views_api().
|
488
|
*/
|
489
|
function panels_views_api() {
|
490
|
return array(
|
491
|
'api' => 2,
|
492
|
'path' => drupal_get_path('module', 'panels') . '/plugins/views',
|
493
|
);
|
494
|
}
|
495
|
|
496
|
/**
|
497
|
* Perform additional processing on a style plugin.
|
498
|
*
|
499
|
* Currently this is only being used to apply versioning information to style
|
500
|
* plugins in order to ensure the legacy renderer passes the right type of
|
501
|
* parameters to a style plugin in a hybrid environment of both new and old
|
502
|
* plugins.
|
503
|
*
|
504
|
* @param array $plugin
|
505
|
* The style plugin that is being processed.
|
506
|
* @param array $info
|
507
|
* The style plugin type info array.
|
508
|
*
|
509
|
* @see _ctools_process_data()
|
510
|
*/
|
511
|
function panels_plugin_styles_process(&$plugin, $info) {
|
512
|
$plugin += array(
|
513
|
'weight' => 0,
|
514
|
);
|
515
|
|
516
|
$compliant_modules = ctools_plugin_api_info('panels', 'styles', 2.0, 2.0);
|
517
|
$plugin['version'] = empty($compliant_modules[$plugin['module']]) ? 1.0 : $compliant_modules[$plugin['module']]['version'];
|
518
|
}
|
519
|
|
520
|
/**
|
521
|
* Declare what style types Panels uses.
|
522
|
*/
|
523
|
function panels_ctools_style_base_types() {
|
524
|
return array(
|
525
|
'region' => array(
|
526
|
'title' => t('Panel region'),
|
527
|
'preview' => 'panels_stylizer_region_preview',
|
528
|
'theme variables' => array(
|
529
|
'settings' => NULL,
|
530
|
'class' => NULL,
|
531
|
'content' => NULL,
|
532
|
),
|
533
|
),
|
534
|
'pane' => array(
|
535
|
'title' => t('Panel pane'),
|
536
|
'preview' => 'panels_stylizer_pane_preview',
|
537
|
'theme variables' => array(
|
538
|
'settings' => NULL,
|
539
|
'content' => NULL,
|
540
|
'pane' => NULL,
|
541
|
'display' => NULL,
|
542
|
),
|
543
|
),
|
544
|
);
|
545
|
}
|
546
|
|
547
|
/**
|
548
|
* Generates Lorem Ipsum.
|
549
|
*
|
550
|
* @return string
|
551
|
* Lorem ipsum string.
|
552
|
*/
|
553
|
function panels_stylizer_lipsum() {
|
554
|
return <<<LIPSUM
|
555
|
<p>
|
556
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at
|
557
|
velit dolor. Donec egestas tellus sit amet urna rhoncus adipiscing. Proin
|
558
|
nec porttitor sem. Maecenas aliquam, purus nec tempus dignissim, nulla arcu
|
559
|
aliquam diam, non tincidunt massa ante vel dolor. Aliquam sapien sapien,
|
560
|
tincidunt id tristique at, pretium sagittis libero.
|
561
|
</p>
|
562
|
<p>
|
563
|
Nulla facilisi. Curabitur lacinia, tellus sed tristique consequat, diam
|
564
|
lorem scelerisque felis, at dictum purus augue facilisis lorem. Duis
|
565
|
pharetra dignissim rutrum. Curabitur ac elit id dui dapibus tincidunt.
|
566
|
Nulla eget sem quam, non eleifend eros. Cras porttitor tempus lectus ac
|
567
|
scelerisque. Curabitur vehicula bibendum lorem, vitae ornare ligula
|
568
|
venenatis ut.
|
569
|
</p>
|
570
|
LIPSUM;
|
571
|
}
|
572
|
|
573
|
/**
|
574
|
* Generate a preview given the current settings.
|
575
|
*/
|
576
|
function panels_stylizer_region_preview($plugin, $settings) {
|
577
|
ctools_stylizer_add_css($plugin, $settings);
|
578
|
return theme(
|
579
|
$plugin['theme'],
|
580
|
array(
|
581
|
'settings' => $settings,
|
582
|
'class' => ctools_stylizer_get_css_class($plugin, $settings),
|
583
|
'content' => panels_stylizer_lipsum(),
|
584
|
)
|
585
|
);
|
586
|
}
|
587
|
|
588
|
/**
|
589
|
* Generate a preview given the current settings.
|
590
|
*/
|
591
|
function panels_stylizer_pane_preview($plugin, $settings) {
|
592
|
ctools_stylizer_add_css($plugin, $settings);
|
593
|
$pane = new stdClass();
|
594
|
|
595
|
$content = new stdClass();
|
596
|
$content->title = t('Lorem ipsum');
|
597
|
$content->content = panels_stylizer_lipsum();
|
598
|
$content->type = 'dummy';
|
599
|
$content->subtype = 'dummy';
|
600
|
|
601
|
$content->css_class = ctools_stylizer_get_css_class($plugin, $settings);
|
602
|
|
603
|
$display = new panels_display();
|
604
|
|
605
|
if (!empty($plugin['theme'])) {
|
606
|
return theme(
|
607
|
$plugin['theme'],
|
608
|
array(
|
609
|
'settings' => $settings,
|
610
|
'content' => $content,
|
611
|
'pane' => $pane,
|
612
|
'display' => $display,
|
613
|
)
|
614
|
);
|
615
|
}
|
616
|
else {
|
617
|
return theme(
|
618
|
'panels_pane',
|
619
|
array(
|
620
|
'content' => $content,
|
621
|
'pane' => $pane,
|
622
|
'display' => $display,
|
623
|
)
|
624
|
);
|
625
|
}
|
626
|
}
|
627
|
|
628
|
/**
|
629
|
* Panels display editing.
|
630
|
*/
|
631
|
|
632
|
/**
|
633
|
* @defgroup mainapi Functions comprising the main panels API.
|
634
|
*/
|
635
|
|
636
|
/**
|
637
|
* Main API entry point to edit a panel display.
|
638
|
*
|
639
|
* Sample implementations utiltizing the the complex $destination behavior can
|
640
|
* be found in panels_page_edit_content() and, in a separate contrib module,
|
641
|
* OG Blueprints (http://drupal.org/project/og_blueprints),
|
642
|
* og_blueprints_blueprint_edit().
|
643
|
*
|
644
|
* @param object $display
|
645
|
* Instanceof panels_display.
|
646
|
*
|
647
|
* A fully loaded panels $display object, as returned from
|
648
|
* panels_load_display(). Merely passing a did is NOT sufficient.
|
649
|
* Note that 'fully loaded' means the $display must already be loaded with
|
650
|
* any contexts the caller wishes to have set for the display.
|
651
|
* @param mixed $destination
|
652
|
* The redirect destination that the user should be taken to on form
|
653
|
* submission or cancellation. With panels_edit, $destination has complex
|
654
|
* effects on the return values of panels_edit() once the form has been
|
655
|
* submitted. See the explanation of the return value below to understand the
|
656
|
* different types of values returned by panels_edit() at different stages of
|
657
|
* FAPI. Under most circumstances, simply passing in drupal_get_destination()
|
658
|
* is all that's necessary.
|
659
|
* @param array $content_types
|
660
|
* An associative array of allowed content types, typically as returned from
|
661
|
* panels_common_get_allowed_types(). Note that context partially governs
|
662
|
* available content types, so you will want to create any relevant contexts
|
663
|
* using panels_create_context() or panels_create_context_empty() to make sure
|
664
|
* all the appropriate content types are available.
|
665
|
*
|
666
|
* @return mixed
|
667
|
* Because the functions called by panels_edit() invoke the form API,
|
668
|
* this function returns different values depending on the stage of form
|
669
|
* submission we're at. In Drupal 5, the phase of form submission is indicated
|
670
|
* by the contents of $_POST['op']. Here is what you'll get at different
|
671
|
* stages:
|
672
|
* - If !$_POST['op']: then we're on on the initial passthrough and the
|
673
|
* form is being rendered, so it's the $form itself that's being
|
674
|
* returned. Because negative margins, a common CSS technique, bork the
|
675
|
* display editor's ajax drag-and-drop, it's important that the $output
|
676
|
* be printed, not returned. Use this syntax in the caller function:
|
677
|
* print theme('page', panels_edit($display, $destination, $content_types), FALSE);
|
678
|
* - If $_POST['op'] == t('Cancel'): form submission has been cancelled.
|
679
|
* If empty($destination) == FALSE, then there is no return value and the
|
680
|
* panels API takes care of redirecting to $destination.
|
681
|
* If empty($destination) == TRUE, then there's still no return value,
|
682
|
* but the caller function has to take care of form redirection.
|
683
|
* - If $_POST['op'] == ('Save'): the form has been submitted successfully
|
684
|
* and has run through panels_edit_display_submit().
|
685
|
* $output depends on the value of $destination:
|
686
|
* - If empty($destination) == TRUE: $output contains the modified $display
|
687
|
* object, and no redirection will occur. This option is useful if the
|
688
|
* caller needs to perform additional operations on or with the modified
|
689
|
* $display before the page request is complete. Using hook_form_alter()
|
690
|
* to add an additional submit handler is typically the preferred method
|
691
|
* for something like this, but there are certain use cases where that is
|
692
|
* infeasible and $destination = NULL should be used instead. If this
|
693
|
* method is employed, the caller will need to handle form redirection.
|
694
|
* Note that having $_REQUEST['destination'] set, whether via
|
695
|
* drupal_get_destination() or some other method, will NOT interfere with
|
696
|
* this functionality; consequently, you can use drupal_get_destination()
|
697
|
* to safely store your desired redirect in the caller function, then
|
698
|
* simply use drupal_goto() once panels_edit() has done its business.
|
699
|
* - If empty($destination) == FALSE: the form will redirect to the URL
|
700
|
* string given in $destination and NO value will be returned.
|
701
|
*
|
702
|
* @ingroup mainapi
|
703
|
*/
|
704
|
function panels_edit($display, $destination = NULL, $content_types = NULL, $title = FALSE) {
|
705
|
ctools_include('display-edit', 'panels');
|
706
|
ctools_include('ajax');
|
707
|
ctools_include('plugins', 'panels');
|
708
|
return _panels_edit($display, $destination, $content_types, $title);
|
709
|
}
|
710
|
|
711
|
/**
|
712
|
* API entry point for selecting a layout for a given display.
|
713
|
*
|
714
|
* Layout selection is nothing more than a list of radio items encompassing the
|
715
|
* available layouts for this display, as defined by .inc files in the
|
716
|
* panels/layouts subdirectory. The only real complexity occurs when a user
|
717
|
* attempts to change the layout of a display that has some content in it.
|
718
|
*
|
719
|
* @param object $display
|
720
|
* A fully loaded panels $display object, as returned from
|
721
|
* panels_load_display(). Merely passing a did is NOT sufficient.
|
722
|
* @param string $finish
|
723
|
* A string that will be used for the text of the form submission button. If
|
724
|
* no value is provided, then the form submission button will default to
|
725
|
* t('Save').
|
726
|
* @param mixed $destination
|
727
|
* Basic usage is a string containing the URL that the form should redirect to
|
728
|
* upon submission. For a discussion of advanced usages, see panels_edit().
|
729
|
* @param mixed $allowed_layouts
|
730
|
* Allowed layouts has three different behaviors that depend on which of three
|
731
|
* value types are passed in by the caller:
|
732
|
* #- if $allowed_layouts instanceof panels_allowed_layouts
|
733
|
* (includes subclasses): the most complex use of the API. The caller is
|
734
|
* passing in a loaded panels_allowed_layouts object that the client
|
735
|
* module previously created and stored somewhere using a custom storage
|
736
|
* mechanism.
|
737
|
* #- if is_string($allowed_layouts): the string will be used in a call to
|
738
|
* variable_get() which will call the
|
739
|
* $allowed_layouts . '_allowed_layouts' var. If the data was stored
|
740
|
* properly in the system var, the $allowed_layouts object will be
|
741
|
* unserialized and recreated.
|
742
|
* #- if is_null($allowed_layouts): the default behavior, which also
|
743
|
* provides backwards compatibility for implementations of the Panels2
|
744
|
* API written before beta4. In this case, a dummy panels_allowed_layouts
|
745
|
* object is created which does not restrict any layouts. Subsequent
|
746
|
* behavior is indistinguishable from pre-beta4 behavior.
|
747
|
*
|
748
|
* @return mixed
|
749
|
* Can return nothing, or a modified $display object, or a redirection string;
|
750
|
* return values for the panels_edit* family of functions are quite complex.
|
751
|
* See panels_edit() for detailed discussion.
|
752
|
*
|
753
|
* @see panels_edit()
|
754
|
* @see panels_common_set_allowed_layouts()
|
755
|
*/
|
756
|
function panels_edit_layout($display, $finish, $destination = NULL, $allowed_layouts = NULL) {
|
757
|
ctools_include('display-layout', 'panels');
|
758
|
ctools_include('plugins', 'panels');
|
759
|
return _panels_edit_layout($display, $finish, $destination, $allowed_layouts);
|
760
|
}
|
761
|
|
762
|
/**
|
763
|
* Panels database functions.
|
764
|
*/
|
765
|
|
766
|
/**
|
767
|
* Forms the basis of a panel display.
|
768
|
*/
|
769
|
class panels_display {
|
770
|
public $args = array();
|
771
|
public $content = array();
|
772
|
public $panels = array();
|
773
|
public $incoming_content = NULL;
|
774
|
public $css_id = NULL;
|
775
|
public $context = array();
|
776
|
public $did = 'new';
|
777
|
public $renderer = 'standard';
|
778
|
|
779
|
/**
|
780
|
* Add a pane.
|
781
|
*/
|
782
|
public function add_pane(&$pane, $location = NULL) {
|
783
|
// If no location specified, use what's set in the pane.
|
784
|
if (empty($location)) {
|
785
|
$location = $pane->panel;
|
786
|
}
|
787
|
else {
|
788
|
$pane->panel = $location;
|
789
|
}
|
790
|
|
791
|
// Generate a permanent uuid for this pane, and use
|
792
|
// it as a temporary pid.
|
793
|
$pane->uuid = ctools_uuid_generate();
|
794
|
$pane->pid = 'new-' . $pane->uuid;
|
795
|
|
796
|
// Add the pane to the appropriate spots.
|
797
|
$this->content[$pane->pid] = &$pane;
|
798
|
$this->panels[$location][] = $pane->pid;
|
799
|
}
|
800
|
|
801
|
/**
|
802
|
* Duplicate a pane.
|
803
|
*/
|
804
|
public function duplicate_pane($pid, $location = FALSE) {
|
805
|
$pane = $this->clone_pane($pid);
|
806
|
$this->add_pane($pane, $location);
|
807
|
}
|
808
|
|
809
|
/**
|
810
|
* Clone a pane.
|
811
|
*/
|
812
|
public function clone_pane($pid) {
|
813
|
$pane = clone $this->content[$pid];
|
814
|
$pane->uuid = ctools_uuid_generate();
|
815
|
return $pane;
|
816
|
}
|
817
|
|
818
|
/**
|
819
|
* Get the title from a display.
|
820
|
*
|
821
|
* The display must have already been rendered, or the setting to set the
|
822
|
* display's title from a pane's title will not have worked.
|
823
|
*
|
824
|
* @return mixed
|
825
|
* The title to use. If NULL, this means to let any default title that may
|
826
|
* be in use pass through. i.e, do not actually set the title.
|
827
|
*/
|
828
|
public function get_title() {
|
829
|
switch ($this->hide_title) {
|
830
|
case PANELS_TITLE_NONE:
|
831
|
return '';
|
832
|
|
833
|
case PANELS_TITLE_PANE:
|
834
|
return isset($this->stored_pane_title) ? $this->stored_pane_title : '';
|
835
|
|
836
|
case PANELS_TITLE_FIXED:
|
837
|
case FALSE;
|
838
|
// For old exported panels that are not in the database.
|
839
|
if (!empty($this->title)) {
|
840
|
return filter_xss_admin(ctools_context_keyword_substitute($this->title, array(), $this->context));
|
841
|
}
|
842
|
return NULL;
|
843
|
}
|
844
|
}
|
845
|
|
846
|
/**
|
847
|
* Render this panels display.
|
848
|
*
|
849
|
* After checking to ensure the designated layout plugin is valid, a
|
850
|
* display renderer object is spawned and runs its rendering logic.
|
851
|
*
|
852
|
* @param mixed $renderer
|
853
|
* An instantiated display renderer object, or the name of a display
|
854
|
* renderer plugin+class to be fetched. Defaults to NULL. When NULL, the
|
855
|
* predesignated display renderer will be used.
|
856
|
*
|
857
|
* @return mixed
|
858
|
* NULL or output of render function.
|
859
|
*/
|
860
|
public function render($renderer = NULL) {
|
861
|
$layout = panels_get_layout($this->layout);
|
862
|
if (!$layout) {
|
863
|
return NULL;
|
864
|
}
|
865
|
|
866
|
// If we were not given a renderer object, load it.
|
867
|
if (!is_object($renderer)) {
|
868
|
// If the renderer was not specified, default to $this->renderer
|
869
|
// which is either standard or was already set for us.
|
870
|
$renderer = panels_get_renderer_handler(!empty($renderer) ? $renderer : $this->renderer, $this);
|
871
|
if (!$renderer) {
|
872
|
return NULL;
|
873
|
}
|
874
|
}
|
875
|
|
876
|
$output = '';
|
877
|
// Let modules act just prior to render.
|
878
|
foreach (module_implements('panels_pre_render') as $module) {
|
879
|
$function = $module . '_panels_pre_render';
|
880
|
$output .= $function($this, $renderer);
|
881
|
}
|
882
|
|
883
|
$output .= $renderer->render();
|
884
|
|
885
|
// Let modules act just after render.
|
886
|
foreach (module_implements('panels_post_render') as $module) {
|
887
|
$function = $module . '_panels_post_render';
|
888
|
$output .= $function($this, $renderer);
|
889
|
}
|
890
|
return $output;
|
891
|
}
|
892
|
|
893
|
/**
|
894
|
* Determine if the given user can perform the requested operation.
|
895
|
*
|
896
|
* @param string $op
|
897
|
* An operation like: create, read, update, or delete.
|
898
|
* @param object $account
|
899
|
* (optional) The account to check access for.
|
900
|
*
|
901
|
* @return bool
|
902
|
* TRUE if access is granted; otherwise FALSE.
|
903
|
*/
|
904
|
public function access($op, $account = NULL) {
|
905
|
global $user;
|
906
|
|
907
|
if (!$account) {
|
908
|
$account = $user;
|
909
|
}
|
910
|
|
911
|
// Even administrators need to go through the access system. However, to
|
912
|
// support legacy plugins, user 1 gets full access no matter what.
|
913
|
if ($account->uid == 1) {
|
914
|
return TRUE;
|
915
|
}
|
916
|
|
917
|
if (!in_array($op, array('create', 'read', 'update', 'delete', 'change layout'))) {
|
918
|
return FALSE;
|
919
|
}
|
920
|
|
921
|
if (empty($this->storage_type) || empty($this->storage_id)) {
|
922
|
return FALSE;
|
923
|
}
|
924
|
|
925
|
if ($this->storage_type == 'unknown') {
|
926
|
return FALSE;
|
927
|
}
|
928
|
|
929
|
$storage_plugin = panels_get_panels_storage_plugin($this->storage_type);
|
930
|
if (!$storage_plugin) {
|
931
|
return FALSE;
|
932
|
}
|
933
|
|
934
|
$access_callback = panels_plugin_get_function('panels_storage', $storage_plugin, 'access callback');
|
935
|
if (!$access_callback) {
|
936
|
return FALSE;
|
937
|
}
|
938
|
|
939
|
return $access_callback($this->storage_type, $this->storage_id, $op, $account);
|
940
|
}
|
941
|
|
942
|
}
|
943
|
|
944
|
/**
|
945
|
* End of 'defgroup mainapi', although other functions are specifically added later.
|
946
|
*/
|
947
|
|
948
|
/**
|
949
|
* Creates a new display, setting the ID to our magic new id.
|
950
|
*/
|
951
|
function panels_new_display() {
|
952
|
ctools_include('export');
|
953
|
$display = ctools_export_new_object('panels_display', FALSE);
|
954
|
$display->did = 'new';
|
955
|
return $display;
|
956
|
}
|
957
|
|
958
|
/**
|
959
|
* Create a new pane.
|
960
|
*
|
961
|
* @todo -- use schema API for some of this?
|
962
|
*/
|
963
|
function panels_new_pane($type, $subtype, $set_defaults = FALSE) {
|
964
|
ctools_include('export');
|
965
|
$pane = ctools_export_new_object('panels_pane', FALSE);
|
966
|
$pane->pid = 'new';
|
967
|
$pane->type = $type;
|
968
|
$pane->subtype = $subtype;
|
969
|
if ($set_defaults) {
|
970
|
ctools_include('content');
|
971
|
$content_type = ctools_get_content_type($type);
|
972
|
$content_subtype = ctools_content_get_subtype($content_type, $subtype);
|
973
|
$pane->configuration = ctools_content_get_defaults($content_type, $content_subtype);
|
974
|
}
|
975
|
drupal_alter('panels_new_pane', $pane);
|
976
|
|
977
|
return $pane;
|
978
|
}
|
979
|
|
980
|
/**
|
981
|
* Load and fill the requested $display object(s).
|
982
|
*
|
983
|
* Helper function primarily for for panels_load_display().
|
984
|
*
|
985
|
* @param array $dids
|
986
|
* An indexed array of dids to be loaded from the database.
|
987
|
*
|
988
|
* @return array
|
989
|
* An array of displays, keyed by their display dids.
|
990
|
*
|
991
|
* @todo schema API can drasticly simplify this code.
|
992
|
*/
|
993
|
function panels_load_displays($dids) {
|
994
|
$displays = array();
|
995
|
if (empty($dids) || !is_array($dids)) {
|
996
|
return $displays;
|
997
|
}
|
998
|
|
999
|
$result = db_query(
|
1000
|
"SELECT * FROM {panels_display} WHERE did IN (:dids)",
|
1001
|
array(':dids' => $dids)
|
1002
|
);
|
1003
|
|
1004
|
ctools_include('export');
|
1005
|
foreach ($result as $obj) {
|
1006
|
$displays[$obj->did] = ctools_export_unpack_object('panels_display', $obj);
|
1007
|
// Modify the hide_title field to go from a bool to an int if necessary.
|
1008
|
}
|
1009
|
|
1010
|
$result = db_query(
|
1011
|
"SELECT * FROM {panels_pane} WHERE did IN (:dids) ORDER BY did, panel, position",
|
1012
|
array(':dids' => $dids)
|
1013
|
);
|
1014
|
foreach ($result as $obj) {
|
1015
|
$pane = ctools_export_unpack_object('panels_pane', $obj);
|
1016
|
|
1017
|
$displays[$pane->did]->panels[$pane->panel][] = $pane->pid;
|
1018
|
$displays[$pane->did]->content[$pane->pid] = $pane;
|
1019
|
}
|
1020
|
return $displays;
|
1021
|
}
|
1022
|
|
1023
|
/**
|
1024
|
* Load a single display.
|
1025
|
*
|
1026
|
* @param int $did
|
1027
|
* The display id (did) of the display to be loaded.
|
1028
|
*
|
1029
|
* @return object $display
|
1030
|
* Returns a partially-loaded panels_display object. $display objects returned
|
1031
|
* from this function have only the following data:
|
1032
|
* - $display->did (the display id)
|
1033
|
* - $display->name (the 'name' of the display, where applicable - it often isn't)
|
1034
|
* - $display->layout (a string with the system name of the display's layout)
|
1035
|
* - $display->panel_settings (custom layout style settings contained in an associative array; NULL if none)
|
1036
|
* - $display->layout_settings (panel size and configuration settings for Flexible layouts; NULL if none)
|
1037
|
* - $display->css_id (the special css_id that has been assigned to this display, if any; NULL if none)
|
1038
|
* - $display->content (an array of pane objects, keyed by pane id (pid))
|
1039
|
* - $display->panels (an associative array of panel regions, each an indexed array of pids in the order they appear in that region)
|
1040
|
* - $display->cache (any relevant data from panels_simple_cache)
|
1041
|
* - $display->args
|
1042
|
* - $display->incoming_content
|
1043
|
* While all of these members are defined, $display->context is NEVER defined in the returned $display;
|
1044
|
* it must be set using one of the ctools_context_create() functions.
|
1045
|
*
|
1046
|
* @ingroup mainapi
|
1047
|
*/
|
1048
|
function panels_load_display($did) {
|
1049
|
$displays = panels_load_displays(array($did));
|
1050
|
if (!empty($displays)) {
|
1051
|
return array_shift($displays);
|
1052
|
}
|
1053
|
}
|
1054
|
|
1055
|
/**
|
1056
|
* Save a display object.
|
1057
|
*
|
1058
|
* Note that a new $display only receives a real did once it is run through
|
1059
|
* this function, and likewise for the pid of any new pane.
|
1060
|
*
|
1061
|
* Until then, a new display uses a string placeholder, 'new', in place of
|
1062
|
* a real did, and a new pane (whether on a new $display or not) appends a
|
1063
|
* universally-unique identifier (which is stored permanently in the 'uuid'
|
1064
|
* field). This format is also used in place of the real pid for exports.
|
1065
|
*
|
1066
|
* @param object $display
|
1067
|
* The display object to be saved. Passed by reference so the caller need not
|
1068
|
* use the return value for any reason except convenience.
|
1069
|
*
|
1070
|
* @return object $display
|
1071
|
* This display panel display object to return.
|
1072
|
*
|
1073
|
* @ingroup mainapi
|
1074
|
*/
|
1075
|
function panels_save_display(&$display) {
|
1076
|
$update = (isset($display->did) && is_numeric($display->did)) ? array('did') : array();
|
1077
|
if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
|
1078
|
$display->uuid = ctools_uuid_generate();
|
1079
|
}
|
1080
|
drupal_write_record('panels_display', $display, $update);
|
1081
|
|
1082
|
$pids = array();
|
1083
|
if ($update) {
|
1084
|
// Get a list of all panes currently in the database for this display so we
|
1085
|
// can know if there are panes that need to be deleted. (i.e, aren't
|
1086
|
// currently in our list of panes).
|
1087
|
$result = db_query(
|
1088
|
"SELECT pid FROM {panels_pane} WHERE did = :did",
|
1089
|
array(':did' => $display->did)
|
1090
|
);
|
1091
|
foreach ($result as $pane) {
|
1092
|
$pids[$pane->pid] = $pane->pid;
|
1093
|
}
|
1094
|
}
|
1095
|
|
1096
|
// Update all the panes.
|
1097
|
ctools_include('plugins', 'panels');
|
1098
|
ctools_include('content');
|
1099
|
|
1100
|
foreach ($display->panels as $id => $panes) {
|
1101
|
$position = 0;
|
1102
|
$new_panes = array();
|
1103
|
foreach ((array) $panes as $pid) {
|
1104
|
if (!isset($display->content[$pid])) {
|
1105
|
continue;
|
1106
|
}
|
1107
|
$pane = $display->content[$pid];
|
1108
|
$type = ctools_get_content_type($pane->type);
|
1109
|
|
1110
|
$pane->position = $position++;
|
1111
|
$pane->did = $display->did;
|
1112
|
|
1113
|
$old_pid = $pane->pid;
|
1114
|
|
1115
|
if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
|
1116
|
$pane->uuid = ctools_uuid_generate();
|
1117
|
}
|
1118
|
|
1119
|
drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array());
|
1120
|
|
1121
|
// Allow other modules to take action after a pane is saved.
|
1122
|
if ($pane->pid == $old_pid) {
|
1123
|
module_invoke_all('panels_pane_update', $pane);
|
1124
|
}
|
1125
|
else {
|
1126
|
module_invoke_all('panels_pane_insert', $pane);
|
1127
|
}
|
1128
|
|
1129
|
if ($pane->pid != $old_pid) {
|
1130
|
// Remove the old new-* entry from the displays content.
|
1131
|
unset($display->content[$pid]);
|
1132
|
|
1133
|
// Put it back so our pids and positions can be used.
|
1134
|
$display->content[$pane->pid] = $pane;
|
1135
|
|
1136
|
// If the title pane was one of our panes that just got its ID changed,
|
1137
|
// we need to change it in the database, too.
|
1138
|
if (isset($display->title_pane) && $display->title_pane == $old_pid) {
|
1139
|
$display->title_pane = $pane->pid;
|
1140
|
// Do a simple update query to write it so we don't have to rewrite
|
1141
|
// the whole record. We can't just save writing the whole record here
|
1142
|
// because it was needed to get the did. Chicken, egg, more chicken.
|
1143
|
db_update('panels_display')
|
1144
|
->fields(array(
|
1145
|
'title_pane' => $pane->pid,
|
1146
|
))
|
1147
|
->condition('did', $display->did)
|
1148
|
->execute();
|
1149
|
}
|
1150
|
}
|
1151
|
|
1152
|
// re-add this to the list of content for this panel.
|
1153
|
$new_panes[] = $pane->pid;
|
1154
|
|
1155
|
// Remove this from the list of panes scheduled for deletion.
|
1156
|
if (isset($pids[$pane->pid])) {
|
1157
|
unset($pids[$pane->pid]);
|
1158
|
}
|
1159
|
}
|
1160
|
|
1161
|
$display->panels[$id] = $new_panes;
|
1162
|
}
|
1163
|
if (!empty($pids)) {
|
1164
|
// Allow other modules to take action before a panes are deleted.
|
1165
|
module_invoke_all('panels_pane_delete', $pids);
|
1166
|
db_delete('panels_pane')->condition('pid', $pids)->execute();
|
1167
|
}
|
1168
|
|
1169
|
// Clear any cached content for this display.
|
1170
|
panels_clear_cached_content($display);
|
1171
|
|
1172
|
// Allow other modules to take action when a display is saved.
|
1173
|
module_invoke_all('panels_display_save', $display);
|
1174
|
|
1175
|
// Log the change to watchdog, using the same style as node.module.
|
1176
|
$watchdog_args = array('%did' => $display->did);
|
1177
|
if (!empty($display->title)) {
|
1178
|
$watchdog_args['%title'] = $display->title;
|
1179
|
watchdog('content', 'Panels: saved display "%title" with display id %did', $watchdog_args, WATCHDOG_NOTICE);
|
1180
|
}
|
1181
|
else {
|
1182
|
watchdog('content', 'Panels: saved display with id %did', $watchdog_args, WATCHDOG_NOTICE);
|
1183
|
}
|
1184
|
|
1185
|
// To be nice, even though we have a reference.
|
1186
|
return $display;
|
1187
|
}
|
1188
|
|
1189
|
/**
|
1190
|
* Delete a display.
|
1191
|
*/
|
1192
|
function panels_delete_display($display) {
|
1193
|
if (is_object($display)) {
|
1194
|
$did = $display->did;
|
1195
|
}
|
1196
|
else {
|
1197
|
$did = $display;
|
1198
|
}
|
1199
|
module_invoke_all('panels_delete_display', $did);
|
1200
|
db_delete('panels_display')->condition('did', $did)->execute();
|
1201
|
db_delete('panels_pane')->condition('did', $did)->execute();
|
1202
|
}
|
1203
|
|
1204
|
/**
|
1205
|
* Exports the provided display into portable code.
|
1206
|
*
|
1207
|
* This function is primarily intended as a mechanism for cloning displays.
|
1208
|
* It generates an exact replica (in code) of the provided $display, with
|
1209
|
* the exception that it replaces all ids (dids and pids) with place-holder
|
1210
|
* values (consisting of the display or pane's uuid, with a 'new-' prefix).
|
1211
|
*
|
1212
|
* Only once panels_save_display() is called on the code version of $display
|
1213
|
* will the exported display be written to the database and permanently saved.
|
1214
|
*
|
1215
|
* @param object $display
|
1216
|
* This export function does no loading of additional data about the provided
|
1217
|
* display. Consequently, the caller should make sure that all the desired
|
1218
|
* data has been loaded into the $display before calling this function.
|
1219
|
* @param string $prefix
|
1220
|
* A string prefix that is prepended to each line of exported code. This is
|
1221
|
* primarily used for prepending a double space when exporting so that the
|
1222
|
* code indents and lines up nicely.
|
1223
|
*
|
1224
|
* @return string $output
|
1225
|
* The passed-in $display expressed as code, ready to be imported. Import by
|
1226
|
* running eval($output) in the caller function; doing so will create a new
|
1227
|
* $display variable with all the exported values. Note that if you have
|
1228
|
* already defined a $display variable in the same scope as where you eval(),
|
1229
|
* your existing $display variable WILL be overwritten.
|
1230
|
*
|
1231
|
* @see panels_page_export() or _panels_page_fetch_display() for samples.
|
1232
|
*
|
1233
|
* @ingroup mainapi
|
1234
|
*/
|
1235
|
function panels_export_display($display, $prefix = '') {
|
1236
|
ctools_include('export');
|
1237
|
if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
|
1238
|
$display->uuid = ctools_uuid_generate();
|
1239
|
}
|
1240
|
$display->did = 'new-' . $display->uuid;
|
1241
|
$output = ctools_export_object('panels_display', $display, $prefix);
|
1242
|
|
1243
|
// Initialize empty properties.
|
1244
|
$output .= $prefix . '$display->content = array()' . ";\n";
|
1245
|
$output .= $prefix . '$display->panels = array()' . ";\n";
|
1246
|
$panels = array();
|
1247
|
|
1248
|
$title_pid = 0;
|
1249
|
if (!empty($display->content)) {
|
1250
|
$region_counters = array();
|
1251
|
foreach ($display->content as $pane) {
|
1252
|
|
1253
|
if (!isset($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
|
1254
|
$pane->uuid = ctools_uuid_generate();
|
1255
|
}
|
1256
|
$pid = 'new-' . $pane->uuid;
|
1257
|
|
1258
|
if ($pane->pid == $display->title_pane) {
|
1259
|
$title_pid = $pid;
|
1260
|
}
|
1261
|
$pane->pid = $pid;
|
1262
|
$output .= ctools_export_object('panels_pane', $pane, $prefix);
|
1263
|
$output .= $prefix . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n";
|
1264
|
if (!isset($region_counters[$pane->panel])) {
|
1265
|
$region_counters[$pane->panel] = 0;
|
1266
|
}
|
1267
|
$output .= $prefix . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ . '] = \'' . $pane->pid . "';\n";
|
1268
|
}
|
1269
|
}
|
1270
|
$output .= $prefix . '$display->hide_title = ';
|
1271
|
switch ($display->hide_title) {
|
1272
|
case PANELS_TITLE_FIXED:
|
1273
|
$output .= 'PANELS_TITLE_FIXED';
|
1274
|
break;
|
1275
|
|
1276
|
case PANELS_TITLE_NONE:
|
1277
|
$output .= 'PANELS_TITLE_NONE';
|
1278
|
break;
|
1279
|
|
1280
|
case PANELS_TITLE_PANE:
|
1281
|
$output .= 'PANELS_TITLE_PANE';
|
1282
|
break;
|
1283
|
}
|
1284
|
$output .= ";\n";
|
1285
|
|
1286
|
$output .= $prefix . '$display->title_pane =' . " '$title_pid';\n";
|
1287
|
return $output;
|
1288
|
}
|
1289
|
|
1290
|
/**
|
1291
|
* Panels Render Display.
|
1292
|
*
|
1293
|
* Render a display by loading the content into an appropriate
|
1294
|
* array and then passing through to panels_render_layout.
|
1295
|
*
|
1296
|
* if $incoming_content is NULL, default content will be applied. Use
|
1297
|
* an empty string to indicate no content.
|
1298
|
*
|
1299
|
* @ingroup hook_invocations
|
1300
|
*/
|
1301
|
function panels_render_display(&$display, $renderer = NULL) {
|
1302
|
ctools_include('plugins', 'panels');
|
1303
|
ctools_include('context');
|
1304
|
|
1305
|
if (!empty($display->context)) {
|
1306
|
if ($form_context = ctools_context_get_form($display->context)) {
|
1307
|
$form_context->form['#theme'] = 'panels_render_display_form';
|
1308
|
if (empty($form_context->form['#theme_wrappers']) || !in_array('form', $form_context->form['#theme_wrappers'])) {
|
1309
|
$form_context->form['#theme_wrappers'][] = 'form';
|
1310
|
}
|
1311
|
$form_context->form['#display'] = &$display;
|
1312
|
return $form_context->form;
|
1313
|
}
|
1314
|
}
|
1315
|
return $display->render($renderer);
|
1316
|
}
|
1317
|
|
1318
|
/**
|
1319
|
* Theme function to render our panel as a form.
|
1320
|
*
|
1321
|
* When rendering a display as a form, the entire display needs to be
|
1322
|
* inside the <form> tag so that the form can be spread across the
|
1323
|
* panes. This sets up the form system to be the main caller and we
|
1324
|
* then operate as a theme function of the form.
|
1325
|
*/
|
1326
|
function theme_panels_render_display_form($vars) {
|
1327
|
return $vars['element']['#display']->render();
|
1328
|
}
|
1329
|
|
1330
|
/**
|
1331
|
* Panels layout icon function.
|
1332
|
*/
|
1333
|
function panels_print_layout_icon($id, $layout, $title = NULL) {
|
1334
|
ctools_add_css('panels_admin', 'panels');
|
1335
|
$file = $layout['path'] . '/' . $layout['icon'];
|
1336
|
return theme(
|
1337
|
'panels_layout_icon',
|
1338
|
array(
|
1339
|
'id' => $id,
|
1340
|
'image' => theme(
|
1341
|
'image',
|
1342
|
array(
|
1343
|
'path' => $file,
|
1344
|
'alt' => strip_tags($layout['title']),
|
1345
|
'title' => strip_tags($layout['description']),
|
1346
|
)
|
1347
|
),
|
1348
|
'title' => $title,
|
1349
|
)
|
1350
|
);
|
1351
|
}
|
1352
|
|
1353
|
/**
|
1354
|
* Theme the layout icon image.
|
1355
|
*
|
1356
|
* @todo move to theme.inc
|
1357
|
*/
|
1358
|
function theme_panels_layout_icon($vars) {
|
1359
|
$id = $vars['id'];
|
1360
|
$image = $vars['image'];
|
1361
|
$title = $vars['title'];
|
1362
|
|
1363
|
$output = '<div class="layout-icon">';
|
1364
|
$output .= $image;
|
1365
|
if ($title) {
|
1366
|
$output .= '<div class="caption">' . $title . '</div>';
|
1367
|
}
|
1368
|
$output .= '</div>';
|
1369
|
return $output;
|
1370
|
}
|
1371
|
|
1372
|
/**
|
1373
|
* Theme the layout link image.
|
1374
|
*
|
1375
|
* @layout
|
1376
|
*
|
1377
|
* @todo Why isn't this a template at this point?
|
1378
|
* @todo Why does this take 4 arguments but only makes use of two?
|
1379
|
*/
|
1380
|
function theme_panels_layout_link($vars) {
|
1381
|
$options = !empty($vars['options']) ? $vars['options'] : array();
|
1382
|
if (!isset($options['attributes']['class'])) {
|
1383
|
$options['attributes']['class'] = array();
|
1384
|
}
|
1385
|
$options['attributes']['class'] = array_merge($options['attributes']['class'], $vars['class']);
|
1386
|
|
1387
|
$content = $vars['image'] . '<span>' . $vars['title'] . '</span>';
|
1388
|
return l($content, $vars['link'], $options + array('html' => TRUE));
|
1389
|
}
|
1390
|
|
1391
|
/**
|
1392
|
* Print the layout link. Sends out to a theme function.
|
1393
|
*
|
1394
|
* @layout
|
1395
|
*/
|
1396
|
function panels_print_layout_link($id, $layout, $link, $options = array(), $current_layout = FALSE) {
|
1397
|
if (isset($options['query']['q'])) {
|
1398
|
unset($options['query']['q']);
|
1399
|
}
|
1400
|
|
1401
|
// Setup classes for layout link, including current-layout information.
|
1402
|
$class = array('layout-link');
|
1403
|
if ($current_layout == $id) {
|
1404
|
$options['attributes']['class'][] = 'current-layout-link';
|
1405
|
$class[] = 'current-layout';
|
1406
|
}
|
1407
|
|
1408
|
ctools_add_css('panels_admin', 'panels');
|
1409
|
$file = $layout['path'] . '/' . $layout['icon'];
|
1410
|
$image = theme('image', array('path' => $file));
|
1411
|
return theme(
|
1412
|
'panels_layout_link',
|
1413
|
array(
|
1414
|
'title' => $layout['title'],
|
1415
|
'image' => $image,
|
1416
|
'link' => $link,
|
1417
|
'class' => $class,
|
1418
|
'options' => $options,
|
1419
|
)
|
1420
|
);
|
1421
|
}
|
1422
|
|
1423
|
|
1424
|
/**
|
1425
|
* Panels Get legacy state.
|
1426
|
*
|
1427
|
* Gateway to the PanelsLegacyState class/object, which does all legacy state
|
1428
|
* checks and provides information about the cause of legacy states as needed.
|
1429
|
*
|
1430
|
* @return PanelsLegacyState $legacy
|
1431
|
* Returns a legacy panels state.
|
1432
|
*/
|
1433
|
function panels_get_legacy_state() {
|
1434
|
static $legacy = NULL;
|
1435
|
if (!isset($legacy)) {
|
1436
|
ctools_include('legacy', 'panels');
|
1437
|
$legacy = new PanelsLegacyState();
|
1438
|
}
|
1439
|
return $legacy;
|
1440
|
}
|
1441
|
|
1442
|
/**
|
1443
|
* Get the display that is currently being rendered as a page.
|
1444
|
*
|
1445
|
* Unlike in previous versions of this, this only returns the display,
|
1446
|
* not the page itself, because there are a number of different ways
|
1447
|
* to get to this point. It is hoped that the page data isn't needed
|
1448
|
* at this point. If it turns out there is, we will do something else to
|
1449
|
* get that functionality.
|
1450
|
*/
|
1451
|
function panels_get_current_page_display($change = NULL) {
|
1452
|
static $display = NULL;
|
1453
|
if ($change) {
|
1454
|
$display = $change;
|
1455
|
}
|
1456
|
|
1457
|
return $display;
|
1458
|
}
|
1459
|
|
1460
|
/**
|
1461
|
* Clean up the panel pane variables for the template.
|
1462
|
*/
|
1463
|
function template_preprocess_panels_pane(&$vars) {
|
1464
|
$content = &$vars['content'];
|
1465
|
|
1466
|
$vars['contextual_links'] = array();
|
1467
|
$vars['classes_array'] = array();
|
1468
|
$vars['admin_links'] = '';
|
1469
|
|
1470
|
if (module_exists('contextual') && user_access('access contextual links')) {
|
1471
|
$links = array();
|
1472
|
// These are specified by the content.
|
1473
|
if (!empty($content->admin_links)) {
|
1474
|
$links += $content->admin_links;
|
1475
|
}
|
1476
|
|
1477
|
// Take any that may have been in the render array we were given and
|
1478
|
// move them up so they appear outside the pane properly.
|
1479
|
if (is_array($content->content) && isset($content->content['#contextual_links'])) {
|
1480
|
$element = array(
|
1481
|
'#type' => 'contextual_links',
|
1482
|
'#contextual_links' => $content->content['#contextual_links'],
|
1483
|
);
|
1484
|
unset($content->content['#contextual_links']);
|
1485
|
|
1486
|
// Add content to $element array.
|
1487
|
if (is_array($content->content)) {
|
1488
|
$element['#element'] = $content->content;
|
1489
|
}
|
1490
|
|
1491
|
$element = contextual_pre_render_links($element);
|
1492
|
if (!empty($element['#links'])) {
|
1493
|
$links += $element['#links'];
|
1494
|
}
|
1495
|
}
|
1496
|
|
1497
|
if ($links) {
|
1498
|
$build = array(
|
1499
|
'#prefix' => '<div class="contextual-links-wrapper">',
|
1500
|
'#suffix' => '</div>',
|
1501
|
'#theme' => 'links__contextual',
|
1502
|
'#links' => $links,
|
1503
|
'#attributes' => array('class' => array('contextual-links')),
|
1504
|
'#attached' => array(
|
1505
|
'library' => array(array('contextual', 'contextual-links')),
|
1506
|
),
|
1507
|
);
|
1508
|
$vars['classes_array'][] = 'contextual-links-region';
|
1509
|
$vars['admin_links'] = drupal_render($build);
|
1510
|
}
|
1511
|
}
|
1512
|
|
1513
|
// Basic classes.
|
1514
|
$vars['classes_array'][] = 'panel-pane';
|
1515
|
$vars['id'] = '';
|
1516
|
|
1517
|
// Add some usable classes based on type/subtype.
|
1518
|
ctools_include('cleanstring');
|
1519
|
$type_class = $content->type ? 'pane-' . ctools_cleanstring($content->type, array('lower case' => TRUE)) : '';
|
1520
|
$subtype_class = $content->subtype ? 'pane-' . ctools_cleanstring($content->subtype, array('lower case' => TRUE)) : '';
|
1521
|
|
1522
|
// Sometimes type and subtype are the same. Avoid redundant classes.
|
1523
|
$vars['classes_array'][] = $type_class;
|
1524
|
if ($type_class != $subtype_class) {
|
1525
|
$vars['classes_array'][] = $subtype_class;
|
1526
|
}
|
1527
|
|
1528
|
// Add id and custom class if sent in.
|
1529
|
if (!empty($content->content)) {
|
1530
|
if (!empty($content->css_id)) {
|
1531
|
$vars['id'] = ' id="' . $content->css_id . '"';
|
1532
|
}
|
1533
|
if (!empty($content->css_class)) {
|
1534
|
$vars['classes_array'][] = $content->css_class;
|
1535
|
}
|
1536
|
}
|
1537
|
|
1538
|
// Set up some placeholders for constructing template file names.
|
1539
|
$base = 'panels_pane';
|
1540
|
$delimiter = '__';
|
1541
|
|
1542
|
// Add template file suggestion for content type and sub-type.
|
1543
|
$vars['theme_hook_suggestions'][] = $base . $delimiter . $content->type;
|
1544
|
$vars['theme_hook_suggestions'][] = $base . $delimiter . strtr(ctools_cleanstring($content->type, array('lower case' => TRUE)), '-', '_') . $delimiter . strtr(ctools_cleanstring($content->subtype, array('lower case' => TRUE)), '-', '_');
|
1545
|
|
1546
|
$vars['pane_prefix'] = !empty($content->pane_prefix) ? $content->pane_prefix : '';
|
1547
|
$vars['pane_suffix'] = !empty($content->pane_suffix) ? $content->pane_suffix : '';
|
1548
|
|
1549
|
$vars['title'] = !empty($content->title) ? $content->title : '';
|
1550
|
$vars['title_heading'] = !empty($content->title_heading) ? $content->title_heading : variable_get('override_title_heading', 'h2');
|
1551
|
$vars['title_attributes_array']['class'][] = 'pane-title';
|
1552
|
|
1553
|
$vars['feeds'] = !empty($content->feeds) ? implode(' ', $content->feeds) : '';
|
1554
|
|
1555
|
$vars['links'] = !empty($content->links) ? theme('links', array('links' => $content->links)) : '';
|
1556
|
$vars['more'] = '';
|
1557
|
if (!empty($content->more)) {
|
1558
|
if (empty($content->more['title'])) {
|
1559
|
$content->more['title'] = t('more');
|
1560
|
}
|
1561
|
$vars['more'] = l($content->more['title'], $content->more['href'], $content->more);
|
1562
|
}
|
1563
|
|
1564
|
if (!empty($content->attributes)) {
|
1565
|
$vars['attributes_array'] = array_merge($vars['attributes_array'], $content->attributes);
|
1566
|
}
|
1567
|
|
1568
|
$vars['content'] = !empty($content->content) ? $content->content : '';
|
1569
|
|
1570
|
}
|
1571
|
|
1572
|
/**
|
1573
|
* Route Panels' AJAX calls to the correct object.
|
1574
|
*
|
1575
|
* Panels' AJAX is controlled mostly by renderer objects. This menu callback
|
1576
|
* accepts the incoming request, figures out which object should handle the
|
1577
|
* request, and attempts to route it. If no object can be found, the default
|
1578
|
* Panels editor object is used.
|
1579
|
*
|
1580
|
* Calls are routed via the ajax_* method space. For example, if visiting
|
1581
|
* panels/ajax/add-pane then $renderer::ajax_add_pane() will be called.
|
1582
|
* This means commands can be added without having to create new callbacks.
|
1583
|
*
|
1584
|
* The first argument *must always* be the cache key so that a cache object
|
1585
|
* can be passed through. Other arguments will be passed through untouched
|
1586
|
* so that the method can do whatever it needs to do.
|
1587
|
*/
|
1588
|
function panels_ajax_router() {
|
1589
|
$args = func_get_args();
|
1590
|
if (count($args) < 3) {
|
1591
|
return MENU_NOT_FOUND;
|
1592
|
}
|
1593
|
|
1594
|
ctools_include('display-edit', 'panels');
|
1595
|
ctools_include('plugins', 'panels');
|
1596
|
ctools_include('ajax');
|
1597
|
ctools_include('modal');
|
1598
|
ctools_include('context');
|
1599
|
ctools_include('content');
|
1600
|
|
1601
|
$plugin_name = array_shift($args);
|
1602
|
$method = array_shift($args);
|
1603
|
$cache_key = array_shift($args);
|
1604
|
|
1605
|
$plugin = panels_get_display_renderer($plugin_name);
|
1606
|
if (!$plugin) {
|
1607
|
// This is the default renderer for handling AJAX commands.
|
1608
|
$plugin = panels_get_display_renderer('editor');
|
1609
|
}
|
1610
|
|
1611
|
$cache = panels_edit_cache_get($cache_key);
|
1612
|
if (empty($cache)) {
|
1613
|
return MENU_ACCESS_DENIED;
|
1614
|
}
|
1615
|
|
1616
|
$renderer = panels_get_renderer_handler($plugin, $cache->display);
|
1617
|
if (!$renderer) {
|
1618
|
return MENU_ACCESS_DENIED;
|
1619
|
}
|
1620
|
|
1621
|
$method = 'ajax_' . str_replace('-', '_', $method);
|
1622
|
if (!method_exists($renderer, $method)) {
|
1623
|
return MENU_NOT_FOUND;
|
1624
|
}
|
1625
|
|
1626
|
$renderer->cache = &$cache;
|
1627
|
ctools_include('cleanstring');
|
1628
|
$renderer->clean_key = ctools_cleanstring($cache_key);
|
1629
|
|
1630
|
$op = $renderer->get_panels_storage_op_for_ajax($method);
|
1631
|
if (!$cache->display->access($op)) {
|
1632
|
return MENU_ACCESS_DENIED;
|
1633
|
}
|
1634
|
|
1635
|
$output = call_user_func_array(array($renderer, $method), $args);
|
1636
|
|
1637
|
if (empty($output) && !empty($renderer->commands)) {
|
1638
|
return array(
|
1639
|
'#type' => 'ajax',
|
1640
|
'#commands' => $renderer->commands,
|
1641
|
);
|
1642
|
}
|
1643
|
else {
|
1644
|
return $output;
|
1645
|
}
|
1646
|
}
|
1647
|
|
1648
|
/**
|
1649
|
* Panels caching functions and callbacks.
|
1650
|
*
|
1651
|
* When editing displays and the like, Panels has a caching system that relies
|
1652
|
* on a callback to determine where to get the actual cache.
|
1653
|
*
|
1654
|
* @todo This system needs to be better documented so that it can be better used.
|
1655
|
*/
|
1656
|
|
1657
|
/**
|
1658
|
* Get an object from cache.
|
1659
|
*/
|
1660
|
function panels_cache_get($obj, $did, $skip_cache = FALSE) {
|
1661
|
ctools_include('object-cache');
|
1662
|
// We often store contexts in cache, so let's just make sure we can load them.
|
1663
|
ctools_include('context');
|
1664
|
return ctools_object_cache_get($obj, 'panels_display:' . $did, $skip_cache);
|
1665
|
}
|
1666
|
|
1667
|
/**
|
1668
|
* Save the edited object into the cache.
|
1669
|
*/
|
1670
|
function panels_cache_set($obj, $did, $cache) {
|
1671
|
ctools_include('object-cache');
|
1672
|
return ctools_object_cache_set($obj, 'panels_display:' . $did, $cache);
|
1673
|
}
|
1674
|
|
1675
|
/**
|
1676
|
* Clear a object from the cache; used if the editing is aborted.
|
1677
|
*/
|
1678
|
function panels_cache_clear($obj, $did) {
|
1679
|
ctools_include('object-cache');
|
1680
|
return ctools_object_cache_clear($obj, 'panels_display:' . $did);
|
1681
|
}
|
1682
|
|
1683
|
/**
|
1684
|
* Create the default cache for editing panel displays.
|
1685
|
*
|
1686
|
* If an application is using the Panels display editor without having
|
1687
|
* specified a cache key, this method can be used to create the default
|
1688
|
* cache.
|
1689
|
*/
|
1690
|
function panels_edit_cache_get_default(&$display, $content_types = NULL, $title = FALSE) {
|
1691
|
if (empty($content_types)) {
|
1692
|
$content_types = ctools_content_get_available_types();
|
1693
|
}
|
1694
|
|
1695
|
$display->cache_key = $display->did;
|
1696
|
panels_cache_clear('display', $display->did);
|
1697
|
|
1698
|
$cache = new stdClass();
|
1699
|
$cache->display = &$display;
|
1700
|
$cache->content_types = $content_types;
|
1701
|
$cache->display_title = $title;
|
1702
|
|
1703
|
panels_edit_cache_set($cache);
|
1704
|
return $cache;
|
1705
|
}
|
1706
|
|
1707
|
/**
|
1708
|
* Panels Editor Cache Get.
|
1709
|
*
|
1710
|
* Method to allow modules to provide their own caching mechanism for the
|
1711
|
* display editor.
|
1712
|
*/
|
1713
|
function panels_edit_cache_get($cache_key) {
|
1714
|
if (strpos($cache_key, ':') !== FALSE) {
|
1715
|
list($module, $argument) = explode(':', $cache_key, 2);
|
1716
|
return module_invoke($module, 'panels_cache_get', $argument);
|
1717
|
}
|
1718
|
|
1719
|
// Fall back to our normal method.
|
1720
|
return panels_cache_get('display', $cache_key);
|
1721
|
}
|
1722
|
|
1723
|
/**
|
1724
|
* Panels Editor Cache Set.
|
1725
|
*
|
1726
|
* Method to allow modules to provide their own caching mechanism for the
|
1727
|
* display editor.
|
1728
|
*/
|
1729
|
function panels_edit_cache_set($cache) {
|
1730
|
$cache_key = $cache->display->cache_key;
|
1731
|
if (strpos($cache_key, ':') !== FALSE) {
|
1732
|
list($module, $argument) = explode(':', $cache_key, 2);
|
1733
|
return module_invoke($module, 'panels_cache_set', $argument, $cache);
|
1734
|
}
|
1735
|
|
1736
|
// Fall back to our normal method.
|
1737
|
return panels_cache_set('display', $cache_key, $cache);
|
1738
|
}
|
1739
|
|
1740
|
/**
|
1741
|
* Panels Editor Cache Save.
|
1742
|
*
|
1743
|
* Method to allow modules to provide their own mechanism to write the
|
1744
|
* cache used in the display editor.
|
1745
|
*/
|
1746
|
function panels_edit_cache_save($cache) {
|
1747
|
$cache_key = $cache->display->cache_key;
|
1748
|
if (strpos($cache_key, ':') !== FALSE) {
|
1749
|
list($module, $argument) = explode(':', $cache_key, 2);
|
1750
|
if (function_exists($module . '_panels_cache_save')) {
|
1751
|
return module_invoke($module, 'panels_cache_save', $argument, $cache);
|
1752
|
}
|
1753
|
}
|
1754
|
|
1755
|
// Fall back to our normal method.
|
1756
|
return panels_save_display($cache->display);
|
1757
|
}
|
1758
|
|
1759
|
/**
|
1760
|
* Panels Editor Cache Clear.
|
1761
|
*
|
1762
|
* Method to allow modules to provide their own mechanism to clear the
|
1763
|
* cache used in the display editor.
|
1764
|
*/
|
1765
|
function panels_edit_cache_clear($cache) {
|
1766
|
$cache_key = $cache->display->cache_key;
|
1767
|
if (strpos($cache_key, ':') !== FALSE) {
|
1768
|
list($module, $argument) = explode(':', $cache_key, 2);
|
1769
|
if (function_exists($module . '_panels_cache_clear')) {
|
1770
|
return module_invoke($module, 'panels_cache_clear', $argument, $cache);
|
1771
|
}
|
1772
|
}
|
1773
|
|
1774
|
// Fall back to our normal method.
|
1775
|
return panels_cache_clear('display', $cache_key);
|
1776
|
}
|
1777
|
|
1778
|
/**
|
1779
|
* Method to allow modules to provide a mechanism to break locks.
|
1780
|
*/
|
1781
|
function panels_edit_cache_break_lock($cache) {
|
1782
|
if (empty($cache->locked)) {
|
1783
|
return;
|
1784
|
}
|
1785
|
|
1786
|
$cache_key = $cache->display->cache_key;
|
1787
|
if (strpos($cache_key, ':') !== FALSE) {
|
1788
|
list($module, $argument) = explode(':', $cache_key, 2);
|
1789
|
if (function_exists($module . '_panels_cache_break_lock')) {
|
1790
|
return module_invoke($module, 'panels_cache_break_lock', $argument, $cache);
|
1791
|
}
|
1792
|
}
|
1793
|
|
1794
|
// Normal panel display editing has no locks, so we do nothing if there is
|
1795
|
// no fallback.
|
1796
|
}
|
1797
|
|
1798
|
/**
|
1799
|
* Callbacks on behalf of the panel_context plugin.
|
1800
|
*
|
1801
|
* The panel_context plugin lets Panels be used in page manager. These
|
1802
|
* callbacks allow the display editing system to use the page manager
|
1803
|
* cache rather than the default display cache. They are routed by the cache
|
1804
|
* key via panels_edit_cache_* functions.
|
1805
|
*/
|
1806
|
|
1807
|
/**
|
1808
|
* Get display edit cache on behalf of panel context.
|
1809
|
*
|
1810
|
* The key is the second half of the key in this form:
|
1811
|
* panel_context:TASK_NAME::HANDLER_NAME::args::url;
|
1812
|
*/
|
1813
|
function panel_context_panels_cache_get($key) {
|
1814
|
ctools_include('common', 'panels');
|
1815
|
ctools_include('context');
|
1816
|
ctools_include('context-task-handler');
|
1817
|
// This loads the panel context inc even if we don't use the plugin.
|
1818
|
$plugin = page_manager_get_task_handler('panel_context');
|
1819
|
|
1820
|
list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
|
1821
|
$page = page_manager_get_page_cache($task_name);
|
1822
|
if (isset($page->display_cache[$handler_name])) {
|
1823
|
return $page->display_cache[$handler_name];
|
1824
|
}
|
1825
|
|
1826
|
if ($handler_name) {
|
1827
|
$handler = &$page->handlers[$handler_name];
|
1828
|
}
|
1829
|
else {
|
1830
|
$handler = &$page->new_handler;
|
1831
|
}
|
1832
|
$cache = new stdClass();
|
1833
|
|
1834
|
$task = page_manager_get_task($page->task_id);
|
1835
|
$arguments = array();
|
1836
|
if ($args) {
|
1837
|
$arguments = explode('\\', $args);
|
1838
|
$contexts = ctools_context_handler_get_task_contexts($task, $page->subtask, $arguments);
|
1839
|
$contexts = ctools_context_handler_get_handler_contexts($contexts, $handler);
|
1840
|
}
|
1841
|
else {
|
1842
|
$contexts = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
|
1843
|
}
|
1844
|
|
1845
|
$cache->display = &panels_panel_context_get_display($handler);
|
1846
|
$cache->display->context = $contexts;
|
1847
|
$cache->display->cache_key = 'panel_context:' . $key;
|
1848
|
$cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
|
1849
|
$cache->display_title = TRUE;
|
1850
|
$cache->locked = $page->locked;
|
1851
|
|
1852
|
return $cache;
|
1853
|
}
|
1854
|
|
1855
|
/**
|
1856
|
* Get the Page Manager cache for the panel_context plugin.
|
1857
|
*/
|
1858
|
function _panel_context_panels_cache_get_page_cache($key, $cache) {
|
1859
|
list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
|
1860
|
$page = page_manager_get_page_cache($task_name);
|
1861
|
$page->display_cache[$handler_name] = $cache;
|
1862
|
if ($handler_name) {
|
1863
|
$page->handlers[$handler_name]->conf['display'] = $cache->display;
|
1864
|
$page->handler_info[$handler_name]['changed'] |= PAGE_MANAGER_CHANGED_CACHED;
|
1865
|
}
|
1866
|
else {
|
1867
|
$page->new_handler->conf['display'] = $cache->display;
|
1868
|
}
|
1869
|
|
1870
|
return $page;
|
1871
|
}
|
1872
|
|
1873
|
/**
|
1874
|
* Store a display edit in progress in the page cache.
|
1875
|
*/
|
1876
|
function panel_context_panels_cache_set($key, $cache) {
|
1877
|
$page = _panel_context_panels_cache_get_page_cache($key, $cache);
|
1878
|
page_manager_set_page_cache($page);
|
1879
|
}
|
1880
|
|
1881
|
/**
|
1882
|
* Save all changes made to a display using the Page Manager page cache.
|
1883
|
*/
|
1884
|
function panel_context_panels_cache_clear($key, $cache) {
|
1885
|
$page = _panel_context_panels_cache_get_page_cache($key, $cache);
|
1886
|
page_manager_clear_page_cache($page->task_name);
|
1887
|
}
|
1888
|
|
1889
|
/**
|
1890
|
* Save all changes made to a display using the Page Manager page cache.
|
1891
|
*/
|
1892
|
function panel_context_panels_cache_save($key, $cache) {
|
1893
|
$page = _panel_context_panels_cache_get_page_cache($key, $cache);
|
1894
|
page_manager_save_page_cache($page);
|
1895
|
}
|
1896
|
|
1897
|
/**
|
1898
|
* Break the lock on a page manager page.
|
1899
|
*/
|
1900
|
function panel_context_panels_cache_break_lock($key, $cache) {
|
1901
|
$page = _panel_context_panels_cache_get_page_cache($key, $cache);
|
1902
|
ctools_object_cache_clear_all('page_manager_page', $page->task_name);
|
1903
|
}
|
1904
|
|
1905
|
/**
|
1906
|
* Callbacks on behalf of the panels page wizards.
|
1907
|
*
|
1908
|
* The page wizards are a pluggable set of 'wizards' to make it easy to create
|
1909
|
* specific types of pages based upon whatever someone felt like putting
|
1910
|
* together. Since they will very often have content editing, we provide
|
1911
|
* a generic mechanism to allow them to store their editing cache in the
|
1912
|
* wizard cache.
|
1913
|
*
|
1914
|
* For them to use this mechanism, they just need to use:
|
1915
|
* $cache = panels_edit_cache_get('panels_page_wizard:' . $plugin['name']);.
|
1916
|
*/
|
1917
|
|
1918
|
/**
|
1919
|
* Get display edit cache for the panels mini export UI.
|
1920
|
*
|
1921
|
* The key is the second half of the key in this form:
|
1922
|
* panels_page_wizard:TASK_NAME:HANDLER_NAME;
|
1923
|
*/
|
1924
|
function panels_page_wizard_panels_cache_get($key) {
|
1925
|
ctools_include('page-wizard');
|
1926
|
ctools_include('context');
|
1927
|
$wizard_cache = page_manager_get_wizard_cache($key);
|
1928
|
if (isset($wizard_cache->display_cache)) {
|
1929
|
return $wizard_cache->display_cache;
|
1930
|
}
|
1931
|
|
1932
|
ctools_include('common', 'panels');
|
1933
|
$cache = new stdClass();
|
1934
|
$cache->display = $wizard_cache->display;
|
1935
|
$cache->display->context = !empty($wizard_cache->context) ? $wizard_cache->context : array();
|
1936
|
$cache->display->cache_key = 'panels_page_wizard:' . $key;
|
1937
|
$cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
|
1938
|
$cache->display_title = TRUE;
|
1939
|
|
1940
|
return $cache;
|
1941
|
}
|
1942
|
|
1943
|
/**
|
1944
|
* Store a display edit in progress in the page cache.
|
1945
|
*/
|
1946
|
function panels_page_wizard_panels_cache_set($key, $cache) {
|
1947
|
ctools_include('page-wizard');
|
1948
|
$wizard_cache = page_manager_get_wizard_cache($key);
|
1949
|
$wizard_cache->display_cache = $cache;
|
1950
|
page_manager_set_wizard_cache($wizard_cache);
|
1951
|
}
|
1952
|
|
1953
|
/**
|
1954
|
* Implements hook_default_page_manager_handlers_alter().
|
1955
|
*
|
1956
|
* If a default Panels display has no storage type, set it.
|
1957
|
*/
|
1958
|
function panels_default_page_manager_handlers_alter(&$handlers) {
|
1959
|
foreach ($handlers as &$handler) {
|
1960
|
if ($handler->handler == 'panel_context') {
|
1961
|
$display =& $handler->conf['display'];
|
1962
|
if (empty($display->storage_type)) {
|
1963
|
$display->storage_type = 'page_manager';
|
1964
|
$display->storage_id = $handler->name;
|
1965
|
}
|
1966
|
}
|
1967
|
}
|
1968
|
}
|
1969
|
|
1970
|
/**
|
1971
|
* Implements hook_default_page_manager_pages_alter().
|
1972
|
*/
|
1973
|
function panels_default_page_manager_pages_alter(&$pages) {
|
1974
|
foreach ($pages as &$page) {
|
1975
|
panels_default_page_manager_handlers_alter($page->default_handlers);
|
1976
|
}
|
1977
|
}
|
1978
|
|
1979
|
/**
|
1980
|
* General utility functions.
|
1981
|
*/
|
1982
|
|
1983
|
/**
|
1984
|
* Perform a drupal_goto on a destination that may be an array like url().
|
1985
|
*/
|
1986
|
function panels_goto($destination) {
|
1987
|
if (!is_array($destination)) {
|
1988
|
return drupal_goto($destination);
|
1989
|
}
|
1990
|
else {
|
1991
|
// Prevent notices by adding defaults.
|
1992
|
$destination += array(
|
1993
|
'query' => NULL,
|
1994
|
'fragment' => NULL,
|
1995
|
'http_response_code' => NULL,
|
1996
|
);
|
1997
|
|
1998
|
return drupal_goto(
|
1999
|
$destination['path'],
|
2000
|
$destination['query'],
|
2001
|
$destination['fragment'],
|
2002
|
$destination['http_response_code']
|
2003
|
);
|
2004
|
}
|
2005
|
}
|
2006
|
|
2007
|
|
2008
|
/**
|
2009
|
* For external use: Given a layout ID and $content array, return panel display.
|
2010
|
*
|
2011
|
* The content array is filled in based upon the content available in the
|
2012
|
* layout. If it's a two column with a content array defined like.
|
2013
|
* @code
|
2014
|
* array(
|
2015
|
* 'left' => t('Left side'),
|
2016
|
* 'right' => t('Right side')
|
2017
|
* ),
|
2018
|
*
|
2019
|
* Then the $content array should be
|
2020
|
* @code
|
2021
|
* array(
|
2022
|
* 'left' => $output_left,
|
2023
|
* 'right' => $output_right,
|
2024
|
* )
|
2025
|
*
|
2026
|
* The output within each panel region can be either a single rendered
|
2027
|
* HTML string or an array of rendered HTML strings as though they were
|
2028
|
* panes. They will simply be concatenated together without separators.
|
2029
|
*/
|
2030
|
function panels_print_layout($layout, $content, $meta = 'standard') {
|
2031
|
ctools_include('plugins', 'panels');
|
2032
|
|
2033
|
// Create a temporary display for this.
|
2034
|
$display = panels_new_display();
|
2035
|
$display->layout = is_array($layout) ? $layout['name'] : $layout;
|
2036
|
$display->content = $content;
|
2037
|
|
2038
|
// Get our simple renderer.
|
2039
|
$renderer = panels_get_renderer_handler('simple', $display);
|
2040
|
$renderer->meta_location = $meta;
|
2041
|
|
2042
|
return $renderer->render();
|
2043
|
}
|
2044
|
|
2045
|
/**
|
2046
|
* Filter callback for array_filter to remove builders from a list of layouts.
|
2047
|
*/
|
2048
|
function _panels_builder_filter($layout) {
|
2049
|
return empty($layout['builder']);
|
2050
|
}
|
2051
|
|
2052
|
/**
|
2053
|
* Implements hook_get_pane_links_alter().
|
2054
|
*/
|
2055
|
function panels_get_pane_links_alter(&$links, $pane, $content_type) {
|
2056
|
// Add links to the Panels pane dropdown menu.
|
2057
|
if ($pane->type === "block") {
|
2058
|
$prefixed_name = $pane->subtype;
|
2059
|
|
2060
|
// Breakup the subtype string into parts.
|
2061
|
$exploded_subtype = explode('-', $pane->subtype);
|
2062
|
|
2063
|
// Get the first part of the string.
|
2064
|
$subtype_prefix = $exploded_subtype[0];
|
2065
|
|
2066
|
// Get the first part of the string and add a hyphen.
|
2067
|
$subtype_prefix_hyphen = $exploded_subtype[0] . '-';
|
2068
|
|
2069
|
// Remove the prefix block- to get the name.
|
2070
|
$name_of_block = preg_replace("/^$subtype_prefix_hyphen/", '', $prefixed_name, 1);
|
2071
|
|
2072
|
// Check for user added menus created at /admin/structure/menu/add
|
2073
|
// menus of that type have a subtype that is prefixed with menu-menu-.
|
2074
|
if (substr($prefixed_name, 0, 10) === "menu-menu-") {
|
2075
|
// Remove the first prefix menu- from menu-menu- to get the name.
|
2076
|
$name_of_block = substr($prefixed_name, 5);
|
2077
|
|
2078
|
$links['top'][] = array(
|
2079
|
'title' => t('Edit block'),
|
2080
|
'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
|
2081
|
'attributes' => array('target' => array('_blank')),
|
2082
|
);
|
2083
|
|
2084
|
$links['top'][] = array(
|
2085
|
'title' => t('Edit menu links'),
|
2086
|
'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
|
2087
|
'attributes' => array('target' => array('_blank')),
|
2088
|
);
|
2089
|
}
|
2090
|
|
2091
|
// Check for module provided menu blocks like Devels or Features
|
2092
|
// menus of that type have a subtype that is prefixed with menu-.
|
2093
|
elseif (substr($prefixed_name, 0, 5) === "menu-") {
|
2094
|
// Remove the first prefix menu- to get the name.
|
2095
|
$name_of_block = substr($prefixed_name, 5);
|
2096
|
|
2097
|
$links['top'][] = array(
|
2098
|
'title' => t('Edit block'),
|
2099
|
'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
|
2100
|
'attributes' => array('target' => array('_blank')),
|
2101
|
);
|
2102
|
|
2103
|
$links['top'][] = array(
|
2104
|
'title' => t('Edit menu links'),
|
2105
|
'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
|
2106
|
'attributes' => array('target' => array('_blank')),
|
2107
|
);
|
2108
|
}
|
2109
|
|
2110
|
// Check for system blocks with menu links.
|
2111
|
elseif (substr($prefixed_name, 0, 7) === "system-") {
|
2112
|
// Remove the first prefix system- to get the name.
|
2113
|
$name_of_block = substr($prefixed_name, 7);
|
2114
|
|
2115
|
$names_of_system_menus = menu_list_system_menus();
|
2116
|
|
2117
|
$links['top'][] = array(
|
2118
|
'title' => t('Edit block'),
|
2119
|
'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
|
2120
|
'attributes' => array('target' => array('_blank')),
|
2121
|
);
|
2122
|
|
2123
|
if (array_key_exists($name_of_block, $names_of_system_menus)) {
|
2124
|
$links['top'][] = array(
|
2125
|
'title' => t('Edit menu links'),
|
2126
|
'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
|
2127
|
'attributes' => array('target' => array('_blank')),
|
2128
|
);
|
2129
|
}
|
2130
|
}
|
2131
|
|
2132
|
// For all other blocks without menus.
|
2133
|
else {
|
2134
|
$links['top'][] = array(
|
2135
|
'title' => t('Edit block'),
|
2136
|
'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
|
2137
|
'attributes' => array('target' => array('_blank')),
|
2138
|
);
|
2139
|
}
|
2140
|
}
|
2141
|
}
|
2142
|
|
2143
|
/**
|
2144
|
* Deprecated functions.
|
2145
|
*
|
2146
|
* Everything below this line will eventually go away.
|
2147
|
*/
|
2148
|
|
2149
|
/**
|
2150
|
* Panels path helper function.
|
2151
|
*/
|
2152
|
function panels_get_path($file, $base_path = FALSE, $module = 'panels') {
|
2153
|
$output = $base_path ? base_path() : '';
|
2154
|
return $output . drupal_get_path('module', $module) . '/' . $file;
|
2155
|
}
|
2156
|
|
2157
|
/**
|
2158
|
* Remove default sidebar related body classes and provide own css classes.
|
2159
|
*/
|
2160
|
function panels_preprocess_html(&$vars) {
|
2161
|
$panel_body_css = &drupal_static('panel_body_css');
|
2162
|
if (!empty($panel_body_css['body_classes_to_remove'])) {
|
2163
|
$classes_to_remove = array_filter(explode(' ', $panel_body_css['body_classes_to_remove']), 'strlen');
|
2164
|
foreach ($vars['classes_array'] as $key => $css_class) {
|
2165
|
if (in_array($css_class, $classes_to_remove)) {
|
2166
|
unset($vars['classes_array'][$key]);
|
2167
|
}
|
2168
|
}
|
2169
|
}
|
2170
|
if (!empty($panel_body_css['body_classes_to_add'])) {
|
2171
|
$vars['classes_array'][] = check_plain($panel_body_css['body_classes_to_add']);
|
2172
|
}
|
2173
|
}
|