Projet

Général

Profil

Paste
Télécharger (68,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / panels / panels.module @ a2bb1a14

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