Projet

Général

Profil

Paste
Télécharger (65,9 ko) Statistiques
| Branche: | Révision:

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

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.8');
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.5');
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
    'administer advanced pane settings' => array(
306
      'title' => t("Configure advanced settings on Panel panes"),
307
      'description' => t(""),
308
    ),
309
    'administer panels layouts' => array(
310
      'title' => t("Administer Panels layouts"),
311
      'description' => t("Allows a user to administer exported Panels layout plugins & instances."),
312
    ),
313
    'administer panels styles' => array(
314
      'title' => t("Administer Panels styles"),
315
      'description' => t("Allows a user to administer the styles of Panel panes."),
316
    ),
317
    'use panels caching features' => array(
318
      'title' => t("Configure caching settings on Panels"),
319
      'description' => t("Allows a user to configure caching on Panels displays and panes."),
320
    ),
321
    'use panels locks' => array(
322
      'title' => t('Use panel locks'),
323
      'description' => t('Allows a user to lock and unlock panes in a panel display.'),
324
    ),
325
    'use ipe with page manager' => array(
326
      'title' => t("Use the Panels In-Place Editor with Page Manager"),
327
      '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.'),
328
    ),
329
  );
330
}
331

    
332
/**
333
 * Implementation of hook_flush_caches().
334
 *
335
 * We implement this so that we can be sure our legacy rendering state setting
336
 * in $conf is updated whenever caches are cleared.
337
 */
338
//function panels_flush_caches() {
339
//  $legacy = panels_get_legacy_state();
340
//  $legacy->determineStatus();
341
//}
342

    
343
/**
344
 * Implements hook_flush_caches().
345
 */
346
function panels_flush_caches() {
347
  if (db_table_exists('cache_panels')) {
348
    return array('cache_panels');
349
  }
350
}
351

    
352
// ---------------------------------------------------------------------------
353
// CTools hook implementations
354
//
355
// These aren't core Drupal hooks but they are just as important.
356

    
357
/**
358
 * Implementation of hook_ctools_plugin_directory() to let the system know
359
 * we implement task and task_handler plugins.
360
 */
361
function panels_ctools_plugin_directory($module, $plugin) {
362
  // Safety: go away if CTools is not at an appropriate version.
363
  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
364
    return;
365
  }
366

    
367
  // We don't support the 'ctools' 'cache' plugin and pretending to causes
368
  // errors when they're in use.
369
  if ($module == 'ctools' && $plugin == 'cache') {
370
    return;
371
    // if we did we'd make a plugin/ctools_cache or something.
372
  }
373

    
374
  if ($module == 'page_manager' || $module == 'panels' || $module == 'ctools' || $module == 'stylizer') {
375
    // Panels and CTools both implement a 'cache' plugin but we don't implement
376
    // the CTools version.
377
    if ($module == 'ctools' && $plugin == 'cache') {
378
      return;
379
    }
380

    
381
    return 'plugins/' . $plugin;
382
  }
383
}
384

    
385
/**
386
 * Implements hook_ctools_plugin_type().
387
 *
388
 * Register layout, style, cache, and display_renderer plugin types, declaring
389
 * relevant plugin type information as necessary.
390
 */
391
function panels_ctools_plugin_type() {
392
  return array(
393
    'layouts' => array(
394
      'load themes' => TRUE, // Can define layouts in themes
395
      'process' => 'panels_layout_process',
396
      'child plugins' => TRUE,
397
    ),
398
    'styles' => array(
399
      'load themes' => TRUE,
400
      'process' => 'panels_plugin_styles_process',
401
      'child plugins' => TRUE,
402
    ),
403
    'cache' => array(),
404
    'display_renderers' => array(
405
      'classes' => array('renderer'),
406
    ),
407
  );
408
}
409

    
410
/**
411
 * Ensure a layout has a minimal set of data.
412
 */
413
function panels_layout_process(&$plugin) {
414
  $plugin += array(
415
    'category' => t('Miscellaneous'),
416
    'description' => '',
417
  );
418
}
419

    
420
/**
421
 * Implementation of hook_ctools_plugin_api().
422
 *
423
 * Inform CTools about version information for various plugins implemented by
424
 * Panels.
425
 *
426
 * @param string $owner
427
 *   The system name of the module owning the API about which information is
428
 *   being requested.
429
 * @param string $api
430
 *   The name of the API about which information is being requested.
431
 */
432
function panels_ctools_plugin_api($owner, $api) {
433
  if ($owner == 'panels' && $api == 'styles') {
434
    // As of 6.x-3.6, Panels has a slightly new system for style plugins.
435
    return array('version' => 2.0);
436
  }
437

    
438
  if ($owner == 'panels' && $api == 'pipelines') {
439
    return array(
440
      'version' => 1,
441
      'path' => drupal_get_path('module', 'panels') . '/includes',
442
    );
443
  }
444
}
445

    
446
/**
447
 * Implementation of hook_views_api().
448
 */
449
function panels_views_api() {
450
  return array(
451
    'api' => 2,
452
    'path' => drupal_get_path('module', 'panels') . '/plugins/views',
453
  );
454
}
455

    
456
/**
457
 * Perform additional processing on a style plugin.
458
 *
459
 * Currently this is only being used to apply versioning information to style
460
 * plugins in order to ensure the legacy renderer passes the right type of
461
 * parameters to a style plugin in a hybrid environment of both new and old
462
 * plugins.
463
 *
464
 * @see _ctools_process_data()
465
 *
466
 * @param array $plugin
467
 *   The style plugin that is being processed.
468
 * @param array $info
469
 *   The style plugin type info array.
470
 */
471
function panels_plugin_styles_process(&$plugin, $info) {
472
  $plugin += array(
473
    'weight' => 0,
474
  );
475

    
476
  $compliant_modules = ctools_plugin_api_info('panels', 'styles', 2.0, 2.0);
477
  $plugin['version'] = empty($compliant_modules[$plugin['module']]) ? 1.0 : $compliant_modules[$plugin['module']]['version'];
478
}
479

    
480
/**
481
 * Declare what style types Panels uses.
482
 */
483
function panels_ctools_style_base_types() {
484
  return array(
485
    'region' => array(
486
      'title' => t('Panel region'),
487
      'preview' => 'panels_stylizer_region_preview',
488
      'theme variables' => array('settings' => NULL, 'class' => NULL, 'content' => NULL),
489
    ),
490
    'pane' => array(
491
      'title' => t('Panel pane'),
492
      'preview' => 'panels_stylizer_pane_preview',
493
      'theme variables' => array('settings' => NULL, 'content' => NULL, 'pane' => NULL, 'display' => NULL),
494
    ),
495
  );
496
}
497

    
498
function panels_stylizer_lipsum() {
499
  return "
500
    <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>
501

    
502
    <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>
503
  ";
504
}
505

    
506
/**
507
 * Generate a preview given the current settings.
508
 */
509
function panels_stylizer_region_preview($plugin, $settings) {
510
  ctools_stylizer_add_css($plugin, $settings);
511
  return theme($plugin['theme'], array('settings' => $settings, 'class' => ctools_stylizer_get_css_class($plugin, $settings), 'content' => panels_stylizer_lipsum()));
512
}
513

    
514
/**
515
 * Generate a preview given the current settings.
516
 */
