Projet

Général

Profil

Paste
Télécharger (69,2 ko) Statistiques
| Branche: | Révision:

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

1
<?php
2

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

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

    
10
/**
11
 * The current working panels version.
12
 *
13
 * In a release, it should be 7.x-3.x, which should match what drush make will
14
 * create. In a dev format, it should be 7.x-3.(x+1)-dev, which will allow
15
 * modules depending on new features in panels to depend on panels > 7.x-3.x.
16
 *
17
 * To define a specific version of Panels as a dependency for another module,
18
 * simply include a dependency line in that module's info file, e.g.:
19
 *   ; Requires Panels v7.x-3.4 or newer.
20
 *   dependencies[] = panels (>=3.4)
21
 */
22
define('PANELS_VERSION', '7.x-3.8');
23

    
24

    
25
// Hide title use to be TRUE/FALSE. So FALSE remains old behavior.
26
define('PANELS_TITLE_FIXED', 0);
27
// And TRUE meant no title.
28
define('PANELS_TITLE_NONE', 1);
29
// And this is the new behavior, where the title field will pick from a pane.
30
define('PANELS_TITLE_PANE', 2);
31

    
32
/**
33
 * Returns the API version of Panels. This didn't exist in 1.
34
 *
35
 * @todo -- this should work more like the CTools API version.
36
 *
37
 * @return array
38
 *   An array with the major and minor versions
39
 */
40
function panels_api_version() {
41
  return array(3, 1);
42
}
43

    
44
/**
45
 * Implements hook_theme().
46
 */
47
function panels_theme() {
48
  // Safety: go away if CTools is not at an appropriate version.
49
  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
50
    return array();
51
  }
52

    
53
  $theme = array();
54
  $theme['panels_layout_link'] = array(
55
    'variables' => array(
56
      'title' => NULL,
57
      'id' => NULL,
58
      'image' => NULL,
59
      'link' => NULL,
60
      'class' => NULL,
61
      'options' => NULL,
62
    ),
63
  );
64
  $theme['panels_layout_icon'] = array(
65
    'variables' => array(
66
      'id' => NULL,
67
      'image' => NULL,
68
      'title' => NULL,
69
    ),
70
  );
71
  $theme['panels_pane'] = array(
72
    'variables' => array(
73
      'content' => array(),
74
      'pane' => array(),
75
      'display' => array(),
76
    ),
77
    'path' => drupal_get_path('module', 'panels') . '/templates',
78
    'template' => 'panels-pane',
79
  );
80
  $theme['panels_common_content_list'] = array(
81
    'variables' => array('display' => NULL),
82
    'file' => 'includes/common.inc',
83
  );
84
  $theme['panels_render_display_form'] = array(
85
    'render element' => 'element',
86
  );
87

    
88
  $theme['panels_dashboard'] = array(
89
    'variables' => array(),
90
    'path' => drupal_get_path('module', 'panels') . '/templates',
91
    'file' => '../includes/callbacks.inc',
92
    'template' => 'panels-dashboard',
93
  );
94

    
95
  $theme['panels_dashboard_link'] = array(
96
    'variables' => array('link' => array()),
97
    'path' => drupal_get_path('module', 'panels') . '/templates',
98
    'file' => '../includes/callbacks.inc',
99
    'template' => 'panels-dashboard-link',
100
  );
101

    
102
  $theme['panels_dashboard_block'] = array(
103
    'variables' => array('block' => array()),
104
    'path' => drupal_get_path('module', 'panels') . '/templates',
105
    'file' => '../includes/callbacks.inc',
106
    'template' => 'panels-dashboard-block',
107
  );
108

    
109
  $theme['panels_add_content_modal'] = array(
110
    'variables' => array(
111
      'renderer' => NULL,
112
      'categories' => array(),
113
      'region' => NULL,
114
      'category' => NULL,
115
      'column_count' => 2,
116
    ),
117
    'path' => drupal_get_path('module', 'panels') . '/templates',
118
    'file' => '../includes/add-content.inc',
119
    'template' => 'panels-add-content-modal',
120
  );
121

    
122
  $theme['panels_add_content_link'] = array(
123
    'variables' => array(
124
      'renderer' => NULL,
125
      'region' => NULL,
126
      'content_type' => NULL,
127
    ),
128
    'path' => drupal_get_path('module', 'panels') . '/templates',
129
    'file' => '../includes/add-content.inc',
130
    'template' => 'panels-add-content-link',
131
  );
132

    
133
  // Register layout and style themes on behalf of all of these items.
134
  ctools_include('plugins', 'panels');
135

    
136
  // No need to worry about files; the plugin has to already be loaded for us
137
  // to even know what the theme function is, so files will be auto included.
138
  $layouts = panels_get_layouts();
139
  foreach ($layouts as $name => $data) {
140
    foreach (array('theme', 'admin theme') as $callback) {
141
      if (!empty($data[$callback])) {
142
        $theme[$data[$callback]] = array(
143
          'variables' => array(
144
            'css_id' => NULL,
145
            'content' => NULL,
146
            'settings' => NULL,
147
            'display' => NULL,
148
            'layout' => NULL,
149
            'renderer' => NULL,
150
          ),
151
          'path' => $data['path'],
152
          'file' => $data['file'],
153
        );
154

    
155
        // If no theme function exists, assume template.
156
        if (!function_exists("theme_$data[theme]")) {
157
          $theme[$data[$callback]]['template'] = str_replace('_', '-', $data[$callback]);
158
          // For preprocess.
159
          $theme[$data[$callback]]['file'] = $data['file'];
160
        }
161
      }
162
    }
163
  }
164

    
165
  $styles = panels_get_styles();
166
  foreach ($styles as $name => $data) {
167
    if (!empty($data['render pane'])) {
168
      $theme[$data['render pane']] = array(
169
        'variables' => array(
170
          'content' => NULL,
171
          'pane' => NULL,
172
          'display' => NULL,
173
          'style' => NULL,
174
          'settings' => NULL,
175
        ),
176
        'path' => $data['path'],
177
        'file' => $data['file'],
178
      );
179
    }
180
    if (!empty($data['render region'])) {
181
      $theme[$data['render region']] = array(
182
        'variables' => array(
183
          'display' => NULL,
184
          'owner_id' => NULL,
185
          'panes' => NULL,
186
          'settings' => NULL,
187
          'region_id' => NULL,
188
          'style' => NULL,
189
        ),
190
        'path' => $data['path'],
191
        'file' => $data['file'],
192
      );
193
    }
194

    
195
    if (!empty($data['hook theme'])) {
196
      if (is_array($data['hook theme'])) {
197
        $theme += $data['hook theme'];
198
      }
199
      elseif (function_exists($data['hook theme'])) {
200
        $data['hook theme']($theme, $data);
201
      }
202
    }
203
  }
204
  return $theme;
205
}
206

    
207
/**
208
 * Implements hook_menu().
209
 */
210
function panels_menu() {
211
  // Safety: go away if CTools is not at an appropriate version.
212
  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
213
    return array();
214
  }
215
  $items = array();
216

    
217
  // Base AJAX router callback.
218
  $items['panels/ajax'] = array(
219
    'access arguments' => array('access content'),
220
    'page callback' => 'panels_ajax_router',
221
    'theme callback' => 'ajax_base_page_theme',
222
    'delivery callback' => 'ajax_deliver',
223
    'type' => MENU_CALLBACK,
224
  );
225

    
226
  $admin_base = array(
227
    'file' => 'includes/callbacks.inc',
228
    'access arguments' => array('use panels dashboard'),
229
  );
230
  // Provide a nice location for a panels admin panel.
231
  $items['admin/structure/panels'] = array(
232
    'title' => 'Panels',
233
    'page callback' => 'panels_admin_page',
234
    'description' => 'Get a bird\'s eye view of items related to Panels.',
235
  ) + $admin_base;
236

    
237
  $items['admin/structure/panels/dashboard'] = array(
238
    'title' => 'Dashboard',
239
    'page callback' => 'panels_admin_page',
240
    'type' => MENU_DEFAULT_LOCAL_TASK,
241
    'weight' => -10,
242
  ) + $admin_base;
243

    
244
  $items['admin/structure/panels/settings'] = array(
245
    'title' => 'Settings',
246
    'page callback' => 'drupal_get_form',
247
    'page arguments' => array('panels_admin_settings_page'),
248
    'type' => MENU_LOCAL_TASK,
249
  ) + $admin_base;
250

    
251
  $items['admin/structure/panels/settings/general'] = array(
252
    'title' => 'General',
253
    'page callback' => 'drupal_get_form',
254
    'page arguments' => array('panels_admin_settings_page'),
255
    'access arguments' => array('administer page manager'),
256
    'type' => MENU_DEFAULT_LOCAL_TASK,
257
    'weight' => -10,
258
  ) + $admin_base;
259

    
260
  if (module_exists('page_manager')) {
261
    $items['admin/structure/panels/settings/panel-page'] = array(
262
      'title' => 'Panel pages',
263
      'page callback' => 'panels_admin_panel_context_page',
264
      'type' => MENU_LOCAL_TASK,
265
      'weight' => -10,
266
    ) + $admin_base;
267
  }
268

    
269
  ctools_include('plugins', 'panels');
270
  $layouts = panels_get_layouts();
271
  foreach ($layouts as $name => $data) {
272
    if (!empty($data['hook menu'])) {
273
      if (is_array($data['hook menu'])) {
274
        $items += $data['hook menu'];
275
      }
276
      elseif (function_exists($data['hook menu'])) {
277
        $data['hook menu']($items, $data);
278
      }
279
    }
280
  }
281
  return $items;
282
}
283

    
284
/**
285
 * Menu loader function to load a cache item for Panels AJAX.
286
 *
287
 * This load all of the includes needed to perform AJAX, and loads the
288
 * cache object and makes sure it is valid.
289
 */
290
function panels_edit_cache_load($cache_key) {
291
  ctools_include('display-edit', 'panels');
292
  ctools_include('plugins', 'panels');
293
  ctools_include('ajax');
294
  ctools_include('modal');
295
  ctools_include('context');
296

    
297
  return panels_edit_cache_get($cache_key);
298
}
299

    
300
/**
301
 * Implements hook_init().
302
 */
