Projet

Général

Profil

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

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

1
<?php
2

    
3
/**
4
 * @file panels.module
5
 *
6
 * Core functionality for the Panels engine.
7
 */
8

    
9
define('PANELS_REQUIRED_CTOOLS_API', '2.0.9');
10

    
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
define('PANELS_VERSION', '7.x-3.8');
24

    
25

    
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
    'variables' => array('content' => array(), 'pane' => array(), 'display' => array()),
62
    '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
      'description' => t('Allows a user to access the <a href="@url">Panels Dashboard</a>.', array('@url' => url('admin/structure/panels'))),
288
    ),
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
    '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
    '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
      '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
      'description' => t("Allows a user to administer the styles of Panel panes."),
329
    ),
330
    '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
    '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
    '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
  );
347
}
348

    
349
/**
350
 * Implements hook_flush_caches().
351
 */
352
function panels_flush_caches() {
353
  if (db_table_exists('cache_panels')) {
354
    return array('cache_panels');
355
  }
356
}
357

    
358
// ---------------------------------------------------------------------------
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
    'panels_storage' => array(),
414
  );
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
    // 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

    
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
    $pane->uuid = ctools_uuid_generate();
706
    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

    
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
}
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
  drupal_alter('panels_new_pane', $pane);
862

    
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
 * 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
 *
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
  if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
959
    $display->uuid = ctools_uuid_generate();
960
  }
961
  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

    
992
      if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
993
        $pane->uuid = ctools_uuid_generate();
994
      }
995

    
996
      drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array());
997

    
998
      // 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
      if ($pane->pid != $old_pid) {
1007
        // 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
        $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
    // Allow other modules to take action before a panes are deleted.
1042
    module_invoke_all('panels_pane_delete', $pids);
1043
    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
  module_invoke_all('panels_delete_display', $did);
1077
  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
 * 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
 *
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
  if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
1113
    $display->uuid = ctools_uuid_generate();
1114
  }
1115
  $display->did = 'new-' . $display->uuid;
1116
  $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

    
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
      if ($pane->pid == $display->title_pane) {
1134
        $title_pid = $pid;
1135
      }
1136
      $pane->pid = $pid;
1137
      $output .= ctools_export_object('panels_pane', $pane, $prefix);
1138
      $output .= $prefix . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n";
1139
      if (!isset($region_counters[$pane->panel])) {
1140
        $region_counters[$pane->panel] = 0;
1141
      }
1142
      $output .= $prefix . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ .'] = \'' . $pane->pid . "';\n";
1143
    }
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
      if (empty($form_context->form['#theme_wrappers']) || !in_array('form', $form_context->form['#theme_wrappers'])) {
1179
        $form_context['#theme_wrappers'][] = 'form';
1180
      }
1181
      $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
  return $vars['element']['#display']->render();
1198
}
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
      if(!empty($element['#links'])) {
1335
        $links += $element['#links'];
1336
      }
1337
    }
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
  $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

    
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
  $vars['title_heading'] = !empty($content->title_heading) ? $content->title_heading : variable_get('override_title_heading', 'h2');
1393
  $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
  $op = $renderer->get_panels_storage_op_for_ajax($method);
1469
  if (!$cache->display->access($op)) {
1470
    return MENU_ACCESS_DENIED;
1471
  }
1472

    
1473
  $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
 * panel_context:TASK_NAME::HANDLER_NAME::args::url;
1643
 */
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
  list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
1652
  $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
  $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
  $cache->display = &panels_panel_context_get_display($handler);
1678
  $cache->display->context = $contexts;
1679
  $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
  list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
1692
  $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
/**
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
// --------------------------------------------------------------------------
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
/**
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
// --------------------------------------------------------------------------
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
    $classes_to_remove = array_filter(explode(' ', $panel_body_css['body_classes_to_remove']), 'strlen');
1992
    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
}