517
function panels_stylizer_pane_preview($plugin, $settings) {
518
  ctools_stylizer_add_css($plugin, $settings);
519
  $pane = new stdClass();
520

    
521
  $content = new stdClass;
522
  $content->title = t('Lorem ipsum');
523
  $content->content = panels_stylizer_lipsum();
524
  $content->type = 'dummy';
525
  $content->subtype = 'dummy';
526

    
527
  $content->css_class = ctools_stylizer_get_css_class($plugin, $settings);
528

    
529
  $display = new panels_display();
530

    
531
  if (!empty($plugin['theme'])) {
532
    return theme($plugin['theme'], array('settings' => $settings, 'content' => $content, 'pane' => $pane, 'display' => $display));
533
  }
534
  else {
535
    return theme('panels_pane', array('content' => $content, 'pane' => $pane, 'display' => $display));
536
  }
537
}
538

    
539
// ---------------------------------------------------------------------------
540
// Panels display editing
541

    
542
/**
543
 * @defgroup mainapi Functions comprising the main panels API
544
 * @{
545
 */
546

    
547
/**
548
 * Main API entry point to edit a panel display.
549
 *
550
 * Sample implementations utiltizing the the complex $destination behavior can be found
551
 * in panels_page_edit_content() and, in a separate contrib module, OG Blueprints
552
 * (http://drupal.org/project/og_blueprints), og_blueprints_blueprint_edit().
553
 *
554
 * @ingroup mainapi
555
 *
556
 * @param object $display instanceof panels_display \n
557
 *  A fully loaded panels $display object, as returned from panels_load_display().
558
 *  Merely passing a did is NOT sufficient. \n
559
 *  Note that 'fully loaded' means the $display must already be loaded with any contexts
560
 *  the caller wishes to have set for the display.
561
 * @param mixed $destination \n
562
 *  The redirect destination that the user should be taken to on form submission or
563
 *  cancellation. With panels_edit, $destination has complex effects on the return
564
 *  values of panels_edit() once the form has been submitted. See the explanation of
565
 *  the return value below to understand the different types of values returned by panels_edit()
566
 *  at different stages of FAPI. Under most circumstances, simply passing in
567
 *  drupal_get_destination() is all that's necessary.
568
 * @param array $content_types \n
569
 *  An associative array of allowed content types, typically as returned from
570
 *  panels_common_get_allowed_types(). Note that context partially governs available content types,
571
 *  so you will want to create any relevant contexts using panels_create_context() or
572
 *  panels_create_context_empty() to make sure all the appropriate content types are available.
573
 *
574
 * @return
575
 *  Because the functions called by panels_edit() invoke the form API, this function
576
 *  returns different values depending on the stage of form submission we're at. In Drupal 5,
577
 *  the phase of form submission is indicated by the contents of $_POST['op']. Here's what you'll
578
 *  get at different stages:
579
 *    -# If !$_POST['op']: then we're on on the initial passthrough and the form is being
580
 *       rendered, so it's the $form itself that's being returned. Because negative margins,
581
 *       a common CSS technique, bork the display editor's ajax drag-and-drop, it's important
582
 *       that the $output be printed, not returned. Use this syntax in the caller function: \n
583
 *          print theme('page', panels_edit($display, $destination, $content_types), FALSE); \n
584
 *    -# If $_POST['op'] == t('Cancel'): form submission has been cancelled. If empty($destination) == FALSE,
585
 *       then there is no return value and the panels API takes care of redirecting to $destination.
586
 *       If empty($destination) == TRUE, then there's still no return value, but the caller function
587
 *       has to take care of form redirection.
588
 *    -# If $_POST['op'] == ('Save'): the form has been submitted successfully and has run through
589
 *        panels_edit_display_submit(). $output depends on the value of $destination:
590
 *      - If empty($destination) == TRUE: $output contains the modified $display
591
 *        object, and no redirection will occur. This option is useful if the caller
592
 *        needs to perform additional operations on or with the modified $display before
593
 *        the page request is complete. Using hook_form_alter() to add an additional submit
594
 *        handler is typically the preferred method for something like this, but there
595
 *        are certain use cases where that is infeasible and $destination = NULL should
596
 *        be used instead. If this method is employed, the caller will need to handle form
597
 *        redirection. Note that having $_REQUEST['destination'] set, whether via
598
 *        drupal_get_destination() or some other method, will NOT interfere with this
599
 *        functionality; consequently, you can use drupal_get_destination() to safely store
600
 *        your desired redirect in the caller function, then simply use drupal_goto() once
601
 *        panels_edit() has done its business.
602
 *      - If empty($destination) == FALSE: the form will redirect to the URL string
603
 *        given in $destination and NO value will be returned.
604
 */
605
function panels_edit($display, $destination = NULL, $content_types = NULL, $title = FALSE) {
606
  ctools_include('display-edit', 'panels');
607
  ctools_include('ajax');
608
  ctools_include('plugins', 'panels');
609
  return _panels_edit($display, $destination, $content_types, $title);
610
}
611

    
612
/**
613
 * API entry point for selecting a layout for a given display.
614
 *
615
 * Layout selection is nothing more than a list of radio items encompassing the available
616
 * layouts for this display, as defined by .inc files in the panels/layouts subdirectory.
617
 * The only real complexity occurs when a user attempts to change the layout of a display
618
 * that has some content in it.
619
 *
620
 * @param object $display instanceof panels_display \n
621
 *  A fully loaded panels $display object, as returned from panels_load_display().
622
 *  Merely passing a did is NOT sufficient.
623
 * @param string $finish
624
 *  A string that will be used for the text of the form submission button. If no value is provided,
625
 *  then the form submission button will default to t('Save').
626
 * @param mixed $destination
627
 *  Basic usage is a string containing the URL that the form should redirect to upon submission.
628
 *  For a discussion of advanced usages, see panels_edit().
629
 * @param mixed $allowed_layouts
630
 *  Allowed layouts has three different behaviors that depend on which of three value types
631
 *  are passed in by the caller:
632
 *    #- if $allowed_layouts instanceof panels_allowed_layouts (includes subclasses): the most
633
 *       complex use of the API. The caller is passing in a loaded panels_allowed_layouts object
634
 *       that the client module previously created and stored somewhere using a custom storage
635
 *       mechanism.
636
 *    #- if is_string($allowed_layouts): the string will be used in a call to variable_get() which
637
 *       will call the $allowed_layouts . '_allowed_layouts' var. If the data was stored properly
638
 *       in the system var, the $allowed_layouts object will be unserialized and recreated.
639
 *       @see panels_common_set_allowed_layouts()
640
 *    #- if is_null($allowed_layouts): the default behavior, which also provides backwards
641
 *       compatibility for implementations of the Panels2 API written before beta4. In this case,
642
 *       a dummy panels_allowed_layouts object is created which does not restrict any layouts.
643
 *       Subsequent behavior is indistinguishable from pre-beta4 behavior.
644
 *
645
 * @return
646
 *  Can return nothing, or a modified $display object, or a redirection string; return values for the
647
 *  panels_edit* family of functions are quite complex. See panels_edit() for detailed discussion.
648
 * @see panels_edit()
649
 */
650
function panels_edit_layout($display, $finish, $destination = NULL, $allowed_layouts = NULL) {
651
  ctools_include('display-layout', 'panels');
652
  ctools_include('plugins', 'panels');
653
  return _panels_edit_layout($display, $finish, $destination, $allowed_layouts);
654
}
655

    
656
// ---------------------------------------------------------------------------
657
// Panels database functions
658

    
659
/**
660
 * Forms the basis of a panel display
661
 *
662
 */