303
function panels_init() {
304
  // Safety: go away if CTools is not at an appropriate version.
305
  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
306
    if (user_access('administer site configuration')) {
307
      drupal_set_message(t('Panels is enabled but CTools is out of date. All Panels modules are disabled until CTools is updated. See the status page for more information.'), 'error');
308
    }
309
    return;
310
  }
311

    
312
  ctools_add_css('panels', 'panels');
313
}
314

    
315
/**
316
 * Implements hook_permission().
317
 *
318
 * @todo Almost all of these need to be moved into pipelines.
319
 */
320
function panels_permission() {
321
  return array(
322
    'use panels dashboard' => array(
323
      'title' => t("Use Panels Dashboard"),
324
      'description' => t('Allows a user to access the <a href="@url">Panels Dashboard</a>.', array('@url' => url('admin/structure/panels'))),
325
    ),
326
    // @todo
327
    'view pane admin links' => array(
328
      'title' => t("View administrative links on Panel panes"),
329
      'description' => "",
330
    ),
331
    // @todo should we really have a global perm for this, or should it be moved into a pipeline question?
332
    'administer pane access' => array(
333
      'title' => t("Configure access settings on Panel panes"),
334
      'description' => t("Access rules (often also called visibility rules) can be configured on a per-pane basis. This permission allows users to configure those settings."),
335
    ),
336
    'use panels in place editing' => array(
337
      'title' => t("Use the Panels In-Place Editor"),
338
      'description' => t("Allows a user to utilize Panels' In-Place Editor."),
339
    ),
340
    'change layouts in place editing' => array(
341
      'title' => t("Change layouts with the Panels In-Place Editor"),
342
      'description' => t("Allows a user to change layouts with the IPE."),
343
    ),
344
    'bypass access in place editing' => array(
345
      'title' => t("Bypass access checks when using Panels In-Place Editor"),
346
      'description' => t("Allows using IPE even if user does not have additional permissions granted by other modules."),
347
      'restrict access' => TRUE,
348
    ),
349
    'administer advanced pane settings' => array(
350
      'title' => t("Configure advanced settings on Panel panes"),
351
      'description' => "",
352
    ),
353
    'administer panels layouts' => array(
354
      'title' => t("Administer Panels layouts"),
355
      'description' => t("Allows a user to administer exported Panels layout plugins & instances."),
356
    ),
357
    'administer panels styles' => array(
358
      'title' => t("Administer Panels styles"),
359
      'description' => t("DEPRECATED: Modules using this permission should use specific style permissions. See Issue #2329419 for more info."),
360
    ),
361
    'administer panels display styles' => array(
362
      'title' => t("Administer Panels display styles"),
363
      'description' => t("Allows a user to administer the styles of Panel displays."),
364
    ),
365
    'administer panels pane styles' => array(
366
      'title' => t("Administer Panels pane styles"),
367
      'description' => t("Allows a user to administer the styles of Panel panes."),
368
    ),
369
    'administer panels region styles' => array(
370
      'title' => t("Administer Panels region styles"),
371
      'description' => t("Allows a user to administer the styles of Panel regions."),
372
    ),
373
    'use panels caching features' => array(
374
      'title' => t("Configure caching settings on Panels"),
375
      'description' => t("Allows a user to configure caching on Panels displays and panes."),
376
    ),
377
    'use panels locks' => array(
378
      'title' => t('Use panel locks'),
379
      'description' => t('Allows a user to lock and unlock panes in a panel display.'),
380
    ),
381
    'use ipe with page manager' => array(
382
      'title' => t("Use the Panels In-Place Editor with Page Manager"),
383
      'description' => t('Allows users with access to the In-Place editor to administer page manager pages. This permission is only needed for users without "use page manager" access.'),
384
    ),
385
  );
386
}
387

    
388
/**
389
 * Implements hook_flush_caches().
390
 */
391
function panels_flush_caches() {
392
  if (db_table_exists('cache_panels')) {
393
    return array('cache_panels');
394
  }
395
}
396

    
397
/**
398
 * CTools hook implementations.
399
 *
400
 * These aren't core Drupal hooks but they are just as important.
401
 */
402

    
403
/**
404
 * Implements hook_ctools_plugin_directory().
405
 */
406
function panels_ctools_plugin_directory($module, $plugin) {
407
  // To let the system know we implement task and task_handler plugins.
408
  // Safety: go away if CTools is not at an appropriate version.
409
  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
410
    return;
411
  }
412

    
413
  // We don't support the 'ctools' 'cache' plugin and pretending to causes
414
  // errors when they're in use.
415
  if ($module == 'ctools' && $plugin == 'cache') {
416
    // If we did we'd make a plugin/ctools_cache or something.
417
    return;
418
  }
419

    
420
  if ($module == 'page_manager' || $module == 'panels' || $module == 'ctools' || $module == 'stylizer') {
421
    // Panels and CTools both implement a 'cache' plugin but we don't implement
422
    // the CTools version.
423
    if ($module == 'ctools' && $plugin == 'cache') {
424
      return;
425
    }
426
    return 'plugins/' . $plugin;
427
  }
428
}
429

    
430
/**
431
 * Implements hook_ctools_plugin_type().
432
 *
433
 * Register layout, style, cache, and display_renderer plugin types, declaring
434
 * relevant plugin type information as necessary.
435
 */
436
function panels_ctools_plugin_type() {
437
  return array(
438
    'layouts' => array(
439
      // We can define layouts in themes.
440
      'load themes' => TRUE,
441
      'process' => 'panels_layout_process',
442
      'child plugins' => TRUE,
443
    ),
444
    'styles' => array(
445
      'load themes' => TRUE,
446
      'process' => 'panels_plugin_styles_process',
447
      'child plugins' => TRUE,
448
    ),
449
    'cache' => array(),
450
    'display_renderers' => array(
451
      'classes' => array('renderer'),
452
    ),
453
    'panels_storage' => array(),
454
  );
455
}
456

    
457
/**
458
 * Ensure a layout has a minimal set of data.
459
 */
460
function panels_layout_process(&$plugin) {
461
  $plugin += array(
462
    'category' => t('Miscellaneous'),
463
    'description' => '',
464
  );
465
}
466

    
467
/**
468
 * Implements hook_ctools_plugin_api().
469
 */
470
function panels_ctools_plugin_api($owner, $api) {
471
  // Inform CTools about version information for various plugins implemented by
472
  // panels.
473
  if ($owner == 'panels' && $api == 'styles') {
474
    // As of 6.x-3.6, Panels has a slightly new system for style plugins.
475
    return array('version' => 2.0);
476
  }
477

    
478
  if ($owner == 'panels' && $api == 'pipelines') {
479
    return array(
480
      'version' => 1,
481
      'path' => drupal_get_path('module', 'panels') . '/includes',
482
    );
483
  }
484
}
485

    
486
/**
487
 * Implements hook_views_api().
488
 */
489
function panels_views_api() {
490
  return array(
491
    'api' => 2,
492
    'path' => drupal_get_path('module', 'panels') . '/plugins/views',
493
  );
494
}
495

    
496
/**
497
 * Perform additional processing on a style plugin.
498
 *
499
 * Currently this is only being used to apply versioning information to style
500
 * plugins in order to ensure the legacy renderer passes the right type of
501
 * parameters to a style plugin in a hybrid environment of both new and old
502
 * plugins.
503
 *
504
 * @param array $plugin
505
 *   The style plugin that is being processed.
506
 * @param array $info
507
 *   The style plugin type info array.
508
 *
509
 * @see _ctools_process_data()
510
 */
511
function panels_plugin_styles_process(&$plugin, $info) {
512
  $plugin += array(
513
    'weight' => 0,
514
  );
515

    
516
  $compliant_modules = ctools_plugin_api_info('panels', 'styles', 2.0, 2.0);
517
  $plugin['version'] = empty($compliant_modules[$plugin['module']]) ? 1.0 : $compliant_modules[$plugin['module']]['version'];
518
}
519

    
520
/**
521
 * Declare what style types Panels uses.
522
 */
523
function panels_ctools_style_base_types() {
524
  return array(
525
    'region' => array(
526
      'title' => t('Panel region'),
527
      'preview' => 'panels_stylizer_region_preview',
528
      'theme variables' => array(
529
        'settings' => NULL,
530
        'class' => NULL,
531
        'content' => NULL,
532
      ),
533
    ),
534
    'pane' => array(
535
      'title' => t('Panel pane'),
536
      'preview' => 'panels_stylizer_pane_preview',
537
      'theme variables' => array(
538
        'settings' => NULL,
539
        'content' => NULL,
540
        'pane' => NULL,
541
        'display' => NULL,
542
      ),
543
    ),
544
  );
545
}
546

    
547
/**
548
 * Generates Lorem Ipsum.
549
 *
550
 * @return string
551
 *   Lorem ipsum string.
552
 */
553
function panels_stylizer_lipsum() {
554
  return <<<LIPSUM
555
    <p>
556
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at
557
      velit dolor. Donec egestas tellus sit amet urna rhoncus adipiscing. Proin
558
      nec porttitor sem. Maecenas aliquam, purus nec tempus dignissim, nulla arcu
559
      aliquam diam, non tincidunt massa ante vel dolor. Aliquam sapien sapien,
560
      tincidunt id tristique at, pretium sagittis libero.
561
    </p>
562
    <p>
563
      Nulla facilisi. Curabitur lacinia, tellus sed tristique consequat, diam
564
      lorem scelerisque felis, at dictum purus augue facilisis lorem. Duis
565
      pharetra dignissim rutrum. Curabitur ac elit id dui dapibus tincidunt.
566
      Nulla eget sem quam, non eleifend eros. Cras porttitor tempus lectus ac
567
      scelerisque. Curabitur vehicula bibendum lorem, vitae ornare ligula
568
      venenatis ut.
569
    </p>
570
LIPSUM;
571
}
572

    
573
/**
574
 * Generate a preview given the current settings.
575
 */
576
function panels_stylizer_region_preview($plugin, $settings) {
577
  ctools_stylizer_add_css($plugin, $settings);
578
  return theme(
579
    $plugin['theme'],
580
    array(
581
      'settings' => $settings,
582
      'class' => ctools_stylizer_get_css_class($plugin, $settings),
583
      'content' => panels_stylizer_lipsum(),
584
    )
585
  );
586
}
587

    
588
/**
589
 * Generate a preview given the current settings.
590
 */
591
function panels_stylizer_pane_preview($plugin, $settings) {
592
  ctools_stylizer_add_css($plugin, $settings);
593
  $pane = new stdClass();
594

    
595
  $content = new stdClass();
596
  $content->title = t('Lorem ipsum');
597
  $content->content = panels_stylizer_lipsum();
598
  $content->type = 'dummy';
599
  $content->subtype = 'dummy';
600

    
601
  $content->css_class = ctools_stylizer_get_css_class($plugin, $settings);
602

    
603
  $display = new panels_display();
604

    
605
  if (!empty($plugin['theme'])) {
606
    return theme(
607
      $plugin['theme'],
608
      array(
609
        'settings' => $settings,
610
        'content' => $content,
611
        'pane' => $pane,
612
        'display' => $display,
613
      )
614
    );
615
  }
616
  else {
617
    return theme(
618
      'panels_pane',
619
      array(
620
        'content' => $content,
621
        'pane' => $pane,
622
        'display' => $display,
623
      )
624
    );
625
  }
626
}
627

    
628
/**
629
 * Panels display editing.
630
 */
631

    
632
/**
633
 * @defgroup mainapi Functions comprising the main panels API.
634
 */
635

    
636
/**
637
 * Main API entry point to edit a panel display.
638
 *
639
 * Sample implementations utiltizing the the complex $destination behavior can
640
 * be found in panels_page_edit_content() and, in a separate contrib module,
641
 * OG Blueprints (http://drupal.org/project/og_blueprints),
642
 * og_blueprints_blueprint_edit().
643
 *
644
 * @param object $display
645
 *   Instanceof panels_display.
646
 *
647
 *   A fully loaded panels $display object, as returned from
648
 *   panels_load_display(). Merely passing a did is NOT sufficient.
649
 *   Note that 'fully loaded' means the $display must already be loaded with
650
 *   any contexts the caller wishes to have set for the display.
651
 * @param mixed $destination
652
 *   The redirect destination that the user should be taken to on form
653
 *   submission or cancellation. With panels_edit, $destination has complex
654
 *   effects on the return values of panels_edit() once the form has been
655
 *   submitted. See the explanation of the return value below to understand the
656
 *   different types of values returned by panels_edit() at different stages of
657
 *   FAPI. Under most circumstances, simply passing in drupal_get_destination()
658
 *   is all that's necessary.
659
 * @param array $content_types
660
 *   An associative array of allowed content types, typically as returned from
661
 *   panels_common_get_allowed_types(). Note that context partially governs
662
 *   available content types, so you will want to create any relevant contexts
663
 *   using panels_create_context() or panels_create_context_empty() to make sure
664
 *   all the appropriate content types are available.
665
 *
666
 * @return mixed
667
 *   Because the functions called by panels_edit() invoke the form API,
668
 *   this function returns different values depending on the stage of form
669
 *   submission we're at. In Drupal 5, the phase of form submission is indicated
670
 *   by the contents of $_POST['op']. Here is what you'll get at different
671
 *   stages:
672
 *     -  If !$_POST['op']: then we're on on the initial passthrough and the
673
 *        form is being rendered, so it's the $form itself that's being
674
 *        returned. Because negative margins, a common CSS technique, bork the
675
 *        display editor's ajax drag-and-drop, it's important that the $output
676
 *        be printed, not returned. Use this syntax in the caller function:
677
 *        print theme('page', panels_edit($display, $destination, $content_types), FALSE);
678
 *     -  If $_POST['op'] == t('Cancel'): form submission has been cancelled.
679
 *        If empty($destination) == FALSE, then there is no return value and the
680
 *        panels API takes care of redirecting to $destination.
681
 *        If empty($destination) == TRUE, then there's still no return value,
682
 *        but the caller function has to take care of form redirection.
683
 *     -  If $_POST['op'] == ('Save'): the form has been submitted successfully
684
 *        and has run through panels_edit_display_submit().
685
 *        $output depends on the value of $destination:
686
 *     -  If empty($destination) == TRUE: $output contains the modified $display
687
 *        object, and no redirection will occur. This option is useful if the
688
 *        caller needs to perform additional operations on or with the modified
689
 *        $display before the page request is complete. Using hook_form_alter()
690
 *        to add an additional submit handler is typically the preferred method
691
 *        for something like this, but there are certain use cases where that is
692
 *        infeasible and $destination = NULL should be used instead. If this
693
 *        method is employed, the caller will need to handle form redirection.
694
 *        Note that having $_REQUEST['destination'] set, whether via
695
 *        drupal_get_destination() or some other method, will NOT interfere with
696
 *        this functionality; consequently, you can use drupal_get_destination()
697
 *        to safely store your desired redirect in the caller function, then
698
 *        simply use drupal_goto() once panels_edit() has done its business.
699
 *     -  If empty($destination) == FALSE: the form will redirect to the URL
700
 *        string given in $destination and NO value will be returned.
701
 *
702
 * @ingroup mainapi
703
 */
704
function panels_edit($display, $destination = NULL, $content_types = NULL, $title = FALSE) {
705
  ctools_include('display-edit', 'panels');
706
  ctools_include('ajax');
707
  ctools_include('plugins', 'panels');
708
  return _panels_edit($display, $destination, $content_types, $title);
709
}
710

    
711
/**
712
 * API entry point for selecting a layout for a given display.
713
 *
714
 * Layout selection is nothing more than a list of radio items encompassing the
715
 * available layouts for this display, as defined by .inc files in the
716
 * panels/layouts subdirectory. The only real complexity occurs when a user
717
 * attempts to change the layout of a display that has some content in it.
718
 *
719
 * @param object $display
720
 *   A fully loaded panels $display object, as returned from
721
 *   panels_load_display(). Merely passing a did is NOT sufficient.
722
 * @param string $finish
723
 *   A string that will be used for the text of the form submission button. If
724
 *   no value is provided, then the form submission button will default to
725
 *   t('Save').
726
 * @param mixed $destination
727
 *   Basic usage is a string containing the URL that the form should redirect to
728
 *   upon submission. For a discussion of advanced usages, see panels_edit().
729
 * @param mixed $allowed_layouts
730
 *   Allowed layouts has three different behaviors that depend on which of three
731
 *   value types are passed in by the caller:
732
 *     #- if $allowed_layouts instanceof panels_allowed_layouts
733
 *        (includes subclasses): the most complex use of the API. The caller is
734
 *        passing in a loaded panels_allowed_layouts object that the client
735
 *        module previously created and stored somewhere using a custom storage
736
 *        mechanism.
737
 *     #- if is_string($allowed_layouts): the string will be used in a call to
738
 *        variable_get() which will call the
739
 *        $allowed_layouts . '_allowed_layouts' var. If the data was stored
740
 *        properly in the system var, the $allowed_layouts object will be
741
 *        unserialized and recreated.
742
 *     #- if is_null($allowed_layouts): the default behavior, which also
743
 *        provides backwards compatibility for implementations of the Panels2
744
 *        API written before beta4. In this case, a dummy panels_allowed_layouts
745
 *        object is created which does not restrict any layouts. Subsequent
746
 *        behavior is indistinguishable from pre-beta4 behavior.
747
 *
748
 * @return mixed
749
 *   Can return nothing, or a modified $display object, or a redirection string;
750
 *   return values for the panels_edit* family of functions are quite complex.
751
 *   See panels_edit() for detailed discussion.
752
 *
753
 * @see panels_edit()
754
 * @see panels_common_set_allowed_layouts()
755
 */
756
function panels_edit_layout($display, $finish, $destination = NULL, $allowed_layouts = NULL) {
757
  ctools_include('display-layout', 'panels');
758
  ctools_include('plugins', 'panels');
759
  return _panels_edit_layout($display, $finish, $destination, $allowed_layouts);
760
}
761

    
762
/**
763
 * Panels database functions.
764
 */
765

    
766
/**
767
 * Forms the basis of a panel display.
768
 */