663
class panels_display {
664
  var $args = array();
665
  var $content = array();
666
  var $panels = array();
667
  var $incoming_content = NULL;
668
  var $css_id = NULL;
669
  var $context = array();
670
  var $did = 'new';
671
  var $renderer = 'standard';
672

    
673
  function add_pane(&$pane, $location = NULL) {
674
    // If no location specified, use what's set in the pane.
675
    if (empty($location)) {
676
      $location = $pane->panel;
677
    }
678
    else {
679
      $pane->panel = $location;
680
    }
681

    
682
    // Generate a permanent uuid for this pane, and use
683
    // it as a temporary pid.
684
    $pane->uuid = ctools_uuid_generate();
685
    $pane->pid = 'new-' . $pane->uuid;
686

    
687
    // Add the pane to the approprate spots.
688
    $this->content[$pane->pid] = &$pane;
689
    $this->panels[$location][] = $pane->pid;
690
  }
691

    
692
  function duplicate_pane($pid, $location = FALSE) {
693
    $pane = $this->clone_pane($pid);
694
    $this->add_pane($pane, $location);
695
  }
696

    
697
  function clone_pane($pid) {
698
    $pane = clone $this->content[$pid];
699
    $pane->uuid = ctools_uuid_generate();
700
    return $pane;
701
  }
702

    
703
  /**
704
   * Get the title from a display.
705
   *
706
   * The display must have already been rendered, or the setting to set the
707
   * display's title from a pane's title will not have worked.
708
   *
709
   * @return
710
   *   The title to use. If NULL, this means to let any default title that may be in use
711
   *   pass through. i.e, do not actually set the title.
712
   */
713
  function get_title() {
714
    switch ($this->hide_title) {
715
      case PANELS_TITLE_NONE:
716
        return '';
717

    
718
      case PANELS_TITLE_PANE:
719
        return isset($this->stored_pane_title) ? $this->stored_pane_title : '';
720

    
721
      case PANELS_TITLE_FIXED:
722
      case FALSE; // For old exported panels that are not in the database.
723
        if (!empty($this->title)) {
724
          return filter_xss_admin(ctools_context_keyword_substitute($this->title, array(), $this->context));
725
        }
726
        return NULL;
727
    }
728
  }
729

    
730
  /**
731
   * Render this panels display.
732
   *
733
   * After checking to ensure the designated layout plugin is valid, a
734
   * display renderer object is spawned and runs its rendering logic.
735
   *
736
   * @param mixed $renderer
737
   *    An instantiated display renderer object, or the name of a display
738
   *    renderer plugin+class to be fetched. Defaults to NULL. When NULL, the
739
   *    predesignated display renderer will be used.
740
   */
741
  function render($renderer = NULL) {
742
    $layout = panels_get_layout($this->layout);
743
    if (!$layout) {
744
      return NULL;
745
    }
746

    
747
    // If we were not given a renderer object, load it.
748
    if (!is_object($renderer)) {
749
      // If the renderer was not specified, default to $this->renderer
750
      // which is either standard or was already set for us.
751
      $renderer = panels_get_renderer_handler(!empty($renderer) ? $renderer : $this->renderer, $this);
752
      if (!$renderer) {
753
        return NULL;
754
      }
755
    }
756

    
757
    $output = '';
758
    // Let modules act just prior to render.
759
    foreach (module_implements('panels_pre_render') as $module) {
760
      $function = $module . '_panels_pre_render';
761
      $output .= $function($this, $renderer);
762
    }
763

    
764
    $output .= $renderer->render();
765

    
766
    // Let modules act just after render.
767
    foreach (module_implements('panels_post_render') as $module) {
768
      $function = $module . '_panels_post_render';
769
      $output .= $function($this, $renderer);
770
    }
771
    return $output;
772
  }
773
}
774

    
775
/**
776
 * }@ End of 'defgroup mainapi', although other functions are specifically added later
777
 */
778

    
779
/**
780
 * Creates a new display, setting the ID to our magic new id.
781
 */
782
function panels_new_display() {
783
  ctools_include('export');
784
  $display = ctools_export_new_object('panels_display', FALSE);
785
  $display->did = 'new';
786
  return $display;
787
}
788

    
789
/**
790
 * Create a new pane.
791
 *
792
 * @todo -- use schema API for some of this?
793
 */
794
function panels_new_pane($type, $subtype, $set_defaults = FALSE) {
795
  ctools_include('export');
796
  $pane = ctools_export_new_object('panels_pane', FALSE);
797
  $pane->pid = 'new';
798
  $pane->type = $type;
799
  $pane->subtype = $subtype;
800
  if ($set_defaults) {
801
    ctools_include('content');
802
    $content_type = ctools_get_content_type($type);
803
    $content_subtype = ctools_content_get_subtype($content_type, $subtype);
804
    $pane->configuration = ctools_content_get_defaults($content_type, $content_subtype);
805
  }
806
  drupal_alter('panels_new_pane', $pane);
807

    
808
  return $pane;
809
}
810

    
811
/**
812
 * Load and fill the requested $display object(s).
813
 *
814
 * Helper function primarily for for panels_load_display().
815
 *
816
 * @param array $dids
817
 *  An indexed array of dids to be loaded from the database.
818
 *
819
 * @return $displays
820
 *  An array of displays, keyed by their display dids.
821
 *
822
 * @todo schema API can drasticly simplify this code.
823
 */
824
function panels_load_displays($dids) {
825
  $displays = array();
826
  if (empty($dids) || !is_array($dids)) {
827
    return $displays;
828
  }
829

    
830
  $result = db_query("SELECT * FROM {panels_display} WHERE did IN (:dids)", array(':dids' => $dids));
831

    
832
  ctools_include('export');
833
  foreach ($result as $obj) {
834
    $displays[$obj->did] = ctools_export_unpack_object('panels_display', $obj);
835
    // Modify the hide_title field to go from a bool to an int if necessary.
836
  }
837

    
838
  $result = db_query("SELECT * FROM {panels_pane} WHERE did IN (:dids) ORDER BY did, panel, position", array(':dids' => $dids));
839
  foreach ($result as $obj) {
840
    $pane = ctools_export_unpack_object('panels_pane', $obj);
841

    
842
    $displays[$pane->did]->panels[$pane->panel][] = $pane->pid;
843
    $displays[$pane->did]->content[$pane->pid] = $pane;
844
  }
845

    
846
  return $displays;
847
}
848

    
849
/**
850
 * Load a single display.
851
 *
852
 * @ingroup mainapi
853
 *
854
 * @param int $did
855
 *  The display id (did) of the display to be loaded.
856
 *
857
 * @return object $display instanceof panels_display \n
858
 *  Returns a partially-loaded panels_display object. $display objects returned from
859
 *  from this function have only the following data:
860
 *    - $display->did (the display id)
861
 *    - $display->name (the 'name' of the display, where applicable - it often isn't)
862
 *    - $display->layout (a string with the system name of the display's layout)
863
 *    - $display->panel_settings (custom layout style settings contained in an associative array; NULL if none)
864
 *    - $display->layout_settings (panel size and configuration settings for Flexible layouts; NULL if none)
865
 *    - $display->css_id (the special css_id that has been assigned to this display, if any; NULL if none)
866
 *    - $display->content (an array of pane objects, keyed by pane id (pid))
867
 *    - $display->panels (an associative array of panel regions, each an indexed array of pids in the order they appear in that region)
868
 *    - $display->cache (any relevant data from panels_simple_cache)
869
 *    - $display->args
870
 *    - $display->incoming_content
871
 *
872
 * While all of these members are defined, $display->context is NEVER defined in the returned $display;
873
 * it must be set using one of the ctools_context_create() functions.
874
 */
875
function panels_load_display($did) {
876
  $displays = panels_load_displays(array($did));
877
  if (!empty($displays)) {
878
    return array_shift($displays);
879
  }
880
}
881

    
882
/**
883
 * Save a display object.
884
 *
885
 * @ingroup mainapi
886
 *
887
 * Note that a new $display only receives a real did once it is run through
888
 * this function, and likewise for the pid of any new pane.
889
 *
890
 * Until then, a new display uses a string placeholder, 'new', in place of
891
 * a real did, and a new pane (whether on a new $display or not) appends a
892
 * universally-unique identifier (which is stored permanently in the 'uuid'
893
 * field). This format is also used in place of the real pid for exports.
894
 *
895
 * @param object $display instanceof panels_display \n
896
 *  The display object to be saved. Passed by reference so the caller need not use
897
 *  the return value for any reason except convenience.
898
 *
899
 * @return object $display instanceof panels_display \n
900
 */
901
function panels_save_display(&$display) {
902
  $update = (isset($display->did) && is_numeric($display->did)) ? array('did') : array();
903
  if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
904
    $display->uuid = ctools_uuid_generate();
905
  }
906
  drupal_write_record('panels_display', $display, $update);
907

    
908
  $pids = array();
909
  if ($update) {
910
    // Get a list of all panes currently in the database for this display so we can know if there
911
    // are panes that need to be deleted. (i.e, aren't currently in our list of panes).
912
    $result = db_query("SELECT pid FROM {panels_pane} WHERE did = :did", array(':did' => $display->did));
913
    foreach ($result as $pane) {
914
      $pids[$pane->pid] = $pane->pid;
915
    }
916
  }
917

    
918
  // update all the panes
919
  ctools_include('plugins', 'panels');
920
  ctools_include('content');
921

    
922
  foreach ($display->panels as $id => $panes) {
923
    $position = 0;
924
    $new_panes = array();
925
    foreach ((array) $panes as $pid) {
926
      if (!isset($display->content[$pid])) {
927
        continue;
928
      }
929
      $pane = $display->content[$pid];
930
      $type = ctools_get_content_type($pane->type);
931

    
932
      $pane->position = $position++;
933
      $pane->did = $display->did;
934

    
935
      $old_pid = $pane->pid;
936

    
937
      if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
938
        $pane->uuid = ctools_uuid_generate();
939
      }
940

    
941
      drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array());
942

    
943
      // Allow other modules to take action after a pane is saved.
944
      if ($pane->pid == $old_pid) {
945
        module_invoke_all('panels_pane_update', $pane);
946
      }
947
      else {
948
        module_invoke_all('panels_pane_insert', $pane);
949
      }
950

    
951
      if ($pane->pid != $old_pid) {
952
        // Remove the old new-* entry from the displays content.
953
        unset($display->content[$pid]);
954

    
955
        // and put it back so our pids and positions can be used.
956
        $display->content[$pane->pid] = $pane;
957

    
958
        // If the title pane was one of our panes that just got its ID changed,
959
        // we need to change it in the database, too.
960
        if (isset($display->title_pane) && $display->title_pane == $old_pid) {
961
          $display->title_pane = $pane->pid;
962
          // Do a simple update query to write it so we don't have to rewrite
963
          // the whole record. We can't just save writing the whole record here
964
          // because it was needed to get the did. Chicken, egg, more chicken.
965
          db_update('panels_display')
966
            ->fields(array(
967
              'title_pane' => $pane->pid
968
            ))
969
            ->condition('did', $display->did)
970
            ->execute();
971
        }
972
      }
973

    
974
      // re-add this to the list of content for this panel.
975
      $new_panes[] = $pane->pid;
976

    
977
      // Remove this from the list of panes scheduled for deletion.
978
      if (isset($pids[$pane->pid])) {
979
        unset($pids[$pane->pid]);
980
      }
981
    }
982

    
983
    $display->panels[$id] = $new_panes;
984
  }
985
  if (!empty($pids)) {
986
    // Allow other modules to take action before a panes are deleted.
987
    module_invoke_all('panels_pane_delete', $pids);
988
    db_delete('panels_pane')->condition('pid', $pids)->execute();
989
  }
990

    
991
  // Clear any cached content for this display.
992
  panels_clear_cached_content($display);
993

    
994
  // Allow other modules to take action when a display is saved.
995
  module_invoke_all('panels_display_save', $display);
996

    
997
  // Log the change to watchdog, using the same style as node.module
998
  $watchdog_args = array('%did' => $display->did);
999
  if (!empty($display->title)) {
1000
    $watchdog_args['%title'] = $display->title;
1001
    watchdog('content', 'Panels: saved display "%title" with display id %did', $watchdog_args, WATCHDOG_NOTICE);
1002
  }
1003
  else {
1004
    watchdog('content', 'Panels: saved display with id %did', $watchdog_args, WATCHDOG_NOTICE);
1005
  }
1006

    
1007
  // to be nice, even tho we have a reference.
1008
  return $display;
1009
}
1010

    
1011
/**
1012
 * Delete a display.
1013
 */
1014
function panels_delete_display($display) {
1015
  if (is_object($display)) {
1016
    $did = $display->did;
1017
  }
1018
  else {
1019
    $did = $display;
1020
  }
1021
  module_invoke_all('panels_delete_display', $did);
1022
  db_delete('panels_display')->condition('did', $did)->execute();
1023
  db_delete('panels_pane')->condition('did', $did)->execute();
1024
}
1025

    
1026
/**
1027
 * Exports the provided display into portable code.
1028
 *
1029
 * This function is primarily intended as a mechanism for cloning displays.
1030
 * It generates an exact replica (in code) of the provided $display, with
1031
 * the exception that it replaces all ids (dids and pids) with place-holder
1032
 * values (consisting of the display or pane's uuid, with a 'new-' prefix).
1033
 *
1034
 * Only once panels_save_display() is called on the code version of $display
1035
 * will the exported display be written to the database and permanently saved.
1036
 *
1037
 * @see panels_page_export() or _panels_page_fetch_display() for sample implementations.
1038
 *
1039
 * @ingroup mainapi
1040
 *
1041
 * @param object $display instanceof panels_display \n
1042
 *  This export function does no loading of additional data about the provided
1043
 *  display. Consequently, the caller should make sure that all the desired data
1044
 *  has been loaded into the $display before calling this function.
1045
 * @param string $prefix
1046
 *  A string prefix that is prepended to each line of exported code. This is primarily
1047
 *  used for prepending a double space when exporting so that the code indents and lines up nicely.
1048
 *
1049
 * @return string $output
1050
 *  The passed-in $display expressed as code, ready to be imported. Import by running
1051
 *  eval($output) in the caller function; doing so will create a new $display variable
1052
 *  with all the exported values. Note that if you have already defined a $display variable in
1053
 *  the same scope as where you eval(), your existing $display variable WILL be overwritten.
1054
 */
1055
function panels_export_display($display, $prefix = '') {
1056
  ctools_include('export');
1057
  if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
1058
    $display->uuid = ctools_uuid_generate();
1059
  }
1060
  $display->did = 'new-' . $display->uuid;
1061
  $output = ctools_export_object('panels_display', $display, $prefix);
1062

    
1063
  // Initialize empty properties.
1064
  $output .= $prefix . '$display->content = array()' . ";\n";
1065
  $output .= $prefix . '$display->panels = array()' . ";\n";
1066
  $panels = array();
1067

    
1068
  $title_pid = 0;
1069
  if (!empty($display->content)) {
1070
    $region_counters = array();
1071
    foreach ($display->content as $pane) {
1072

    
1073
      if (!isset($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
1074
        $pane->uuid = ctools_uuid_generate();
1075
      }
1076
      $pid = 'new-' . $pane->uuid;
1077

    
1078
      if ($pane->pid == $display->title_pane) {
1079
        $title_pid = $pid;
1080
      }
1081
      $pane->pid = $pid;
1082
      $output .= ctools_export_object('panels_pane', $pane, $prefix . '  ');
1083
      $output .= "$prefix  " . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n";
1084
      if (!isset($region_counters[$pane->panel])) {
1085
        $region_counters[$pane->panel] = 0;
1086
      }
1087
      $output .= "$prefix  " . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ .'] = \'' . $pane->pid . "';\n";
1088
    }
1089
  }
1090
  $output .= $prefix . '$display->hide_title = ';
1091
  switch ($display->hide_title) {
1092
    case PANELS_TITLE_FIXED:
1093
      $output .= 'PANELS_TITLE_FIXED';
1094
      break;
1095
    case PANELS_TITLE_NONE:
1096
      $output .= 'PANELS_TITLE_NONE';
1097
      break;
1098
    case PANELS_TITLE_PANE:
1099
      $output .= 'PANELS_TITLE_PANE';
1100
      break;
1101
  }
1102
  $output .= ";\n";
1103

    
1104
  $output .= $prefix . '$display->title_pane =' . " '$title_pid';\n";
1105
  return $output;
1106
}
1107

    
1108
/**
1109
 * Render a display by loading the content into an appropriate
1110
 * array and then passing through to panels_render_layout.
1111
 *
1112
 * if $incoming_content is NULL, default content will be applied. Use
1113
 * an empty string to indicate no content.
1114
 * @ingroup hook_invocations
1115
 */