769
class panels_display {
770
  public $args = array();
771
  public $content = array();
772
  public $panels = array();
773
  public $incoming_content = NULL;
774
  public $css_id = NULL;
775
  public $context = array();
776
  public $did = 'new';
777
  public $renderer = 'standard';
778

    
779
  /**
780
   * Add a pane.
781
   */
782
  public function add_pane(&$pane, $location = NULL) {
783
    // If no location specified, use what's set in the pane.
784
    if (empty($location)) {
785
      $location = $pane->panel;
786
    }
787
    else {
788
      $pane->panel = $location;
789
    }
790

    
791
    // Generate a permanent uuid for this pane, and use
792
    // it as a temporary pid.
793
    $pane->uuid = ctools_uuid_generate();
794
    $pane->pid = 'new-' . $pane->uuid;
795

    
796
    // Add the pane to the appropriate spots.
797
    $this->content[$pane->pid] = &$pane;
798
    $this->panels[$location][] = $pane->pid;
799
  }
800

    
801
  /**
802
   * Duplicate a pane.
803
   */
804
  public function duplicate_pane($pid, $location = FALSE) {
805
    $pane = $this->clone_pane($pid);
806
    $this->add_pane($pane, $location);
807
  }
808

    
809
  /**
810
   * Clone a pane.
811
   */
812
  public function clone_pane($pid) {
813
    $pane = clone $this->content[$pid];
814
    $pane->uuid = ctools_uuid_generate();
815
    return $pane;
816
  }
817

    
818
  /**
819
   * Get the title from a display.
820
   *
821
   * The display must have already been rendered, or the setting to set the
822
   * display's title from a pane's title will not have worked.
823
   *
824
   * @return mixed
825
   *   The title to use. If NULL, this means to let any default title that may
826
   *   be in use pass through. i.e, do not actually set the title.
827
   */
828
  public function get_title() {
829
    switch ($this->hide_title) {
830
      case PANELS_TITLE_NONE:
831
        return '';
832

    
833
      case PANELS_TITLE_PANE:
834
        return isset($this->stored_pane_title) ? $this->stored_pane_title : '';
835

    
836
      case PANELS_TITLE_FIXED:
837
      case FALSE;
838
        // For old exported panels that are not in the database.
839
        if (!empty($this->title)) {
840
          return filter_xss_admin(ctools_context_keyword_substitute($this->title, array(), $this->context));
841
        }
842
        return NULL;
843
    }
844
  }
845

    
846
  /**
847
   * Render this panels display.
848
   *
849
   * After checking to ensure the designated layout plugin is valid, a
850
   * display renderer object is spawned and runs its rendering logic.
851
   *
852
   * @param mixed $renderer
853
   *    An instantiated display renderer object, or the name of a display
854
   *    renderer plugin+class to be fetched. Defaults to NULL. When NULL, the
855
   *    predesignated display renderer will be used.
856
   *
857
   * @return mixed
858
   *    NULL or output of render function.
859
   */
860
  public function render($renderer = NULL) {
861
    $layout = panels_get_layout($this->layout);
862
    if (!$layout) {
863
      return NULL;
864
    }
865

    
866
    // If we were not given a renderer object, load it.
867
    if (!is_object($renderer)) {
868
      // If the renderer was not specified, default to $this->renderer
869
      // which is either standard or was already set for us.
870
      $renderer = panels_get_renderer_handler(!empty($renderer) ? $renderer : $this->renderer, $this);
871
      if (!$renderer) {
872
        return NULL;
873
      }
874
    }
875

    
876
    $output = '';
877
    // Let modules act just prior to render.
878
    foreach (module_implements('panels_pre_render') as $module) {
879
      $function = $module . '_panels_pre_render';
880
      $output .= $function($this, $renderer);
881
    }
882

    
883
    $output .= $renderer->render();
884

    
885
    // Let modules act just after render.
886
    foreach (module_implements('panels_post_render') as $module) {
887
      $function = $module . '_panels_post_render';
888
      $output .= $function($this, $renderer);
889
    }
890
    return $output;
891
  }
892

    
893
  /**
894
   * Determine if the given user can perform the requested operation.
895
   *
896
   * @param string $op
897
   *   An operation like: create, read, update, or delete.
898
   * @param object $account
899
   *   (optional) The account to check access for.
900
   *
901
   * @return bool
902
   *   TRUE if access is granted; otherwise FALSE.
903
   */
904
  public function access($op, $account = NULL) {
905
    global $user;
906

    
907
    if (!$account) {
908
      $account = $user;
909
    }
910

    
911
    // Even administrators need to go through the access system. However, to
912
    // support legacy plugins, user 1 gets full access no matter what.
913
    if ($account->uid == 1) {
914
      return TRUE;
915
    }
916

    
917
    if (!in_array($op, array('create', 'read', 'update', 'delete', 'change layout'))) {
918
      return FALSE;
919
    }
920

    
921
    if (empty($this->storage_type) || empty($this->storage_id)) {
922
      return FALSE;
923
    }
924

    
925
    if ($this->storage_type == 'unknown') {
926
      return FALSE;
927
    }
928

    
929
    $storage_plugin = panels_get_panels_storage_plugin($this->storage_type);
930
    if (!$storage_plugin) {
931
      return FALSE;
932
    }
933

    
934
    $access_callback = panels_plugin_get_function('panels_storage', $storage_plugin, 'access callback');
935
    if (!$access_callback) {
936
      return FALSE;
937
    }
938

    
939
    return $access_callback($this->storage_type, $this->storage_id, $op, $account);
940
  }
941

    
942
}
943

    
944
/**
945
 * End of 'defgroup mainapi', although other functions are specifically added later.
946
 */
947

    
948
/**
949
 * Creates a new display, setting the ID to our magic new id.
950
 */
951
function panels_new_display() {
952
  ctools_include('export');
953
  $display = ctools_export_new_object('panels_display', FALSE);
954
  $display->did = 'new';
955
  return $display;
956
}
957

    
958
/**
959
 * Create a new pane.
960
 *
961
 * @todo -- use schema API for some of this?
962
 */
963
function panels_new_pane($type, $subtype, $set_defaults = FALSE) {
964
  ctools_include('export');
965
  $pane = ctools_export_new_object('panels_pane', FALSE);
966
  $pane->pid = 'new';
967
  $pane->type = $type;
968
  $pane->subtype = $subtype;
969
  if ($set_defaults) {
970
    ctools_include('content');
971
    $content_type = ctools_get_content_type($type);
972
    $content_subtype = ctools_content_get_subtype($content_type, $subtype);
973
    $pane->configuration = ctools_content_get_defaults($content_type, $content_subtype);
974
  }
975
  drupal_alter('panels_new_pane', $pane);
976

    
977
  return $pane;
978
}
979

    
980
/**
981
 * Load and fill the requested $display object(s).
982
 *
983
 * Helper function primarily for for panels_load_display().
984
 *
985
 * @param array $dids
986
 *   An indexed array of dids to be loaded from the database.
987
 *
988
 * @return array
989
 *   An array of displays, keyed by their display dids.
990
 *
991
 * @todo schema API can drasticly simplify this code.
992
 */
993
function panels_load_displays($dids) {
994
  $displays = array();
995
  if (empty($dids) || !is_array($dids)) {
996
    return $displays;
997
  }
998

    
999
  $result = db_query(
1000
    "SELECT * FROM {panels_display} WHERE did IN (:dids)",
1001
    array(':dids' => $dids)
1002
  );
1003

    
1004
  ctools_include('export');
1005
  foreach ($result as $obj) {
1006
    $displays[$obj->did] = ctools_export_unpack_object('panels_display', $obj);
1007
    // Modify the hide_title field to go from a bool to an int if necessary.
1008
  }
1009

    
1010
  $result = db_query(
1011
    "SELECT * FROM {panels_pane} WHERE did IN (:dids) ORDER BY did, panel, position",
1012
    array(':dids' => $dids)
1013
  );
1014
  foreach ($result as $obj) {
1015
    $pane = ctools_export_unpack_object('panels_pane', $obj);
1016

    
1017
    $displays[$pane->did]->panels[$pane->panel][] = $pane->pid;
1018
    $displays[$pane->did]->content[$pane->pid] = $pane;
1019
  }
1020
  return $displays;
1021
}
1022

    
1023
/**
1024
 * Load a single display.
1025
 *
1026
 * @param int $did
1027
 *   The display id (did) of the display to be loaded.
1028
 *
1029
 * @return object $display
1030
 *   Returns a partially-loaded panels_display object. $display objects returned
1031
 *   from this function have only the following data:
1032
 *    - $display->did (the display id)
1033
 *    - $display->name (the 'name' of the display, where applicable - it often isn't)
1034
 *    - $display->layout (a string with the system name of the display's layout)
1035
 *    - $display->panel_settings (custom layout style settings contained in an associative array; NULL if none)
1036
 *    - $display->layout_settings (panel size and configuration settings for Flexible layouts; NULL if none)
1037
 *    - $display->css_id (the special css_id that has been assigned to this display, if any; NULL if none)
1038
 *    - $display->content (an array of pane objects, keyed by pane id (pid))
1039
 *    - $display->panels (an associative array of panel regions, each an indexed array of pids in the order they appear in that region)
1040
 *    - $display->cache (any relevant data from panels_simple_cache)
1041
 *    - $display->args
1042
 *    - $display->incoming_content
1043
 *   While all of these members are defined, $display->context is NEVER defined in the returned $display;
1044
 *   it must be set using one of the ctools_context_create() functions.
1045
 *
1046
 * @ingroup mainapi
1047
 */
1048
function panels_load_display($did) {
1049
  $displays = panels_load_displays(array($did));
1050
  if (!empty($displays)) {
1051
    return array_shift($displays);
1052
  }
1053
}
1054

    
1055
/**
1056
 * Save a display object.
1057
 *
1058
 * Note that a new $display only receives a real did once it is run through
1059
 * this function, and likewise for the pid of any new pane.
1060
 *
1061
 * Until then, a new display uses a string placeholder, 'new', in place of
1062
 * a real did, and a new pane (whether on a new $display or not) appends a
1063
 * universally-unique identifier (which is stored permanently in the 'uuid'
1064
 * field). This format is also used in place of the real pid for exports.
1065
 *
1066
 * @param object $display
1067
 *   The display object to be saved. Passed by reference so the caller need not
1068
 *   use the return value for any reason except convenience.
1069
 *
1070
 * @return object $display
1071
 *   This display panel display object to return.
1072
 *
1073
 * @ingroup mainapi
1074
 */
1075
function panels_save_display(&$display) {
1076
  $update = (isset($display->did) && is_numeric($display->did)) ? array('did') : array();
1077
  if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
1078
    $display->uuid = ctools_uuid_generate();
1079
  }
1080
  drupal_write_record('panels_display', $display, $update);
1081

    
1082
  $pids = array();
1083
  if ($update) {
1084
    // Get a list of all panes currently in the database for this display so we
1085
    // can know if there are panes that need to be deleted. (i.e, aren't
1086
    // currently in our list of panes).
1087
    $result = db_query(
1088
      "SELECT pid FROM {panels_pane} WHERE did = :did",
1089
      array(':did' => $display->did)
1090
    );
1091
    foreach ($result as $pane) {
1092
      $pids[$pane->pid] = $pane->pid;
1093
    }
1094
  }
1095

    
1096
  // Update all the panes.
1097
  ctools_include('plugins', 'panels');
1098
  ctools_include('content');