1116
function panels_render_display(&$display, $renderer = NULL) {
1117
  ctools_include('plugins', 'panels');
1118
  ctools_include('context');
1119

    
1120
  if (!empty($display->context)) {
1121
    if ($form_context = ctools_context_get_form($display->context)) {
1122
      $form_context->form['#theme'] = 'panels_render_display_form';
1123
      if (empty($form_context->form['#theme_wrappers']) || !in_array('form', $form_context->form['#theme_wrappers'])) {
1124
        $form_context['#theme_wrappers'][] = 'form';
1125
      }
1126
      $form_context->form['#display'] = &$display;
1127
      return $form_context->form;
1128
    }
1129
  }
1130
  return $display->render($renderer);
1131
}
1132

    
1133
/**
1134
 * Theme function to render our panel as a form.
1135
 *
1136
 * When rendering a display as a form, the entire display needs to be
1137
 * inside the <form> tag so that the form can be spread across the
1138
 * panes. This sets up the form system to be the main caller and we
1139
 * then operate as a theme function of the form.
1140
 */
1141
function theme_panels_render_display_form($vars) {
1142
  return $vars['element']['#display']->render();
1143
}
1144

    
1145
// @layout
1146
function panels_print_layout_icon($id, $layout, $title = NULL) {
1147
  ctools_add_css('panels_admin', 'panels');
1148
  $file = $layout['path'] . '/' . $layout['icon'];
1149
  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));
1150
}
1151

    
1152
/**
1153
 * Theme the layout icon image
1154
 * @layout
1155
 * @todo move to theme.inc
1156
 */
1157
function theme_panels_layout_icon($vars) {
1158
  $id = $vars['id'];
1159
  $image = $vars['image'];
1160
  $title = $vars['title'];
1161

    
1162
  $output = '<div class="layout-icon">';
1163
  $output .= $image;
1164
  if ($title) {
1165
    $output .= '<div class="caption">' . $title . '</div>';
1166
  }
1167
  $output .= '</div>';
1168
  return $output;
1169
}
1170

    
1171
/**
1172
 * Theme the layout link image
1173
 * @layout
1174
 *
1175
 * @todo Why isn't this a template at this point?
1176
 * @todo Why does this take 4 arguments but only makes use of two?
1177
 */
1178
function theme_panels_layout_link($vars) {
1179
  $title = $vars['title'];
1180
  $image = $vars['image'];
1181
  $class = $vars['class'];
1182

    
1183
  $output = '<div class="' . implode(' ', $class) . '">';
1184
  $output .= $vars['image'];
1185
  $output .= '<div>' . $vars['title'] . '</div>';
1186
  $output .= '</div>';
1187
  return $output;
1188
}
1189

    
1190
/**
1191
 * Print the layout link. Sends out to a theme function.
1192
 * @layout
1193
 */
1194
function panels_print_layout_link($id, $layout, $link, $options = array(), $current_layout = FALSE) {
1195
  if (isset($options['query']['q'])) {
1196
    unset($options['query']['q']);
1197
  }
1198

    
1199
  // Setup classes for layout link, including current-layout information
1200
  $class = array('layout-link');
1201
  if ($current_layout == $id) {
1202
    $options['attributes']['class'][] = 'current-layout-link';
1203
    $class[] = 'current-layout';
1204
  }
1205

    
1206
  ctools_add_css('panels_admin', 'panels');
1207
  $file = $layout['path'] . '/' . $layout['icon'];
1208
  $image = l(theme('image', array('path' => $file)), $link, array('html' => true) + $options);
1209
  $title = l($layout['title'], $link, $options);
1210
  return theme('panels_layout_link', array('title' => $title, 'image' => $image, 'class' => $class));
1211
}
1212

    
1213

    
1214
/**
1215
 * Gateway to the PanelsLegacyState class/object, which does all legacy state
1216
 * checks and provides information about the cause of legacy states as needed.
1217
 *
1218
 * @return PanelsLegacyState $legacy
1219
 */
1220
function panels_get_legacy_state() {
1221
  static $legacy = NULL;
1222
  if (!isset($legacy)) {
1223
    ctools_include('legacy', 'panels');
1224
    $legacy = new PanelsLegacyState();
1225
  }
1226
  return $legacy;
1227
}
1228

    
1229
/**
1230
 * Get the display that is currently being rendered as a page.
1231
 *
1232
 * Unlike in previous versions of this, this only returns the display,
1233
 * not the page itself, because there are a number of different ways
1234
 * to get to this point. It is hoped that the page data isn't needed
1235
 * at this point. If it turns out there is, we will do something else to
1236
 * get that functionality.
1237
 */
1238
function panels_get_current_page_display($change = NULL) {
1239
  static $display = NULL;
1240
  if ($change) {
1241
    $display = $change;
1242
  }
1243

    
1244
  return $display;
1245
}
1246

    
1247
/**
1248
 * Clean up the panel pane variables for the template.
1249
 */
1250
function template_preprocess_panels_pane(&$vars) {
1251
  $content = &$vars['content'];
1252

    
1253
  $vars['contextual_links'] = array();
1254
  $vars['classes_array'] = array();
1255
  $vars['admin_links'] = '';
1256

    
1257
  if (module_exists('contextual') && user_access('access contextual links')) {
1258
    $links = array();
1259
    // These are specified by the content.
1260
    if (!empty($content->admin_links)) {
1261
      $links += $content->admin_links;
1262
    }
1263

    
1264
    // Take any that may have been in the render array we were given and
1265
    // move them up so they appear outside the pane properly.
1266
    if (is_array($content->content) && isset($content->content['#contextual_links'])) {
1267
      $element = array(
1268
        '#type' => 'contextual_links',
1269
        '#contextual_links' => $content->content['#contextual_links'],
1270
      );
1271
      unset($content->content['#contextual_links']);
1272

    
1273
      // Add content to $element array
1274
      if (is_array($content->content)) {
1275
        $element['#element'] = $content->content;
1276
      }
1277

    
1278
      $element = contextual_pre_render_links($element);
1279
      if(!empty($element['#links'])) {
1280
        $links += $element['#links'];
1281
      }
1282
    }
1283

    
1284
    if ($links) {
1285
      $build = array(
1286
        '#prefix' => '<div class="contextual-links-wrapper">',
1287
        '#suffix' => '</div>',
1288
        '#theme' => 'links__contextual',
1289
        '#links' => $links,
1290
        '#attributes' => array('class' => array('contextual-links')),
1291
        '#attached' => array(
1292
          'library' => array(array('contextual', 'contextual-links')),
1293
        ),
1294
      );
1295
      $vars['classes_array'][] = 'contextual-links-region';
1296
      $vars['admin_links'] = drupal_render($build);
1297
    }
1298
  }
1299

    
1300
  // basic classes
1301
  $vars['classes_array'][] = 'panel-pane';
1302
  $vars['id'] = '';
1303

    
1304
  // Add some usable classes based on type/subtype
1305
  ctools_include('cleanstring');
1306
  $type_class = $content->type ? 'pane-'. ctools_cleanstring($content->type, array('lower case' => TRUE)) : '';
1307
  $subtype_class = $content->subtype ? 'pane-'. ctools_cleanstring($content->subtype, array('lower case' => TRUE)) : '';
1308

    
1309
  // Sometimes type and subtype are the same. Avoid redundant classes.
1310
  $vars['classes_array'][] = $type_class;
1311
  if ($type_class != $subtype_class) {
1312
    $vars['classes_array'][] = $subtype_class;
1313
  }
1314

    
1315
  // Add id and custom class if sent in.
1316
  if (!empty($content->content)) {
1317
    if (!empty($content->css_id)) {
1318
      $vars['id'] = ' id="' . $content->css_id . '"';
1319
    }
1320
    if (!empty($content->css_class)) {
1321
      $vars['classes_array'][] = $content->css_class;
1322
    }
1323
  }
1324

    
1325
  // Set up some placeholders for constructing template file names.
1326
  $base = 'panels_pane';
1327
  $delimiter = '__';
1328

    
1329
  // Add template file suggestion for content type and sub-type.
1330
  $vars['theme_hook_suggestions'][] = $base . $delimiter . $content->type;
1331
  $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)), '-', '_');
1332

    
1333
  $vars['pane_prefix'] = !empty($content->pane_prefix) ? $content->pane_prefix : '';
1334
  $vars['pane_suffix'] = !empty($content->pane_suffix) ? $content->pane_suffix : '';
1335

    
1336
  $vars['title'] = !empty($content->title) ? $content->title : '';
1337
  $vars['title_heading'] = !empty($content->title_heading) ? $content->title_heading : 'h2';
1338
  $vars['title_attributes_array']['class'][] = 'pane-title';
1339

    
1340
  $vars['feeds'] = !empty($content->feeds) ? implode(' ', $content->feeds) : '';
1341

    
1342
  $vars['links'] = !empty($content->links) ? theme('links', array('links' => $content->links)) : '';
1343
  $vars['more'] = '';
1344
  if (!empty($content->more)) {
1345
    if (empty($content->more['title'])) {
1346
      $content->more['title'] = t('more');
1347
    }
1348
    $vars['more'] = l($content->more['title'], $content->more['href'], $content->more);
1349
  }
1350

    
1351
  $vars['content'] = !empty($content->content) ? $content->content : '';
1352

    
1353
}
1354

    
1355
/**
1356
 * Route Panels' AJAX calls to the correct object.
1357
 *
1358
 * Panels' AJAX is controlled mostly by renderer objects. This menu callback
1359
 * accepts the incoming request, figures out which object should handle the
1360
 * request, and attempts to route it. If no object can be found, the default
1361
 * Panels editor object is used.
1362
 *
1363
 * Calls are routed via the ajax_* method space. For example, if visiting
1364
 * panels/ajax/add-pane then $renderer::ajax_add_pane() will be called.
1365
 * This means commands can be added without having to create new callbacks.
1366
 *
1367
 * The first argument *must always* be the cache key so that a cache object
1368
 * can be passed through. Other arguments will be passed through untouched
1369
 * so that the method can do whatever it needs to do.
1370
 */
1371
function panels_ajax_router() {
1372
  $args = func_get_args();
1373
  if (count($args) < 3) {
1374
    return MENU_NOT_FOUND;
1375
  }
1376

    
1377
  ctools_include('display-edit', 'panels');
1378
  ctools_include('plugins', 'panels');
1379
  ctools_include('ajax');
1380
  ctools_include('modal');
1381
  ctools_include('context');
1382
  ctools_include('content');
1383

    
1384
  $plugin_name = array_shift($args);
1385
  $method = array_shift($args);
1386
  $cache_key = array_shift($args);
1387

    
1388
  $plugin = panels_get_display_renderer($plugin_name);
1389
  if (!$plugin) {
1390
    // This is the default renderer for handling AJAX commands.
1391
    $plugin = panels_get_display_renderer('editor');
1392
  }
1393

    
1394
  $cache = panels_edit_cache_get($cache_key);
1395
  if (empty($cache)) {
1396
    return MENU_ACCESS_DENIED;
1397
  }
1398

    
1399
  $renderer = panels_get_renderer_handler($plugin, $cache->display);
1400
  if (!$renderer) {
1401
    return MENU_ACCESS_DENIED;
1402
  }
1403

    
1404
  $method = 'ajax_' . str_replace('-', '_', $method);
1405
  if (!method_exists($renderer, $method)) {
1406
    return MENU_NOT_FOUND;
1407
  }
1408

    
1409
  $renderer->cache = &$cache;
1410
  ctools_include('cleanstring');
1411
  $renderer->clean_key = ctools_cleanstring($cache_key);
1412

    
1413
  $output = call_user_func_array(array($renderer, $method), $args);
1414

    
1415
  if (empty($output) && !empty($renderer->commands)) {
1416
    return array(
1417
      '#type' => 'ajax',
1418
      '#commands' => $renderer->commands,
1419
    );
1420
  }
1421
  else {
1422
    return $output;
1423
  }
1424
}
1425

    
1426
// --------------------------------------------------------------------------
1427
// Panels caching functions and callbacks
1428
//
1429
// When editing displays and the like, Panels has a caching system that relies
1430
// on a callback to determine where to get the actual cache.
1431

    
1432
// @todo This system needs to be better documented so that it can be
1433
// better used.
1434

    
1435
/**
1436
 * Get an object from cache.
1437
 */
1438
function panels_cache_get($obj, $did, $skip_cache = FALSE) {
1439
  ctools_include('object-cache');
1440
  // we often store contexts in cache, so let's just make sure we can load
1441
  // them.
1442
  ctools_include('context');
1443
  return ctools_object_cache_get($obj, 'panels_display:' . $did, $skip_cache);
1444
}
1445

    
1446
/**
1447
 * Save the edited object into the cache.
1448
 */
1449
function panels_cache_set($obj, $did, $cache) {
1450
  ctools_include('object-cache');
1451
  return ctools_object_cache_set($obj, 'panels_display:' . $did, $cache);
1452
}
1453

    
1454
/**
1455
 * Clear a object from the cache; used if the editing is aborted.
1456
 */
1457
function panels_cache_clear($obj, $did) {
1458
  ctools_include('object-cache');
1459
  return ctools_object_cache_clear($obj, 'panels_display:' . $did);
1460
}
1461

    
1462
/**
1463
 * Create the default cache for editing panel displays.
1464
 *
1465
 * If an application is using the Panels display editor without having
1466
 * specified a cache key, this method can be used to create the default
1467
 * cache.
1468
 */
1469
function panels_edit_cache_get_default(&$display, $content_types = NULL, $title = FALSE) {
1470
  if (empty($content_types)) {
1471
    $content_types = ctools_content_get_available_types();
1472
  }
1473

    
1474
  $display->cache_key = $display->did;
1475
  panels_cache_clear('display', $display->did);
1476

    
1477
  $cache = new stdClass();
1478
  $cache->display = &$display;
1479
  $cache->content_types = $content_types;
1480
  $cache->display_title = $title;
1481

    
1482
  panels_edit_cache_set($cache);
1483
  return $cache;
1484
}
1485

    
1486
/**
1487
 * Method to allow modules to provide their own caching mechanism for the
1488
 * display editor.
1489
 */
1490
function panels_edit_cache_get($cache_key) {
1491
  if (strpos($cache_key, ':') !== FALSE) {
1492
    list($module, $argument) = explode(':', $cache_key, 2);
1493
    return module_invoke($module, 'panels_cache_get', $argument);
1494
  }
1495

    
1496
  // Fall back to our normal method:
1497
  return panels_cache_get('display', $cache_key);
1498
}
1499

    
1500
/**
1501
 * Method to allow modules to provide their own caching mechanism for the
1502
 * display editor.
1503
 */