1099

    
1100
  foreach ($display->panels as $id => $panes) {
1101
    $position = 0;
1102
    $new_panes = array();
1103
    foreach ((array) $panes as $pid) {
1104
      if (!isset($display->content[$pid])) {
1105
        continue;
1106
      }
1107
      $pane = $display->content[$pid];
1108
      $type = ctools_get_content_type($pane->type);
1109

    
1110
      $pane->position = $position++;
1111
      $pane->did = $display->did;
1112

    
1113
      $old_pid = $pane->pid;
1114

    
1115
      if (empty($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
1116
        $pane->uuid = ctools_uuid_generate();
1117
      }
1118

    
1119
      drupal_write_record('panels_pane', $pane, is_numeric($pid) ? array('pid') : array());
1120

    
1121
      // Allow other modules to take action after a pane is saved.
1122
      if ($pane->pid == $old_pid) {
1123
        module_invoke_all('panels_pane_update', $pane);
1124
      }
1125
      else {
1126
        module_invoke_all('panels_pane_insert', $pane);
1127
      }
1128

    
1129
      if ($pane->pid != $old_pid) {
1130
        // Remove the old new-* entry from the displays content.
1131
        unset($display->content[$pid]);
1132

    
1133
        // Put it back so our pids and positions can be used.
1134
        $display->content[$pane->pid] = $pane;
1135

    
1136
        // If the title pane was one of our panes that just got its ID changed,
1137
        // we need to change it in the database, too.
1138
        if (isset($display->title_pane) && $display->title_pane == $old_pid) {
1139
          $display->title_pane = $pane->pid;
1140
          // Do a simple update query to write it so we don't have to rewrite
1141
          // the whole record. We can't just save writing the whole record here
1142
          // because it was needed to get the did. Chicken, egg, more chicken.
1143
          db_update('panels_display')
1144
            ->fields(array(
1145
              'title_pane' => $pane->pid,
1146
            ))
1147
            ->condition('did', $display->did)
1148
            ->execute();
1149
        }
1150
      }
1151

    
1152
      // re-add this to the list of content for this panel.
1153
      $new_panes[] = $pane->pid;
1154

    
1155
      // Remove this from the list of panes scheduled for deletion.
1156
      if (isset($pids[$pane->pid])) {
1157
        unset($pids[$pane->pid]);
1158
      }
1159
    }
1160

    
1161
    $display->panels[$id] = $new_panes;
1162
  }
1163
  if (!empty($pids)) {
1164
    // Allow other modules to take action before a panes are deleted.
1165
    module_invoke_all('panels_pane_delete', $pids);
1166
    db_delete('panels_pane')->condition('pid', $pids)->execute();
1167
  }
1168

    
1169
  // Clear any cached content for this display.
1170
  panels_clear_cached_content($display);
1171

    
1172
  // Allow other modules to take action when a display is saved.
1173
  module_invoke_all('panels_display_save', $display);
1174

    
1175
  // Log the change to watchdog, using the same style as node.module.
1176
  $watchdog_args = array('%did' => $display->did);
1177
  if (!empty($display->title)) {
1178
    $watchdog_args['%title'] = $display->title;
1179
    watchdog('content', 'Panels: saved display "%title" with display id %did', $watchdog_args, WATCHDOG_NOTICE);
1180
  }
1181
  else {
1182
    watchdog('content', 'Panels: saved display with id %did', $watchdog_args, WATCHDOG_NOTICE);
1183
  }
1184

    
1185
  // To be nice, even though we have a reference.
1186
  return $display;
1187
}
1188

    
1189
/**
1190
 * Delete a display.
1191
 */
1192
function panels_delete_display($display) {
1193
  if (is_object($display)) {
1194
    $did = $display->did;
1195
  }
1196
  else {
1197
    $did = $display;
1198
  }
1199
  module_invoke_all('panels_delete_display', $did);
1200
  db_delete('panels_display')->condition('did', $did)->execute();
1201
  db_delete('panels_pane')->condition('did', $did)->execute();
1202
}
1203

    
1204
/**
1205
 * Exports the provided display into portable code.
1206
 *
1207
 * This function is primarily intended as a mechanism for cloning displays.
1208
 * It generates an exact replica (in code) of the provided $display, with
1209
 * the exception that it replaces all ids (dids and pids) with place-holder
1210
 * values (consisting of the display or pane's uuid, with a 'new-' prefix).
1211
 *
1212
 * Only once panels_save_display() is called on the code version of $display
1213
 * will the exported display be written to the database and permanently saved.
1214
 *
1215
 * @param object $display
1216
 *   This export function does no loading of additional data about the provided
1217
 *   display. Consequently, the caller should make sure that all the desired
1218
 *   data has been loaded into the $display before calling this function.
1219
 * @param string $prefix
1220
 *   A string prefix that is prepended to each line of exported code. This is
1221
 *   primarily used for prepending a double space when exporting so that the
1222
 *   code indents and lines up nicely.
1223
 *
1224
 * @return string $output
1225
 *   The passed-in $display expressed as code, ready to be imported. Import by
1226
 *   running eval($output) in the caller function; doing so will create a new
1227
 *   $display variable with all the exported values. Note that if you have
1228
 *   already defined a $display variable in the same scope as where you eval(),
1229
 *   your existing $display variable WILL be overwritten.
1230
 *
1231
 * @see panels_page_export() or _panels_page_fetch_display() for samples.
1232
 *
1233
 * @ingroup mainapi
1234
 */
1235
function panels_export_display($display, $prefix = '') {
1236
  ctools_include('export');
1237
  if (empty($display->uuid) || !ctools_uuid_is_valid($display->uuid)) {
1238
    $display->uuid = ctools_uuid_generate();
1239
  }
1240
  $display->did = 'new-' . $display->uuid;
1241
  $output = ctools_export_object('panels_display', $display, $prefix);
1242

    
1243
  // Initialize empty properties.
1244
  $output .= $prefix . '$display->content = array()' . ";\n";
1245
  $output .= $prefix . '$display->panels = array()' . ";\n";
1246
  $panels = array();
1247

    
1248
  $title_pid = 0;
1249
  if (!empty($display->content)) {
1250
    $region_counters = array();
1251
    foreach ($display->content as $pane) {
1252

    
1253
      if (!isset($pane->uuid) || !ctools_uuid_is_valid($pane->uuid)) {
1254
        $pane->uuid = ctools_uuid_generate();
1255
      }
1256
      $pid = 'new-' . $pane->uuid;
1257

    
1258
      if ($pane->pid == $display->title_pane) {
1259
        $title_pid = $pid;
1260
      }
1261
      $pane->pid = $pid;
1262
      $output .= ctools_export_object('panels_pane', $pane, $prefix);
1263
      $output .= $prefix . '$display->content[\'' . $pane->pid . '\'] = $pane' . ";\n";
1264
      if (!isset($region_counters[$pane->panel])) {
1265
        $region_counters[$pane->panel] = 0;
1266
      }
1267
      $output .= $prefix . '$display->panels[\'' . $pane->panel . '\'][' . $region_counters[$pane->panel]++ . '] = \'' . $pane->pid . "';\n";
1268
    }
1269
  }
1270
  $output .= $prefix . '$display->hide_title = ';
1271
  switch ($display->hide_title) {
1272
    case PANELS_TITLE_FIXED:
1273
      $output .= 'PANELS_TITLE_FIXED';
1274
      break;
1275

    
1276
    case PANELS_TITLE_NONE:
1277
      $output .= 'PANELS_TITLE_NONE';
1278
      break;
1279

    
1280
    case PANELS_TITLE_PANE:
1281
      $output .= 'PANELS_TITLE_PANE';
1282
      break;
1283
  }
1284
  $output .= ";\n";
1285

    
1286
  $output .= $prefix . '$display->title_pane =' . " '$title_pid';\n";
1287
  return $output;
1288
}
1289

    
1290
/**
1291
 * Panels Render Display.
1292
 *
1293
 * Render a display by loading the content into an appropriate
1294
 * array and then passing through to panels_render_layout.
1295
 *
1296
 * if $incoming_content is NULL, default content will be applied. Use
1297
 * an empty string to indicate no content.
1298
 *
1299
 * @ingroup hook_invocations
1300
 */
1301
function panels_render_display(&$display, $renderer = NULL) {
1302
  ctools_include('plugins', 'panels');
1303
  ctools_include('context');
1304

    
1305
  if (!empty($display->context)) {
1306
    if ($form_context = ctools_context_get_form($display->context)) {
1307
      $form_context->form['#theme'] = 'panels_render_display_form';
1308
      if (empty($form_context->form['#theme_wrappers']) || !in_array('form', $form_context->form['#theme_wrappers'])) {
1309
        $form_context->form['#theme_wrappers'][] = 'form';
1310
      }
1311
      $form_context->form['#display'] = &$display;
1312
      return $form_context->form;
1313
    }
1314
  }
1315
  return $display->render($renderer);
1316
}
1317

    
1318
/**
1319
 * Theme function to render our panel as a form.
1320
 *
1321
 * When rendering a display as a form, the entire display needs to be
1322
 * inside the <form> tag so that the form can be spread across the
1323
 * panes. This sets up the form system to be the main caller and we
1324
 * then operate as a theme function of the form.
1325
 */
1326
function theme_panels_render_display_form($vars) {
1327
  return $vars['element']['#display']->render();
1328
}
1329

    
1330
/**
1331
 * Panels layout icon function.
1332
 */
1333
function panels_print_layout_icon($id, $layout, $title = NULL) {
1334
  ctools_add_css('panels_admin', 'panels');
1335
  $file = $layout['path'] . '/' . $layout['icon'];
1336
  return theme(
1337
    'panels_layout_icon',
1338
    array(
1339
      'id' => $id,
1340
      'image' => theme(
1341
        'image',
1342
        array(
1343
          'path' => $file,
1344
          'alt' => strip_tags($layout['title']),
1345
          'title' => strip_tags($layout['description']),
1346
        )
1347
      ),
1348
      'title' => $title,
1349
    )
1350
  );
1351
}
1352

    
1353
/**
1354
 * Theme the layout icon image.
1355
 *
1356
 * @todo move to theme.inc
1357
 */
1358
function theme_panels_layout_icon($vars) {
1359
  $id = $vars['id'];
1360
  $image = $vars['image'];
1361
  $title = $vars['title'];
1362

    
1363
  $output = '<div class="layout-icon">';
1364
  $output .= $image;
1365
  if ($title) {
1366
    $output .= '<div class="caption">' . $title . '</div>';
1367
  }
1368
  $output .= '</div>';
1369
  return $output;
1370
}
1371

    
1372
/**
1373
 * Theme the layout link image.
1374
 *
1375
 * @layout
1376
 *
1377
 * @todo Why isn't this a template at this point?
1378
 * @todo Why does this take 4 arguments but only makes use of two?
1379
 */
1380
function theme_panels_layout_link($vars) {
1381
  $options = !empty($vars['options']) ? $vars['options'] : array();
1382
  if (!isset($options['attributes']['class'])) {
1383
    $options['attributes']['class'] = array();
1384
  }
1385
  $options['attributes']['class'] = array_merge($options['attributes']['class'], $vars['class']);
1386

    
1387
  $content = $vars['image'] . '<span>' . $vars['title'] . '</span>';
1388
  return l($content, $vars['link'], $options + array('html' => TRUE));
1389
}
1390

    
1391
/**
1392
 * Print the layout link. Sends out to a theme function.
1393
 *
1394
 * @layout
1395
 */
1396
function panels_print_layout_link($id, $layout, $link, $options = array(), $current_layout = FALSE) {
1397
  if (isset($options['query']['q'])) {
1398
    unset($options['query']['q']);
1399
  }
1400

    
1401
  // Setup classes for layout link, including current-layout information.
1402
  $class = array('layout-link');
1403
  if ($current_layout == $id) {
1404
    $options['attributes']['class'][] = 'current-layout-link';
1405
    $class[] = 'current-layout';
1406
  }
1407

    
1408
  ctools_add_css('panels_admin', 'panels');
1409
  $file = $layout['path'] . '/' . $layout['icon'];
1410
  $image = theme('image', array('path' => $file));
1411
  return theme(
1412
    'panels_layout_link',
1413
    array(
1414
      'title' => $layout['title'],
1415
      'image' => $image,
1416
      'link' => $link,
1417
      'class' => $class,
1418
      'options' => $options,
1419
    )
1420
  );
1421
}
1422

    
1423

    
1424
/**
1425
 * Panels Get legacy state.
1426
 *
1427
 * Gateway to the PanelsLegacyState class/object, which does all legacy state
1428
 * checks and provides information about the cause of legacy states as needed.
1429
 *
1430
 * @return PanelsLegacyState $legacy
1431
 *   Returns a legacy panels state.
1432
 */
1433
function panels_get_legacy_state() {
1434
  static $legacy = NULL;
1435
  if (!isset($legacy)) {
1436
    ctools_include('legacy', 'panels');
1437
    $legacy = new PanelsLegacyState();
1438
  }
1439
  return $legacy;
1440
}
1441

    
1442
/**
1443
 * Get the display that is currently being rendered as a page.
1444
 *
1445
 * Unlike in previous versions of this, this only returns the display,
1446
 * not the page itself, because there are a number of different ways
1447
 * to get to this point. It is hoped that the page data isn't needed
1448
 * at this point. If it turns out there is, we will do something else to
1449
 * get that functionality.
1450
 */
1451
function panels_get_current_page_display($change = NULL) {
1452
  static $display = NULL;
1453
  if ($change) {
1454
    $display = $change;
1455
  }
1456

    
1457
  return $display;
1458
}
1459

    
1460
/**
1461
 * Clean up the panel pane variables for the template.
1462
 */
1463
function template_preprocess_panels_pane(&$vars) {
1464
  $content = &$vars['content'];
1465

    
1466
  $vars['contextual_links'] = array();
1467
  $vars['classes_array'] = array();
1468
  $vars['admin_links'] = '';
1469

    
1470
  if (module_exists('contextual') && user_access('access contextual links')) {
1471
    $links = array();
1472
    // These are specified by the content.
1473
    if (!empty($content->admin_links)) {
1474
      $links += $content->admin_links;
1475
    }
1476

    
1477
    // Take any that may have been in the render array we were given and
1478
    // move them up so they appear outside the pane properly.
1479
    if (is_array($content->content) && isset($content->content['#contextual_links'])) {
1480
      $element = array(
1481
        '#type' => 'contextual_links',
1482
        '#contextual_links' => $content->content['#contextual_links'],
1483
      );
1484
      unset($content->content['#contextual_links']);
1485

    
1486
      // Add content to $element array.
1487
      if (is_array($content->content)) {
1488
        $element['#element'] = $content->content;
1489
      }
1490

    
1491
      $element = contextual_pre_render_links($element);
1492
      if (!empty($element['#links'])) {
1493
        $links += $element['#links'];
1494
      }
1495
    }
1496

    
1497
    if ($links) {
1498
      $build = array(
1499
        '#prefix' => '<div class="contextual-links-wrapper">',
1500
        '#suffix' => '</div>',
1501
        '#theme' => 'links__contextual',
1502
        '#links' => $links,
1503
        '#attributes' => array('class' => array('contextual-links')),
1504
        '#attached' => array(
1505
          'library' => array(array('contextual', 'contextual-links')),
1506
        ),
1507
      );
1508
      $vars['classes_array'][] = 'contextual-links-region';
1509
      $vars['admin_links'] = drupal_render($build);
1510
    }
1511
  }
1512

    
1513
  // Basic classes.
1514
  $vars['classes_array'][] = 'panel-pane';
1515
  $vars['id'] = '';
1516

    
1517
  // Add some usable classes based on type/subtype.
1518
  ctools_include('cleanstring');
1519
  $type_class = $content->type ? 'pane-' . ctools_cleanstring($content->type, array('lower case' => TRUE)) : '';
1520
  $subtype_class = $content->subtype ? 'pane-' . ctools_cleanstring($content->subtype, array('lower case' => TRUE)) : '';
1521

    
1522
  // Sometimes type and subtype are the same. Avoid redundant classes.
1523
  $vars['classes_array'][] = $type_class;
1524
  if ($type_class != $subtype_class) {
1525
    $vars['classes_array'][] = $subtype_class;
1526
  }
1527

    
1528
  // Add id and custom class if sent in.
1529
  if (!empty($content->content)) {
1530
    if (!empty($content->css_id)) {
1531
      $vars['id'] = ' id="' . $content->css_id . '"';
1532
    }
1533
    if (!empty($content->css_class)) {
1534
      $vars['classes_array'][] = $content->css_class;
1535
    }
1536
  }
1537

    
1538
  // Set up some placeholders for constructing template file names.
1539
  $base = 'panels_pane';
1540
  $delimiter = '__';
1541

    
1542
  // Add template file suggestion for content type and sub-type.
1543
  $vars['theme_hook_suggestions'][] = $base . $delimiter . $content->type;
1544
  $vars['theme_hook_suggestions'][] = $base . $delimiter . strtr(ctools_cleanstring($content->type, array('lower case' => TRUE)), '-', '_') . $delimiter . strtr(ctools_cleanstring($content->subtype, array('lower case' => TRUE)), '-', '_');
1545

    
1546
  $vars['pane_prefix'] = !empty($content->pane_prefix) ? $content->pane_prefix : '';
1547
  $vars['pane_suffix'] = !empty($content->pane_suffix) ? $content->pane_suffix : '';
1548

    
1549
  $vars['title'] = !empty($content->title) ? $content->title : '';
1550
  $vars['title_heading'] = !empty($content->title_heading) ? $content->title_heading : variable_get('override_title_heading', 'h2');
1551
  $vars['title_attributes_array']['class'][] = 'pane-title';
1552

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

    
1555
  $vars['links'] = !empty($content->links) ? theme('links', array('links' => $content->links)) : '';
1556
  $vars['more'] = '';
1557
  if (!empty($content->more)) {
1558
    if (empty($content->more['title'])) {
1559
      $content->more['title'] = t('more');
1560
    }
1561
    $vars['more'] = l($content->more['title'], $content->more['href'], $content->more);
1562
  }
1563

    
1564
  if (!empty($content->attributes)) {
1565
    $vars['attributes_array'] = array_merge($vars['attributes_array'], $content->attributes);
1566
  }
1567

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

    
1570
}
1571

    
1572
/**
1573
 * Route Panels' AJAX calls to the correct object.
1574
 *
1575
 * Panels' AJAX is controlled mostly by renderer objects. This menu callback
1576
 * accepts the incoming request, figures out which object should handle the
1577
 * request, and attempts to route it. If no object can be found, the default
1578
 * Panels editor object is used.
1579
 *
1580
 * Calls are routed via the ajax_* method space. For example, if visiting
1581
 * panels/ajax/add-pane then $renderer::ajax_add_pane() will be called.
1582
 * This means commands can be added without having to create new callbacks.
1583
 *
1584
 * The first argument *must always* be the cache key so that a cache object
1585
 * can be passed through. Other arguments will be passed through untouched
1586
 * so that the method can do whatever it needs to do.
1587
 */
1588
function panels_ajax_router() {
1589
  $args = func_get_args();
1590
  if (count($args) < 3) {
1591
    return MENU_NOT_FOUND;
1592
  }
1593

    
1594
  ctools_include('display-edit', 'panels');
1595
  ctools_include('plugins', 'panels');
1596
  ctools_include('ajax');
1597
  ctools_include('modal');
1598
  ctools_include('context');
1599
  ctools_include('content');
1600

    
1601
  $plugin_name = array_shift($args);
1602
  $method = array_shift($args);
1603
  $cache_key = array_shift($args);
1604

    
1605
  $plugin = panels_get_display_renderer($plugin_name);
1606
  if (!$plugin) {
1607
    // This is the default renderer for handling AJAX commands.
1608
    $plugin = panels_get_display_renderer('editor');
1609
  }
1610

    
1611
  $cache = panels_edit_cache_get($cache_key);
1612
  if (empty($cache)) {
1613
    return MENU_ACCESS_DENIED;
1614
  }
1615

    
1616
  $renderer = panels_get_renderer_handler($plugin, $cache->display);
1617
  if (!$renderer) {
1618
    return MENU_ACCESS_DENIED;
1619
  }
1620

    
1621
  $method = 'ajax_' . str_replace('-', '_', $method);
1622
  if (!method_exists($renderer, $method)) {
1623
    return MENU_NOT_FOUND;
1624
  }
1625

    
1626
  $renderer->cache = &$cache;
1627
  ctools_include('cleanstring');
1628
  $renderer->clean_key = ctools_cleanstring($cache_key);
1629

    
1630
  $op = $renderer->get_panels_storage_op_for_ajax($method);
1631
  if (!$cache->display->access($op)) {
1632
    return MENU_ACCESS_DENIED;
1633
  }
1634

    
1635
  $output = call_user_func_array(array($renderer, $method), $args);
1636

    
1637
  if (empty($output) && !empty($renderer->commands)) {
1638
    return array(
1639
      '#type' => 'ajax',
1640
      '#commands' => $renderer->commands,
1641
    );
1642
  }
1643
  else {
1644
    return $output;
1645
  }
1646
}
1647

    
1648
/**
1649
 * Panels caching functions and callbacks.
1650
 *
1651
 * When editing displays and the like, Panels has a caching system that relies
1652
 * on a callback to determine where to get the actual cache.
1653
 *
1654
 * @todo This system needs to be better documented so that it can be better used.
1655
 */