1504
function panels_edit_cache_set($cache) {
1505
  $cache_key = $cache->display->cache_key;
1506
  if (strpos($cache_key, ':') !== FALSE) {
1507
    list($module, $argument) = explode(':', $cache_key, 2);
1508
    return module_invoke($module, 'panels_cache_set', $argument, $cache);
1509
  }
1510

    
1511
  // Fall back to our normal method:
1512
  return panels_cache_set('display', $cache_key, $cache);
1513
}
1514

    
1515
/**
1516
 * Method to allow modules to provide their own mechanism to write the
1517
 * cache used in the display editor.
1518
 */
1519
function panels_edit_cache_save($cache) {
1520
  $cache_key = $cache->display->cache_key;
1521
  if (strpos($cache_key, ':') !== FALSE) {
1522
    list($module, $argument) = explode(':', $cache_key, 2);
1523
    if (function_exists($module . '_panels_cache_save')) {
1524
      return module_invoke($module, 'panels_cache_save', $argument, $cache);
1525
    }
1526
  }
1527

    
1528
  // Fall back to our normal method:
1529
  return panels_save_display($cache->display);
1530
}
1531

    
1532
/**
1533
 * Method to allow modules to provide their own mechanism to clear the
1534
 * cache used in the display editor.
1535
 */
1536
function panels_edit_cache_clear($cache) {
1537
  $cache_key = $cache->display->cache_key;
1538
  if (strpos($cache_key, ':') !== FALSE) {
1539
    list($module, $argument) = explode(':', $cache_key, 2);
1540
    if (function_exists($module . '_panels_cache_clear')) {
1541
      return module_invoke($module, 'panels_cache_clear', $argument, $cache);
1542
    }
1543
  }
1544

    
1545
  // Fall back to our normal method:
1546
  return panels_cache_clear('display', $cache_key);
1547
}
1548

    
1549
/**
1550
 * Method to allow modules to provide a mechanism to break locks.
1551
 */
1552
function panels_edit_cache_break_lock($cache) {
1553
  if (empty($cache->locked)) {
1554
    return;
1555
  }
1556

    
1557
  $cache_key = $cache->display->cache_key;
1558
  if (strpos($cache_key, ':') !== FALSE) {
1559
    list($module, $argument) = explode(':', $cache_key, 2);
1560
    if (function_exists($module . '_panels_cache_break_lock')) {
1561
      return module_invoke($module, 'panels_cache_break_lock', $argument, $cache);
1562
    }
1563
  }
1564

    
1565
  // Normal panel display editing has no locks, so we do nothing if there is
1566
  // no fallback.
1567
  return;
1568
}
1569

    
1570
// --------------------------------------------------------------------------
1571
// Callbacks on behalf of the panel_context plugin.
1572
//
1573
// The panel_context plugin lets Panels be used in page manager. These
1574
// callbacks allow the display editing system to use the page manager
1575
// cache rather than the default display cache. They are routed by the cache
1576
// key via panels_edit_cache_* functions.
1577

    
1578
/**
1579
 * Get display edit cache on behalf of panel context.
1580
 *
1581
 * The key is the second half of the key in this form:
1582
 * panel_context:TASK_NAME::HANDLER_NAME::args::url;
1583
 */
1584
function panel_context_panels_cache_get($key) {
1585
  ctools_include('common', 'panels');
1586
  ctools_include('context');
1587
  ctools_include('context-task-handler');
1588
  // this loads the panel context inc even if we don't use the plugin.
1589
  $plugin = page_manager_get_task_handler('panel_context');
1590

    
1591
  list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
1592
  $page = page_manager_get_page_cache($task_name);
1593
  if (isset($page->display_cache[$handler_name])) {
1594
    return $page->display_cache[$handler_name];
1595
  }
1596

    
1597
  if ($handler_name) {
1598
    $handler = &$page->handlers[$handler_name];
1599
  }
1600
  else {
1601
    $handler = &$page->new_handler;
1602
  }
1603
  $cache = new stdClass();
1604

    
1605
  $task = page_manager_get_task($page->task_id);
1606
  //ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
1607
  $arguments = array();
1608
  if ($args) {
1609
    $arguments = explode('\\', $args);
1610
    $contexts = ctools_context_handler_get_task_contexts($task, $page->subtask, $arguments);
1611
    $contexts = ctools_context_handler_get_handler_contexts($contexts, $handler);
1612
  }
1613
  else {
1614
    $contexts = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
1615
  }
1616

    
1617
  $cache->display = &panels_panel_context_get_display($handler);
1618
  $cache->display->context = $contexts;
1619
  $cache->display->cache_key = 'panel_context:' . $key;
1620
  $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
1621
  $cache->display_title = TRUE;
1622
  $cache->locked = $page->locked;
1623

    
1624
  return $cache;
1625
}
1626

    
1627
/**
1628
 * Get the Page Manager cache for the panel_context plugin.
1629
 */
1630
function _panel_context_panels_cache_get_page_cache($key, $cache) {
1631
  list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
1632
  $page = page_manager_get_page_cache($task_name);
1633
  $page->display_cache[$handler_name] = $cache;
1634
  if ($handler_name) {
1635
    $page->handlers[$handler_name]->conf['display'] = $cache->display;
1636
    $page->handler_info[$handler_name]['changed'] |= PAGE_MANAGER_CHANGED_CACHED;
1637
  }
1638
  else {
1639
    $page->new_handler->conf['display'] = $cache->display;
1640
  }
1641

    
1642
  return $page;
1643
}
1644

    
1645
/**
1646
 * Store a display edit in progress in the page cache.
1647
 */
1648
function panel_context_panels_cache_set($key, $cache) {
1649
  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
1650
  page_manager_set_page_cache($page);
1651
}
1652

    
1653
/**
1654
 * Save all changes made to a display using the Page Manager page cache.
1655
 */
1656
function panel_context_panels_cache_clear($key, $cache) {
1657
  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
1658
  page_manager_clear_page_cache($page->task_name);
1659
}
1660

    
1661
/**
1662
 * Save all changes made to a display using the Page Manager page cache.
1663
 */
1664
function panel_context_panels_cache_save($key, $cache) {
1665
  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
1666
  page_manager_save_page_cache($page);
1667
}
1668

    
1669
/**
1670
 * Break the lock on a page manager page.
1671
 */
1672
function panel_context_panels_cache_break_lock($key, $cache) {
1673
  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
1674
  ctools_object_cache_clear_all('page_manager_page', $page->task_name);
1675
}
1676

    
1677
// --------------------------------------------------------------------------
1678
// Callbacks on behalf of the panels page wizards
1679
//
1680
// The page wizards are a pluggable set of 'wizards' to make it easy to create
1681
// specific types of pages based upon whatever someone felt like putting
1682
// together. Since they will very often have content editing, we provide
1683
// a generic mechanism to allow them to store their editing cache in the
1684
// wizard cache.
1685
//
1686
// For them to use this mechanism, they just need to use:
1687
//   $cache = panels_edit_cache_get('panels_page_wizard:' . $plugin['name']);
1688

    
1689
/**
1690
 * Get display edit cache for the panels mini export UI
1691
 *
1692
 * The key is the second half of the key in this form:
1693
 * panels_page_wizard:TASK_NAME:HANDLER_NAME;
1694
 */
1695
function panels_page_wizard_panels_cache_get($key) {
1696
  ctools_include('page-wizard');
1697
  ctools_include('context');
1698
  $wizard_cache = page_manager_get_wizard_cache($key);
1699
  if (isset($wizard_cache->display_cache)) {
1700
    return $wizard_cache->display_cache;
1701
  }
1702

    
1703
  ctools_include('common', 'panels');
1704
  $cache = new stdClass();
1705
  $cache->display = $wizard_cache->display;
1706
  $cache->display->context = !empty($wizard_cache->context) ? $wizard_cache->context : array();
1707
  $cache->display->cache_key = 'panels_page_wizard:' . $key;
1708
  $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
1709
  $cache->display_title = TRUE;
1710

    
1711
  return $cache;
1712
}
1713

    
1714
/**
1715
 * Store a display edit in progress in the page cache.
1716
 */
1717
function panels_page_wizard_panels_cache_set($key, $cache) {
1718
  ctools_include('page-wizard');
1719
  $wizard_cache = page_manager_get_wizard_cache($key);
1720
  $wizard_cache->display_cache = $cache;
1721
  page_manager_set_wizard_cache($wizard_cache);
1722
}
1723

    
1724
// --------------------------------------------------------------------------
1725
// General utility functions
1726

    
1727
/**
1728
 * Perform a drupal_goto on a destination that may be an array like url().
1729
 */
1730
function panels_goto($destination) {
1731
  if (!is_array($destination)) {
1732
    return drupal_goto($destination);
1733
  }
1734
  else {
1735
    // Prevent notices by adding defaults
1736
    $destination += array(
1737
      'query' => NULL,
1738
      'fragment' => NULL,
1739
      'http_response_code' => NULL,
1740
    );
1741

    
1742
    return drupal_goto($destination['path'], $destination['query'], $destination['fragment'], $destination['http_response_code']);
1743
  }
1744
}
1745

    
1746

    
1747
/**
1748
 * For external use: Given a layout ID and a $content array, return the
1749
 * panel display.
1750
 *
1751
 * The content array is filled in based upon the content available in the
1752
 * layout. If it's a two column with a content array defined like
1753
 * @code
1754
 *   array(
1755
 *    'left' => t('Left side'),
1756
 *    'right' => t('Right side')
1757
 *  ),
1758
 * @endcode
1759
 *
1760
 * Then the $content array should be
1761
 * @code
1762
 * array(
1763
 *   'left' => $output_left,
1764
 *   'right' => $output_right,
1765
 * )
1766
 * @endcode
1767
 *
1768
 * The output within each panel region can be either a single rendered
1769
 * HTML string or an array of rendered HTML strings as though they were
1770
 * panes. They will simply be concatenated together without separators.
1771
 */
1772
function panels_print_layout($layout, $content, $meta = 'standard') {
1773
  ctools_include('plugins', 'panels');
1774

    
1775
  // Create a temporary display for this.
1776
  $display = panels_new_display();
1777
  $display->layout = is_array($layout) ? $layout['name'] : $layout;
1778
  $display->content = $content;
1779

    
1780
  // Get our simple renderer
1781
  $renderer = panels_get_renderer_handler('simple', $display);
1782
  $renderer->meta_location = $meta;
1783

    
1784
  return $renderer->render();
1785
}
1786

    
1787
/**
1788
 * Filter callback for array_filter to remove builders from a list of layouts.
1789
 */
1790
function _panels_builder_filter($layout) {
1791
  return empty($layout['builder']);
1792
}
1793

    
1794
/**
1795
 * Implements hook_get_pane_links_alter().
1796
 *
1797
 * add links to the Panels pane dropdown menu.
1798
 */
1799
function panels_get_pane_links_alter(&$links, $pane, $content_type) {
1800
  if ($pane->type === "block"){
1801
    $prefixed_name = $pane->subtype;
1802

    
1803
    // breakup the subtype string into parts.
1804
    $exploded_subtype = explode('-', $pane->subtype);
1805

    
1806
    // get the first part of the string.
1807
    $subtype_prefix = $exploded_subtype[0];
1808

    
1809
    // get the first part of the string and add a hyphen.
1810
    $subtype_prefix_hyphen = $exploded_subtype[0] . '-';
1811

    
1812
    // remove the prefix block- to get the name.
1813
    $name_of_block = ltrim( $prefixed_name, $subtype_prefix_hyphen);
1814

    
1815
    // check for user added menus created at /admin/structure/menu/add
1816
    // menus of that type have a subtype that is prefixed with menu-menu-
1817
    if (substr($prefixed_name, 0, 10) === "menu-menu-"){
1818
      // remove the first prefix menu- from menu-menu- to get the name.
1819
      $name_of_block = substr($prefixed_name, 5);
1820

    
1821
      $links['top'][] = array(
1822
        'title' => t('Edit block'),
1823
        'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
1824
        'attributes' => array('target' => array('_blank')),
1825
      );
1826

    
1827
      $links['top'][] = array(
1828
        'title' => t('Edit menu links'),
1829
        'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
1830
        'attributes' => array('target' => array('_blank')),
1831
      );
1832
    }
1833

    
1834
    // check for module provided menu blocks like Devels or Features
1835
    // menus of that type have a subtype that is prefixed with menu-
1836
    elseif(substr($prefixed_name, 0, 5) === "menu-"){
1837
      // remove the first prefix menu- to get the name.
1838
      $name_of_block = substr($prefixed_name, 5);
1839

    
1840
      $links['top'][] = array(
1841
        'title' => t('Edit block'),
1842
        'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
1843
        'attributes' => array('target' => array('_blank')),
1844
      );
1845

    
1846
      $links['top'][] = array(
1847
        'title' => t('Edit menu links'),
1848
        'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
1849
        'attributes' => array('target' => array('_blank')),
1850
      );
1851
    }
1852

    
1853
    // check for system blocks with menu links
1854
    elseif(substr($prefixed_name, 0, 7) === "system-") {
1855
      // remove the first prefix system- to get the name
1856
      $name_of_block = substr($prefixed_name, 7);
1857

    
1858
      $names_of_system_menus = menu_list_system_menus();
1859

    
1860
      $links['top'][] = array(
1861
        'title' => t('Edit block'),
1862
        'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
1863
        'attributes' => array('target' => array('_blank')),
1864
      );
1865

    
1866
      if (array_key_exists($name_of_block, $names_of_system_menus)){
1867
        $links['top'][] = array(
1868
          'title' => t('Edit menu links'),
1869
          'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
1870
          'attributes' => array('target' => array('_blank')),
1871
        );
1872
      }
1873
    }
1874

    
1875
    // for all other blocks without menus
1876
    else{
1877
      $links['top'][] = array(
1878
        'title' => t('Edit block'),
1879
        'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
1880
        'attributes' => array('target' => array('_blank')),
1881
      );
1882
    }
1883
  }
1884
}
1885

    
1886
// --------------------------------------------------------------------------
1887
// Deprecated functions
1888
//
1889
// Everything below this line will eventually go away.
1890

    
1891
/**
1892
 * panels path helper function
1893
 */
1894
function panels_get_path($file, $base_path = FALSE, $module = 'panels') {
1895
  $output = $base_path ? base_path() : '';
1896
  return $output . drupal_get_path('module', $module) . '/' . $file;
1897
}
1898

    
1899
/**
1900
 * Remove default sidebar related body classes and provide own css classes
1901
 */
1902
function panels_preprocess_html(&$vars) {
1903
  $panel_body_css = &drupal_static('panel_body_css');
1904
  if (!empty($panel_body_css['body_classes_to_remove'])) {
1905
    $classes_to_remove = array_filter(explode(' ', $panel_body_css['body_classes_to_remove']), 'strlen');
1906
    foreach ($vars['classes_array'] as $key => $css_class) {
1907
      if (in_array($css_class, $classes_to_remove)) {
1908
        unset($vars['classes_array'][$key]);
1909
      }
1910
    }
1911
  }
1912
  if (!empty($panel_body_css['body_classes_to_add'])) {
1913
    $vars['classes_array'][] = check_plain($panel_body_css['body_classes_to_add']);
1914
  }
1915
}