1656

    
1657
/**
1658
 * Get an object from cache.
1659
 */
1660
function panels_cache_get($obj, $did, $skip_cache = FALSE) {
1661
  ctools_include('object-cache');
1662
  // We often store contexts in cache, so let's just make sure we can load them.
1663
  ctools_include('context');
1664
  return ctools_object_cache_get($obj, 'panels_display:' . $did, $skip_cache);
1665
}
1666

    
1667
/**
1668
 * Save the edited object into the cache.
1669
 */
1670
function panels_cache_set($obj, $did, $cache) {
1671
  ctools_include('object-cache');
1672
  return ctools_object_cache_set($obj, 'panels_display:' . $did, $cache);
1673
}
1674

    
1675
/**
1676
 * Clear a object from the cache; used if the editing is aborted.
1677
 */
1678
function panels_cache_clear($obj, $did) {
1679
  ctools_include('object-cache');
1680
  return ctools_object_cache_clear($obj, 'panels_display:' . $did);
1681
}
1682

    
1683
/**
1684
 * Create the default cache for editing panel displays.
1685
 *
1686
 * If an application is using the Panels display editor without having
1687
 * specified a cache key, this method can be used to create the default
1688
 * cache.
1689
 */
1690
function panels_edit_cache_get_default(&$display, $content_types = NULL, $title = FALSE) {
1691
  if (empty($content_types)) {
1692
    $content_types = ctools_content_get_available_types();
1693
  }
1694

    
1695
  $display->cache_key = $display->did;
1696
  panels_cache_clear('display', $display->did);
1697

    
1698
  $cache = new stdClass();
1699
  $cache->display = &$display;
1700
  $cache->content_types = $content_types;
1701
  $cache->display_title = $title;
1702

    
1703
  panels_edit_cache_set($cache);
1704
  return $cache;
1705
}
1706

    
1707
/**
1708
 * Panels Editor Cache Get.
1709
 *
1710
 * Method to allow modules to provide their own caching mechanism for the
1711
 * display editor.
1712
 */
1713
function panels_edit_cache_get($cache_key) {
1714
  if (strpos($cache_key, ':') !== FALSE) {
1715
    list($module, $argument) = explode(':', $cache_key, 2);
1716
    return module_invoke($module, 'panels_cache_get', $argument);
1717
  }
1718

    
1719
  // Fall back to our normal method.
1720
  return panels_cache_get('display', $cache_key);
1721
}
1722

    
1723
/**
1724
 * Panels Editor Cache Set.
1725
 *
1726
 * Method to allow modules to provide their own caching mechanism for the
1727
 * display editor.
1728
 */
1729
function panels_edit_cache_set($cache) {
1730
  $cache_key = $cache->display->cache_key;
1731
  if (strpos($cache_key, ':') !== FALSE) {
1732
    list($module, $argument) = explode(':', $cache_key, 2);
1733
    return module_invoke($module, 'panels_cache_set', $argument, $cache);
1734
  }
1735

    
1736
  // Fall back to our normal method.
1737
  return panels_cache_set('display', $cache_key, $cache);
1738
}
1739

    
1740
/**
1741
 * Panels Editor Cache Save.
1742
 *
1743
 * Method to allow modules to provide their own mechanism to write the
1744
 * cache used in the display editor.
1745
 */
1746
function panels_edit_cache_save($cache) {
1747
  $cache_key = $cache->display->cache_key;
1748
  if (strpos($cache_key, ':') !== FALSE) {
1749
    list($module, $argument) = explode(':', $cache_key, 2);
1750
    if (function_exists($module . '_panels_cache_save')) {
1751
      return module_invoke($module, 'panels_cache_save', $argument, $cache);
1752
    }
1753
  }
1754

    
1755
  // Fall back to our normal method.
1756
  return panels_save_display($cache->display);
1757
}
1758

    
1759
/**
1760
 * Panels Editor Cache Clear.
1761
 *
1762
 * Method to allow modules to provide their own mechanism to clear the
1763
 * cache used in the display editor.
1764
 */
1765
function panels_edit_cache_clear($cache) {
1766
  $cache_key = $cache->display->cache_key;
1767
  if (strpos($cache_key, ':') !== FALSE) {
1768
    list($module, $argument) = explode(':', $cache_key, 2);
1769
    if (function_exists($module . '_panels_cache_clear')) {
1770
      return module_invoke($module, 'panels_cache_clear', $argument, $cache);
1771
    }
1772
  }
1773

    
1774
  // Fall back to our normal method.
1775
  return panels_cache_clear('display', $cache_key);
1776
}
1777

    
1778
/**
1779
 * Method to allow modules to provide a mechanism to break locks.
1780
 */
1781
function panels_edit_cache_break_lock($cache) {
1782
  if (empty($cache->locked)) {
1783
    return;
1784
  }
1785

    
1786
  $cache_key = $cache->display->cache_key;
1787
  if (strpos($cache_key, ':') !== FALSE) {
1788
    list($module, $argument) = explode(':', $cache_key, 2);
1789
    if (function_exists($module . '_panels_cache_break_lock')) {
1790
      return module_invoke($module, 'panels_cache_break_lock', $argument, $cache);
1791
    }
1792
  }
1793

    
1794
  // Normal panel display editing has no locks, so we do nothing if there is
1795
  // no fallback.
1796
}
1797

    
1798
/**
1799
 * Callbacks on behalf of the panel_context plugin.
1800
 *
1801
 * The panel_context plugin lets Panels be used in page manager. These
1802
 * callbacks allow the display editing system to use the page manager
1803
 * cache rather than the default display cache. They are routed by the cache
1804
 * key via panels_edit_cache_* functions.
1805
 */
1806

    
1807
/**
1808
 * Get display edit cache on behalf of panel context.
1809
 *
1810
 * The key is the second half of the key in this form:
1811
 * panel_context:TASK_NAME::HANDLER_NAME::args::url;
1812
 */
1813
function panel_context_panels_cache_get($key) {
1814
  ctools_include('common', 'panels');
1815
  ctools_include('context');
1816
  ctools_include('context-task-handler');
1817
  // This loads the panel context inc even if we don't use the plugin.
1818
  $plugin = page_manager_get_task_handler('panel_context');
1819

    
1820
  list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
1821
  $page = page_manager_get_page_cache($task_name);
1822
  if (isset($page->display_cache[$handler_name])) {
1823
    return $page->display_cache[$handler_name];
1824
  }
1825

    
1826
  if ($handler_name) {
1827
    $handler = &$page->handlers[$handler_name];
1828
  }
1829
  else {
1830
    $handler = &$page->new_handler;
1831
  }
1832
  $cache = new stdClass();
1833

    
1834
  $task = page_manager_get_task($page->task_id);
1835
  $arguments = array();
1836
  if ($args) {
1837
    $arguments = explode('\\', $args);
1838
    $contexts = ctools_context_handler_get_task_contexts($task, $page->subtask, $arguments);
1839
    $contexts = ctools_context_handler_get_handler_contexts($contexts, $handler);
1840
  }
1841
  else {
1842
    $contexts = ctools_context_handler_get_all_contexts($page->task, $page->subtask, $handler);
1843
  }
1844

    
1845
  $cache->display = &panels_panel_context_get_display($handler);
1846
  $cache->display->context = $contexts;
1847
  $cache->display->cache_key = 'panel_context:' . $key;
1848
  $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
1849
  $cache->display_title = TRUE;
1850
  $cache->locked = $page->locked;
1851

    
1852
  return $cache;
1853
}
1854

    
1855
/**
1856
 * Get the Page Manager cache for the panel_context plugin.
1857
 */
1858
function _panel_context_panels_cache_get_page_cache($key, $cache) {
1859
  list($task_name, $handler_name, $args, $q) = explode('::', $key, 4);
1860
  $page = page_manager_get_page_cache($task_name);
1861
  $page->display_cache[$handler_name] = $cache;
1862
  if ($handler_name) {
1863
    $page->handlers[$handler_name]->conf['display'] = $cache->display;
1864
    $page->handler_info[$handler_name]['changed'] |= PAGE_MANAGER_CHANGED_CACHED;
1865
  }
1866
  else {
1867
    $page->new_handler->conf['display'] = $cache->display;
1868
  }
1869

    
1870
  return $page;
1871
}
1872

    
1873
/**
1874
 * Store a display edit in progress in the page cache.
1875
 */
1876
function panel_context_panels_cache_set($key, $cache) {
1877
  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
1878
  page_manager_set_page_cache($page);
1879
}
1880

    
1881
/**
1882
 * Save all changes made to a display using the Page Manager page cache.
1883
 */
1884
function panel_context_panels_cache_clear($key, $cache) {
1885
  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
1886
  page_manager_clear_page_cache($page->task_name);
1887
}
1888

    
1889
/**
1890
 * Save all changes made to a display using the Page Manager page cache.
1891
 */
1892
function panel_context_panels_cache_save($key, $cache) {
1893
  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
1894
  page_manager_save_page_cache($page);
1895
}
1896

    
1897
/**
1898
 * Break the lock on a page manager page.
1899
 */
1900
function panel_context_panels_cache_break_lock($key, $cache) {
1901
  $page = _panel_context_panels_cache_get_page_cache($key, $cache);
1902
  ctools_object_cache_clear_all('page_manager_page', $page->task_name);
1903
}
1904

    
1905
/**
1906
 * Callbacks on behalf of the panels page wizards.
1907
 *
1908
 * The page wizards are a pluggable set of 'wizards' to make it easy to create
1909
 * specific types of pages based upon whatever someone felt like putting
1910
 * together. Since they will very often have content editing, we provide
1911
 * a generic mechanism to allow them to store their editing cache in the
1912
 * wizard cache.
1913
 *
1914
 * For them to use this mechanism, they just need to use:
1915
 * $cache = panels_edit_cache_get('panels_page_wizard:' . $plugin['name']);.
1916
 */
1917

    
1918
/**
1919
 * Get display edit cache for the panels mini export UI.
1920
 *
1921
 * The key is the second half of the key in this form:
1922
 * panels_page_wizard:TASK_NAME:HANDLER_NAME;
1923
 */
1924
function panels_page_wizard_panels_cache_get($key) {
1925
  ctools_include('page-wizard');
1926
  ctools_include('context');
1927
  $wizard_cache = page_manager_get_wizard_cache($key);
1928
  if (isset($wizard_cache->display_cache)) {
1929
    return $wizard_cache->display_cache;
1930
  }
1931

    
1932
  ctools_include('common', 'panels');
1933
  $cache = new stdClass();
1934
  $cache->display = $wizard_cache->display;
1935
  $cache->display->context = !empty($wizard_cache->context) ? $wizard_cache->context : array();
1936
  $cache->display->cache_key = 'panels_page_wizard:' . $key;
1937
  $cache->content_types = panels_common_get_allowed_types('panels_page', $cache->display->context);
1938
  $cache->display_title = TRUE;
1939

    
1940
  return $cache;
1941
}
1942

    
1943
/**
1944
 * Store a display edit in progress in the page cache.
1945
 */
1946
function panels_page_wizard_panels_cache_set($key, $cache) {
1947
  ctools_include('page-wizard');
1948
  $wizard_cache = page_manager_get_wizard_cache($key);
1949
  $wizard_cache->display_cache = $cache;
1950
  page_manager_set_wizard_cache($wizard_cache);
1951
}
1952

    
1953
/**
1954
 * Implements hook_default_page_manager_handlers_alter().
1955
 *
1956
 * If a default Panels display has no storage type, set it.
1957
 */
1958
function panels_default_page_manager_handlers_alter(&$handlers) {
1959
  foreach ($handlers as &$handler) {
1960
    if ($handler->handler == 'panel_context') {
1961
      $display =& $handler->conf['display'];
1962
      if (empty($display->storage_type)) {
1963
        $display->storage_type = 'page_manager';
1964
        $display->storage_id = $handler->name;
1965
      }
1966
    }
1967
  }
1968
}
1969

    
1970
/**
1971
 * Implements hook_default_page_manager_pages_alter().
1972
 */
1973
function panels_default_page_manager_pages_alter(&$pages) {
1974
  foreach ($pages as &$page) {
1975
    panels_default_page_manager_handlers_alter($page->default_handlers);
1976
  }
1977
}
1978

    
1979
/**
1980
 * General utility functions.
1981
 */
1982

    
1983
/**
1984
 * Perform a drupal_goto on a destination that may be an array like url().
1985
 */
1986
function panels_goto($destination) {
1987
  if (!is_array($destination)) {
1988
    return drupal_goto($destination);
1989
  }
1990
  else {
1991
    // Prevent notices by adding defaults.
1992
    $destination += array(
1993
      'query' => NULL,
1994
      'fragment' => NULL,
1995
      'http_response_code' => NULL,
1996
    );
1997

    
1998
    return drupal_goto(
1999
      $destination['path'],
2000
      $destination['query'],
2001
      $destination['fragment'],
2002
      $destination['http_response_code']
2003
    );
2004
  }
2005
}
2006

    
2007

    
2008
/**
2009
 * For external use: Given a layout ID and $content array, return panel display.
2010
 *
2011
 * The content array is filled in based upon the content available in the
2012
 * layout. If it's a two column with a content array defined like.
2013
 * @code
2014
 *   array(
2015
 *    'left' => t('Left side'),
2016
 *    'right' => t('Right side')
2017
 *  ),
2018
 *
2019
 * Then the $content array should be
2020
 * @code
2021
 * array(
2022
 *   'left' => $output_left,
2023
 *   'right' => $output_right,
2024
 * )
2025
 *
2026
 * The output within each panel region can be either a single rendered
2027
 * HTML string or an array of rendered HTML strings as though they were
2028
 * panes. They will simply be concatenated together without separators.
2029
 */
2030
function panels_print_layout($layout, $content, $meta = 'standard') {
2031
  ctools_include('plugins', 'panels');
2032

    
2033
  // Create a temporary display for this.
2034
  $display = panels_new_display();
2035
  $display->layout = is_array($layout) ? $layout['name'] : $layout;
2036
  $display->content = $content;
2037

    
2038
  // Get our simple renderer.
2039
  $renderer = panels_get_renderer_handler('simple', $display);
2040
  $renderer->meta_location = $meta;
2041

    
2042
  return $renderer->render();
2043
}
2044

    
2045
/**
2046
 * Filter callback for array_filter to remove builders from a list of layouts.
2047
 */
2048
function _panels_builder_filter($layout) {
2049
  return empty($layout['builder']);
2050
}
2051

    
2052
/**
2053
 * Implements hook_get_pane_links_alter().
2054
 */
2055
function panels_get_pane_links_alter(&$links, $pane, $content_type) {
2056
  // Add links to the Panels pane dropdown menu.
2057
  if ($pane->type === "block") {
2058
    $prefixed_name = $pane->subtype;
2059

    
2060
    // Breakup the subtype string into parts.
2061
    $exploded_subtype = explode('-', $pane->subtype);
2062

    
2063
    // Get the first part of the string.
2064
    $subtype_prefix = $exploded_subtype[0];
2065

    
2066
    // Get the first part of the string and add a hyphen.
2067
    $subtype_prefix_hyphen = $exploded_subtype[0] . '-';
2068

    
2069
    // Remove the prefix block- to get the name.
2070
    $name_of_block = preg_replace("/^$subtype_prefix_hyphen/", '', $prefixed_name, 1);
2071

    
2072
    // Check for user added menus created at /admin/structure/menu/add
2073
    // menus of that type have a subtype that is prefixed with menu-menu-.
2074
    if (substr($prefixed_name, 0, 10) === "menu-menu-") {
2075
      // Remove the first prefix menu- from menu-menu- to get the name.
2076
      $name_of_block = substr($prefixed_name, 5);
2077

    
2078
      $links['top'][] = array(
2079
        'title' => t('Edit block'),
2080
        'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
2081
        'attributes' => array('target' => array('_blank')),
2082
      );
2083

    
2084
      $links['top'][] = array(
2085
        'title' => t('Edit menu links'),
2086
        'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
2087
        'attributes' => array('target' => array('_blank')),
2088
      );
2089
    }
2090

    
2091
    // Check for module provided menu blocks like Devels or Features
2092
    // menus of that type have a subtype that is prefixed with menu-.
2093
    elseif (substr($prefixed_name, 0, 5) === "menu-") {
2094
      // Remove the first prefix menu- to get the name.
2095
      $name_of_block = substr($prefixed_name, 5);
2096

    
2097
      $links['top'][] = array(
2098
        'title' => t('Edit block'),
2099
        'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
2100
        'attributes' => array('target' => array('_blank')),
2101
      );
2102

    
2103
      $links['top'][] = array(
2104
        'title' => t('Edit menu links'),
2105
        'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
2106
        'attributes' => array('target' => array('_blank')),
2107
      );
2108
    }
2109

    
2110
    // Check for system blocks with menu links.
2111
    elseif (substr($prefixed_name, 0, 7) === "system-") {
2112
      // Remove the first prefix system- to get the name.
2113
      $name_of_block = substr($prefixed_name, 7);
2114

    
2115
      $names_of_system_menus = menu_list_system_menus();
2116

    
2117
      $links['top'][] = array(
2118
        'title' => t('Edit block'),
2119
        'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
2120
        'attributes' => array('target' => array('_blank')),
2121
      );
2122

    
2123
      if (array_key_exists($name_of_block, $names_of_system_menus)) {
2124
        $links['top'][] = array(
2125
          'title' => t('Edit menu links'),
2126
          'href' => url('admin/structure/menu/manage/' . $name_of_block, array('absolute' => TRUE)),
2127
          'attributes' => array('target' => array('_blank')),
2128
        );
2129
      }
2130
    }
2131

    
2132
    // For all other blocks without menus.
2133
    else {
2134
      $links['top'][] = array(
2135
        'title' => t('Edit block'),
2136
        'href' => url('admin/structure/block/manage/' . $subtype_prefix . '/' . $name_of_block . '/configure', array('absolute' => TRUE)),
2137
        'attributes' => array('target' => array('_blank')),
2138
      );
2139
    }
2140
  }
2141
}
2142

    
2143
/**
2144
 * Deprecated functions.
2145
 *
2146
 * Everything below this line will eventually go away.
2147
 */
2148

    
2149
/**
2150
 * Panels path helper function.
2151
 */
2152
function panels_get_path($file, $base_path = FALSE, $module = 'panels') {
2153
  $output = $base_path ? base_path() : '';
2154
  return $output . drupal_get_path('module', $module) . '/' . $file;
2155
}
2156

    
2157
/**
2158
 * Remove default sidebar related body classes and provide own css classes.
2159
 */
2160
function panels_preprocess_html(&$vars) {
2161
  $panel_body_css = &drupal_static('panel_body_css');
2162
  if (!empty($panel_body_css['body_classes_to_remove'])) {
2163
    $classes_to_remove = array_filter(explode(' ', $panel_body_css['body_classes_to_remove']), 'strlen');
2164
    foreach ($vars['classes_array'] as $key => $css_class) {
2165
      if (in_array($css_class, $classes_to_remove)) {
2166
        unset($vars['classes_array'][$key]);
2167
      }
2168
    }
2169
  }
2170
  if (!empty($panel_body_css['body_classes_to_add'])) {
2171
    $vars['classes_array'][] = check_plain($panel_body_css['body_classes_to_add']);
2172
  }
2173
}