Projet

Général

Profil

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

root / drupal7 / sites / all / modules / panelizer / plugins / entity / PanelizerEntityDefault.class.php @ 76df55b7

1
<?php
2
/**
3
 * @file
4
 * Base class for the Panelizer Entity plugin.
5
 */
6

    
7
/**
8
 * Interface to describe how PanelizerEntity plugin objects are implemented.
9
 */
10
interface PanelizerEntityInterface {
11
  /**
12
   * Initialize the plugin object.
13
   */
14
  public function init($plugin);
15

    
16
  // Public Drupal hooks
17
  public function hook_menu(&$items);
18
  public function hook_menu_alter(&$items);
19
  public function hook_form_alter(&$form, &$form_state, $form_id);
20
  public function hook_permission(&$items);
21
  public function hook_admin_paths(&$items);
22
  public function hook_views_data_alter(&$data);
23

    
24
  // Entity specific Drupal hooks
25
  public function hook_entity_load(&$entities);
26
  public function hook_entity_insert($entity);
27
  public function hook_entity_update($entity);
28
  public function hook_entity_delete($entity);
29
  public function hook_field_attach_delete_revision($entity);
30

    
31
  /**
32
   * Add entity specific form to the Panelizer settings form.
33
   *
34
   * This is primarily to allow bundle selection per entity type.
35
   */
36
  public function settings_form(&$form, &$form_state);
37

    
38
  /**
39
   * Validate entity specific settings on the Panelizer settings form.
40
   */
41
  public function settings_form_validate(&$form, &$form_state);
42

    
43
  /**
44
   * Submit entity specific settings on the Panelizer settings form.
45
   */
46
  public function settings_form_submit(&$form, &$form_state);
47

    
48
  /**
49
   * Load the named default panel for the bundle.
50
   */
51
  public function get_default_panelizer_object($bundle, $name);
52

    
53
  /**
54
   * Determine if the current user has access to the $panelizer.
55
   */
56
  public function access_default_panelizer_object($panelizer);
57

    
58
  /**
59
   * Determine if a bundle is panelized
60
   */
61
  public function is_panelized($bundle);
62

    
63
  /**
64
   * Determine if a bundle has a defalt panel
65
   */
66
  public function has_default_panel($bundle);
67

    
68
  /**
69
   * Determine if a bundle is allowed choices.
70
   */
71
  public function has_panel_choice($bundle);
72

    
73
  /**
74
   * Get a default display for a newly panelized entity.
75
   *
76
   * This is meant to give administrators a starting point when panelizing
77
   * new entities.
78
   */
79
  function get_default_display($bundle, $view_mode);
80

    
81
  /**
82
   * Get a panelizer object for the key.
83
   *
84
   * This must be implemented for each entity type, as the default object
85
   * implements a special case for handling panelizer defaults.
86
   */
87
   // @todo this seems to be unused now.
88
//  function get_panelizer_object($key);
89

    
90
  /**
91
   * Render a panelized entity.
92
   */
93
  function render_entity($entity, $view_mode, $langcode = NULL, $args = array(), $address = NULL);
94

    
95
  /**
96
   * Fetch an object array of CTools contexts from panelizer information.
97
   */
98
  public function get_contexts($panelizer, $entity = NULL);
99

    
100
  /**
101
   * Callback to get the base context for a panelized entity
102
   */
103
  public function get_base_contexts($entity = NULL);
104

    
105
  /**
106
   * Determine if the current user has $op access on the $entity.
107
   */
108
  public function entity_access($op, $entity);
109

    
110
  /**
111
   * Implement the save function for the entity.
112
   */
113
  public function entity_save($entity);
114

    
115
  /**
116
   * Determine if an entity allows revisions and whether or not the current
117
   * user has access to control that.
118
   *
119
   * @param $entity
120
   *   The entity in question.
121
   * @return
122
   *   An array. The first parameter is a boolean as to whether or not the
123
   *   entity supports revisions and the second parameter is whether or not
124
   *   the user can control whether or not a revision is created.
125
   */
126
  public function entity_allows_revisions($entity);
127

    
128
  /**
129
   * Get the visible identifier of the identity.
130
   *
131
   * This is overridable because it can be a bit awkward using the
132
   * default label.
133
   *
134
   * @return
135
   *   A translated, safe string.
136
   */
137
  public function entity_identifier($entity);
138

    
139
  /**
140
   * Get the name of bundles on the entity.
141
   *
142
   * Entity API doesn't give us a way to determine this, so the class must
143
   * do this.
144
   *
145
   * @return
146
   *   A translated, safe string.
147
   */
148
  public function entity_bundle_label();
149

    
150
  /**
151
   * Fetch the entity out of a build for hook_entity_view.
152
   *
153
   * @param $build
154
   *   The render array that contains the entity.
155
   */
156
  public function get_entity_view_entity($build);
157
}
158

    
159
/**
160
 * Base class for the Panelizer Entity plugin.
161
 */
162
abstract class PanelizerEntityDefault implements PanelizerEntityInterface {
163
  /**
164
   * Where in the entity admin UI we should add Panelizer tabs with bundles.
165
   */
166
  public $entity_admin_root = NULL;
167

    
168
  /**
169
   * True if the entity supports revisions.
170
   */
171
  public $supports_revisions = FALSE;
172

    
173
  /**
174
   * The base table in SQL the entity uses, for views support.
175
   */
176
  public $views_table = '';
177

    
178
  /**
179
   * The plugin metadata.
180
   */
181
  public $plugin = NULL;
182

    
183
  /**
184
   * The entity type the plugin is for. This is from the $plugin array.
185
   */
186
  public $entity_type = '';
187

    
188
  /**
189
   * Initialize the plugin and store the plugin info.
190
   */
191
  function init($plugin) {
192
    $this->plugin = $plugin;
193
    $this->entity_type = $plugin['name'];
194
  }
195

    
196
  /**
197
   * Implements a delegated hook_permission.
198
   */
199
  public function hook_permission(&$items) {
200
    $entity_info = entity_get_info($this->entity_type);
201
    // Make a permission for each bundle we control.
202
    foreach ($this->plugin['bundles'] as $bundle => $settings) {
203
      // This is before the if because it shows up regardless of whether
204
      // or not a type is panelized.
205
      $items["administer panelizer $this->entity_type $bundle defaults"] = array(
206
        'title' => t('%entity_name %bundle_name: Administer Panelizer default panels, allowed content and settings.', array(
207
          '%entity_name' => $entity_info['label'],
208
          '%bundle_name' => $entity_info['bundles'][$bundle]['label'],
209
        )),
210
        'description' => t('Users with this permission can fully administer panelizer for this entity bundle.'),
211
      );
212

    
213
      if (empty($settings['status'])) {
214
        continue;
215
      }
216

    
217
      $items["administer panelizer $this->entity_type $bundle overview"] = array(
218
        'title' => t('%entity_name %bundle_name: Administer Panelizer overview', array(
219
          '%entity_name' => $entity_info['label'],
220
          '%bundle_name' => $entity_info['bundles'][$bundle]['label'],
221
        )),
222
        'description' => t('Allow access to the panelizer overview page for the entity type/bundle. Note: This permission will be required for panelizer tabs to appear on an entity.'),
223
      );
224
      foreach (panelizer_operations() as $path => $operation) {
225
        $items["administer panelizer $this->entity_type $bundle $path"] = array(
226
          'title' => t('%entity_name %bundle_name: Administer Panelizer @operation', array(
227
            '%entity_name' => $entity_info['label'],
228
            '%bundle_name' => $entity_info['bundles'][$bundle]['label'],
229
            '@operation' => $operation['link title'],
230
          )),
231
        );
232
      }
233

    
234
      if (!empty($settings['choice'])) {
235
        $items["administer panelizer $this->entity_type $bundle choice"] = array(
236
          'title' => t('%entity_name %bundle_name: Choose panels', array(
237
            '%entity_name' => $entity_info['label'],
238
            '%bundle_name' => $entity_info['bundles'][$bundle]['label'],
239
          )),
240
          'description' => t('Allows the user to choose which default panel the entity uses.'),
241
        );
242
      }
243
    }
244
  }
245

    
246
  /**
247
   * Implements a delegated hook_menu.
248
   */
249
  public function hook_menu(&$items) {
250
    if (!empty($this->plugin['entity path'])) {
251
      // Figure out where in the path the entity will be.
252
      $bits = explode('/', $this->plugin['entity path']);
253
      foreach ($bits as $count => $bit) {
254
        if (strpos($bit, '%') === 0) {
255
          $position = $count;
256
          break;
257
        }
258
      }
259

    
260
      if (!isset($position)) {
261
        return;
262
      }
263

    
264
      $total = count($bits);
265

    
266
      // Configure entity editing pages
267
      $base = array(
268
        'access callback' => 'panelizer_entity_plugin_callback_switcher',
269
        'access arguments' => array($this->entity_type, 'access', 'admin', $position, 'overview'),
270
        'type' => MENU_LOCAL_TASK,
271
      );
272

    
273
      $items[$this->plugin['entity path'] . '/panelizer'] = array(
274
        'title' => 'Panelizer',
275
        // make sure this is accessible to panelize entities with no defaults.
276
        'page callback' => 'panelizer_entity_plugin_switcher_page',
277
        'page arguments' => array($this->entity_type, 'overview', $position),
278
        'weight' => 11,
279
        'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
280
      ) + $base;
281

    
282
      $items[$this->plugin['entity path'] . '/panelizer/overview'] = array(
283
        'title' => 'Overview',
284
        'page callback' => 'panelizer_entity_plugin_switcher_page',
285
        'page arguments' => array($this->entity_type, 'overview', $position),
286
        'type' => MENU_DEFAULT_LOCAL_TASK,
287
        'weight' => 11,
288
      ) + $base;
289

    
290
      // Put in all of our view mode based paths.
291
      $weight = 0;
292
      foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
293
        $items[$this->plugin['entity path'] . "/panelizer/$view_mode"] = array(
294
          'title' => $view_mode_info['label'],
295
          'page callback' => 'panelizer_entity_plugin_switcher_page',
296
          'page arguments' => array($this->entity_type, 'settings', $position, $view_mode),
297
          'access arguments' => array($this->entity_type, 'access', 'admin', $position, 'settings', $view_mode),
298
          'weight' => $weight++,
299
        ) + $base;
300

    
301
        foreach (panelizer_operations() as $path => $operation) {
302
          $items[$this->plugin['entity path'] . '/panelizer/' . $view_mode . '/' . $path] = array(
303
            'title' => $operation['menu title'],
304
            'page callback' => 'panelizer_entity_plugin_switcher_page',
305
            'page arguments' => array($this->entity_type, $path, $position, $view_mode),
306
            'access arguments' => array($this->entity_type, 'access', 'admin', $position, $path, $view_mode),
307
            'weight' => $weight++,
308
          ) + $base;
309
          if (isset($operation['file'])) {
310
            $items[$this->plugin['entity path'] . '/panelizer/' . $view_mode . '/' . $path]['file'] = $operation['file'];
311
          }
312
          if (isset($operation['file path'])) {
313
            $items[$this->plugin['entity path'] . '/panelizer/' . $view_mode . '/' . $path]['file path'] = $operation['file path'];
314
          }
315
        }
316
        // Add our special reset item:
317
        $items[$this->plugin['entity path'] . '/panelizer/' . $view_mode . '/reset'] = array(
318
          'title' => t('Reset to Defaults'),
319
          'page callback' => 'panelizer_entity_plugin_switcher_page',
320
          'page arguments' => array($this->entity_type, 'reset', $position, $view_mode),
321
          'type' => MENU_CALLBACK,
322
        ) + $base;
323
      }
324
    }
325

    
326
    // Also add administrative links to the bundle.
327
    if (!empty($this->entity_admin_root)) {
328
      $this->add_admin_links($this->entity_admin_root, $this->entity_admin_bundle, $items);
329
    }
330
  }
331

    
332
  /**
333
   * Helper function to add administrative menu items into an entity's already existing structure.
334
   *
335
   * While this very closely follows the administrative items placed into the
336
   * menu in admin.inc, it is a little bit different because of how bundles
337
   * are placed into the URL. So the code is close but not QUITE reusable
338
   * without going through some hoops.
339
   *
340
   * @param $root
341
   *   The root path. This will be something like 'admin/structure/types/manage/%'.
342
   *   Everything will be placed at $root/panelizer/*.
343
   * @param $bundle
344
   *   This is either the numeric position of the bundle or, for entity types
345
   *   that do not support bundles, a hard coded bundle string.
346
   * @param &$items
347
   *   The array of menu items this is being added to.
348
   */
349
  public function add_admin_links($root, $bundle, &$items) {
350
    // Node $root = 'admin/structure/types/manage/%
351
    // Taxonomy $root = 'admin/structure/taxonomy/%'
352
    // User $root = 'admin/config/people/accounts'
353
    $parts = explode('/', $root);
354
    $base_count = count($parts);
355

    
356
    // Configure settings pages.
357
    $settings_base = array(
358
      'access callback' => 'panelizer_is_panelized',
359
      'access arguments' => array($this->entity_type, $bundle),
360
      'file' => 'includes/admin.inc',
361
    );
362

    
363
    // This is the base tab that will be added. The weight is set
364
    // to try and make sure it stays to the right of manage fields
365
    // and manage display.
366
    $items[$root . '/panelizer'] = array(
367
      'title' => 'Panelizer',
368
      'page callback' => 'panelizer_allowed_content_page',
369
      'page arguments' => array($this->entity_type, $bundle),
370
      'type' => MENU_LOCAL_TASK,
371
      'weight' => 5,
372
    ) + $settings_base;
373

    
374
    $items[$root . '/panelizer/allowed'] = array(
375
      'title' => 'Allowed content',
376
      'page callback' => 'panelizer_allowed_content_page',
377
      'page arguments' => array($this->entity_type, $bundle),
378
      'type' => MENU_DEFAULT_LOCAL_TASK,
379
      'weight' => -10,
380
    ) + $settings_base;
381

    
382
    $weight = 1;
383
    foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
384
      $tabs_base = array(
385
        'access callback' => 'panelizer_has_no_choice_callback',
386
        'access arguments' => array($this->entity_type, $bundle, $view_mode),
387
        'page arguments' => array($this->entity_type, $bundle, 'default', $view_mode),
388
        'type' => MENU_LOCAL_TASK,
389
        'file' => 'includes/admin.inc',
390
        'weight' => $weight++,
391
      );
392

    
393
      $items[$root . '/panelizer/' . $view_mode] = array(
394
        'access callback' => 'panelizer_is_panelized',
395
        'title' => $view_mode_info['label'],
396
        'page callback' => 'panelizer_default_list_or_settings_page',
397
      ) + $tabs_base;
398

    
399
      $index = 0;
400
      foreach (panelizer_operations() as $path => $operation) {
401
        $items[$root . '/panelizer/' . $view_mode . '/' . $path] = array(
402
          'title' => $operation['menu title'],
403
          'page callback' => $operation['admin callback'],
404
          // Use the index to keep them in the proper order.
405
          'weight' => $index - 4,
406
          'type' => ($index === 0) ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
407
        ) + $tabs_base;
408
        if (isset($operation['file'])) {
409
          $items[$root . '/panelizer/' . $view_mode . '/' . $path]['file'] = $operation['file'];
410
        }
411
        if (isset($operation['file path'])) {
412
          $items[$root . '/panelizer/' . $view_mode . '/' . $path]['file path'] = $operation['file path'];
413
        }
414
        $index++;
415
      }
416

    
417
      $subtabs_base = array(
418
        'access callback' => 'panelizer_administer_panelizer_default',
419
        'access arguments' => array($this->entity_type, $bundle, $base_count + 2, $base_count + 1),
420
        'page arguments' => array($this->entity_type, $bundle, $base_count + 2, $base_count + 1),
421
        'type' => MENU_LOCAL_TASK,
422
        'file' => 'includes/admin.inc',
423
      );
424

    
425
      $items[$root . '/panelizer/' . $view_mode . '/%'] = array(
426
        'title' => 'Settings',
427
        'page callback' => 'panelizer_default_settings_page',
428
        'title callback' => 'panelizer_default_name_title_callback',
429
        'type' => MENU_CALLBACK,
430
      ) + $subtabs_base;
431

    
432
      $index = 0;
433
      foreach (panelizer_operations() as $path => $operation) {
434
        $items[$root . '/panelizer/' . $view_mode . '/%/' . $path] = array(
435
          'title' => $operation['menu title'],
436
          'page callback' => $operation['admin callback'],
437
          // Use the index to keep them in the proper order.
438
          'weight' => $index - 4,
439
        ) + $subtabs_base;
440
        if (isset($operation['file'])) {
441
          $items[$root . '/panelizer/' . $view_mode . '/%/' . $path]['file'] = $operation['file'];
442
        }
443
        if (isset($operation['file path'])) {
444
          $items[$root . '/panelizer/' . $view_mode . '/%/' . $path]['file path'] = $operation['file path'];
445
        }
446
        $index++;
447
      }
448

    
449
      // This special tab isn't a normal operation because appears only
450
      // in the admin menu.
451
      $items[$root . '/panelizer/' . $view_mode . '/%/access'] = array(
452
        'title' => 'Access',
453
        'page callback' => 'panelizer_default_access_page',
454
        'weight' => -2,
455
      ) + $subtabs_base;
456

    
457
      // Also make clones of all the export UI menu items. Again there is some
458
      // duplicated code here because of subtle differences.
459
      // Load the $plugin information
460
      $plugin = ctools_get_export_ui('panelizer_defaults');
461

    
462
      $ui_items = $plugin['menu']['items'];
463

    
464
      // Change the item to a tab.
465
      $ui_items['list']['type'] = MENU_LOCAL_TASK;
466
      $ui_items['list']['weight'] = -6;
467
      $ui_items['list']['title'] = 'List';
468

    
469
      // menu local actions are weird.
470
      $ui_items['add']['path'] = 'list/add';
471
      $ui_items['import']['path'] = 'list/import';
472

    
473
      // Edit is being handled elsewhere:
474
      unset($ui_items['edit callback']);
475
      unset($ui_items['access']);
476
      unset($ui_items['list callback']);
477
      // Edit is being handled elsewhere:
478
      foreach (panelizer_operations() as $path => $operation) {
479
        $location = isset($operation['ui path']) ? $operation['ui path'] : $path;
480
        if (isset($ui_items[$location])) {
481
          unset($ui_items[$location]);
482
        }
483
      }
484

    
485
      // Change the callbacks for everything:
486
      foreach ($ui_items as $key => $item) {
487
        // originally admin/config/content/panelizer/%panelizer_handler
488
        $ui_items[$key]['access callback'] = 'panelizer_has_choice_callback_view_mode';
489
        $ui_items[$key]['access arguments'] = array($this->entity_type, $bundle, $view_mode);
490
        $ui_items[$key]['page callback'] = 'panelizer_default_list_or_settings_page';
491
        $ui_items[$key]['page arguments'][0] = $view_mode;
492
        array_unshift($ui_items[$key]['page arguments'], '');
493
        array_unshift($ui_items[$key]['page arguments'], $bundle);
494
        array_unshift($ui_items[$key]['page arguments'], $this->entity_type);
495
        $ui_items[$key]['path'] = str_replace('list/', '', $ui_items[$key]['path']);
496
      }
497

    
498
      foreach ($ui_items as $item) {
499
        // Add menu item defaults.
500
        $item += array(
501
          'file' => 'export-ui.inc',
502
          'file path' => drupal_get_path('module', 'ctools') . '/includes',
503
        );
504

    
505
        $path = !empty($item['path']) ? $root . '/panelizer/' . $view_mode . '/' . $item['path'] : $root . '/panelizer/' . $view_mode;
506
        unset($item['path']);
507
        $items[$path] = $item;
508
      }
509
    }
510
  }
511

    
512
  /**
513
   * Add the panelizer settings form to a single entity bundle config form.
514
   *
515
   * @param &$form
516
   *   The form array.
517
   * @param &$form_state
518
   *   The form state array.
519
   * @param $bundle
520
   *   The machine name of the bundle this form is for.
521
   * @param $type_location
522
   *   The location in the form state values that the bundle name will be;
523
   *   this is used so that if a machine name of a bundle is changed, Panelizer
524
   *   can update as much as possible.
525
   */
526
  public function add_bundle_setting_form(&$form, &$form_state, $bundle, $type_location) {
527
    $settings = !empty($this->plugin['bundles'][$bundle]) ? $this->plugin['bundles'][$bundle] : array('status' => FALSE, 'default' => FALSE, 'choice' => FALSE);
528

    
529
    $form['panelizer'] = array(
530
      '#type' => 'fieldset',
531
      '#title' => t('Panelizer'),
532
      '#collapsible' => TRUE,
533
      '#collapsed' => FALSE,
534
      '#group' => 'additional_settings',
535
      '#attributes' => array(
536
        'class' => array('panelizer-node-type-settings-form'),
537
      ),
538
      '#bundle' => $bundle,
539
      '#location' => $type_location,
540
      '#tree' => TRUE,
541
      '#access' => panelizer_administer_entity_bundle($this, $bundle),
542
//      '#attached' => array(
543
//        'js' => array(drupal_get_path('module', 'comment') . '/panelizer-entity-form.js'),
544
//      ),
545
    );
546

    
547
    $form['panelizer']['status'] = array(
548
      '#title' => t('Panelize'),
549
      '#type' => 'checkbox',
550
      '#default_value' => !empty($settings['status']),
551
      '#id' => 'panelizer-status',
552
    );
553

    
554
    foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
555
      if (isset($this->plugin['view mode status'][$bundle][$view_mode]) && empty($this->plugin['view mode status'][$bundle][$view_mode])) {
556
        continue;
557
      }
558
      $form['panelizer']['view modes'][$view_mode] = array(
559
        '#type' => 'item',
560
        '#title' => $view_mode_info['label'],
561
        '#states' => array(
562
          'visible' => array(
563
            '#panelizer-status' => array('checked' => TRUE),
564
          ),
565
        ),
566
      );
567

    
568
      $form['panelizer']['view modes'][$view_mode]['status'] = array(
569
        '#title' => t('Panelize'),
570
        '#type' => 'checkbox',
571
        '#default_value' => !empty($settings['view modes'][$view_mode]['status']),
572
        '#id' => 'panelizer-' . $view_mode . '-status',
573
        '#states' => array(
574
          'visible' => array(
575
            '#panelizer-status' => array('checked' => TRUE),
576
          ),
577
        ),
578
      );
579
      $form['panelizer']['view modes'][$view_mode]['default'] = array(
580
        '#title' => t('Provide default panel'),
581
        '#type' => 'checkbox',
582
        '#default_value' => !empty($settings['view modes'][$view_mode]['default']),
583
        '#states' => array(
584
          'visible' => array(
585
            '#panelizer-status' => array('checked' => TRUE),
586
            '#panelizer-' . $view_mode . '-status' => array('checked' => TRUE),
587
          ),
588
        ),
589
        '#description' => t('If checked, a default panel will be utilized for all existing and new entities.'),
590
      );
591

    
592
      $form['panelizer']['view modes'][$view_mode]['choice'] = array(
593
        '#title' => t('Allow panel choice'),
594
        '#type' => 'checkbox',
595
        '#default_value' => !empty($settings['view modes'][$view_mode]['choice']),
596
        '#states' => array(
597
          'visible' => array(
598
            '#panelizer-status' => array('checked' => TRUE),
599
            '#panelizer-' . $view_mode . '-status' => array('checked' => TRUE),
600
          ),
601
        ),
602
        '#description' => t('If checked multiple panels can be created and each entity will get a selector to choose which panel to use.'),
603
      );
604
    }
605

    
606
    array_unshift($form['#submit'], 'panelizer_entity_default_bundle_form_submit');
607

    
608
    $form_state['panelizer_entity_handler'] = $this;
609
  }
610

    
611
  /**
612
   * Submit callback for the bundle edit form.
613
   */
614
  public function add_bundle_setting_form_submit($form, &$form_state, $bundle, $type_location) {
615
    // Some types do not support changing bundles, so we don't check if it's
616
    // not possible to change.
617
    if ($type_location) {
618
      $new_bundle = drupal_array_get_nested_value($form_state['values'], $type_location);
619
    }
620
    else {
621
      $new_bundle = $bundle;
622
    }
623

    
624
    // Check to see if the bundle has changed. If so we need to move stuff around.
625
    if ($bundle && $new_bundle != $bundle) {
626
      // Remove old settings.
627
      variable_del('panelizer_defaults_' . $this->entity_type . '_' . $bundle);
628
      $allowed_layouts = variable_get('panelizer_' . $this->entity_type . ':' . $bundle . '_allowed_layouts', NULL);
629
      if ($allowed_layouts) {
630
        variable_del('panelizer_' . $this->entity_type . ':' . $bundle . '_allowed_layouts');
631
        variable_set('panelizer_' . $this->entity_type . ':' . $new_bundle . '_allowed_layouts', $allowed_layouts);
632
      }
633
      $default = variable_get('panelizer_' . $this->entity_type . ':' . $bundle . '_default', NULL);
634
      if ($default) {
635
        variable_del('panelizer_' . $this->entity_type . ':' . $bundle . '_default');
636
        variable_set('panelizer_' . $this->entity_type . ':' . $new_bundle . '_default', $default);
637
      }
638

    
639
      // Load up all panelizer defaults for the old bundle and resave them
640
      // for the new bundle.
641
      $panelizer_defaults = $this->get_default_panelizer_objects($bundle);
642
      foreach ($panelizer_defaults as $panelizer) {
643
        list($entity_type, $old_bundle, $name) = explode(':', $panelizer->name);
644
        $panelizer->name = implode(':', array($entity_type, $new_bundle, $name));
645
        if ($panelizer->view_mode != 'page_manager') {
646
          $panelizer->name .= ':' . $panelizer->view_mode;
647
        }
648

    
649
        $panelizer->panelizer_key = $new_bundle;
650
        // If there's a pnid this should change the name and retain the pnid.
651
        // If there is no pnid this will create a new one in the database
652
        // because exported panelizer defaults attached to a bundle will have
653
        // to be moved to the database in order to follow along and
654
        // then be re-exported.
655
        // @todo -- should we warn the user about this?
656
        ctools_export_crud_save('panelizer_defaults', $panelizer);
657
      }
658
    }
659

    
660
    variable_set('panelizer_defaults_' . $this->entity_type . '_' . $new_bundle, $form_state['values']['panelizer']);
661

    
662
    // Unset this so that the type save forms don't try to save it to variables.
663
    unset($form_state['values']['panelizer']);
664
  }
665

    
666
  /**
667
   * Implements a delegated hook_menu.
668
   */
669
  public function hook_admin_paths(&$items) {
670
    if (!empty($this->plugin['entity path'])) {
671
      $bits = explode('/', $this->plugin['entity path']);
672
      foreach ($bits as $count => $bit) {
673
        if (strpos($bit, '%') === 0) {
674
          $bits[$count] = '*';
675
        }
676
      }
677

    
678
      $path = implode('/', $bits);
679
      $items[$path . '/panelizer*'] = TRUE;
680
    }
681
  }
682

    
683
  public function hook_menu_alter(&$items) {
684

    
685
  }
686

    
687
  public function hook_form_alter(&$form, &$form_state, $form_id) {
688

    
689
  }
690

    
691
  // Entity specific Drupal hooks
692
  public function hook_entity_load(&$entities) {
693
    ctools_include('export');
694
    $ids = array();
695
    $vids = array();
696
    $bundles = array();
697

    
698
    foreach ($entities as $entity) {
699
      // Don't bother if somehow we've already loaded and are asked to
700
      // load again.
701
      if (!empty($entity->panelizer)) {
702
        continue;
703
      }
704

    
705
      list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
706
      if ($this->is_panelized($bundle)) {
707
        $ids[] = $entity_id;
708
        if ($this->supports_revisions) {
709
          $vids[] = $revision_id;
710
        }
711
        $bundles[$entity_id] = $bundle;
712
      }
713
    }
714

    
715
    if (!$ids) {
716
      return;
717
    }
718

    
719
    // Load all the panelizers associated with the list of entities.
720
    if ($this->supports_revisions) {
721
      $result = db_query("SELECT * FROM {panelizer_entity} WHERE entity_type = '$this->entity_type' AND entity_id IN (:ids) AND revision_id IN (:vids)", array(':ids' => $ids, ':vids' => $vids));
722
    }
723
    else {
724
      $result = db_query("SELECT * FROM {panelizer_entity} WHERE entity_type = '$this->entity_type' AND entity_id IN (:ids)", array(':ids' => $ids));
725
    }
726

    
727
    $panelizers = array();
728
    while ($panelizer = $result->fetchObject()) {
729
      $panelizers[$panelizer->entity_id][$panelizer->view_mode] = $panelizer;
730
    }
731

    
732
    $defaults = array();
733
    $dids = array();
734
    // Go through our entity list and generate a list of defaults and displays
735
    foreach ($entities as $entity_id => $entity) {
736
      // Don't bother if somehow we've already loaded and are asked to
737
      // load again.
738
      if (!empty($entity->panelizer)) {
739
        continue;
740
      }
741

    
742
      // Skip not panelized bundles.
743
      if (empty($bundles[$entity_id])) {
744
        continue;
745
      }
746

    
747
      // Check for each view mode.
748
      foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
749
        $name = implode(':', array($this->entity_type, $bundles[$entity_id], 'default'));
750
        if ($view_mode != 'page_manager') {
751
          $name .= ':' . $view_mode;
752
        }
753

    
754
        // If no panelizer was loaded for the view mode, queue up defaults.
755
        if (empty($panelizers[$entity_id][$view_mode]) && $this->has_default_panel($bundles[$entity_id] . '.' . $view_mode)) {
756
          $defaults[$name] = $name;
757
        }
758
        // Otherwise unpack the loaded panelizer.
759
        else if (!empty($panelizers[$entity_id][$view_mode])) {
760
          $entity->panelizer[$view_mode] = ctools_export_unpack_object('panelizer_entity', $panelizers[$entity_id][$view_mode]);
761
          // If somehow we have no name AND no did, fill in the default.
762
          // This can happen if use of defaults has switched around maybe?
763
          if (empty($entity->panelizer[$view_mode]->did) &&
764
            empty($entity->panelizer[$view_mode]->name)) {
765
            if ($this->has_default_panel($bundles[$entity_id] . '.' . $view_mode)) {
766
              $entity->panelizer[$view_mode]->name = $name;
767
            }
768
            else {
769
              // With no default, did or name, this doesn't actually exist.
770
              unset($entity->panelizer[$view_mode]);
771
              list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
772

    
773
              db_delete('panelizer_entity')
774
                ->condition('entity_type', $this->entity_type)
775
                ->condition('entity_id', $entity_id)
776
                ->condition('revision_id', $revision_id)
777
                ->condition('view_mode', $view_mode)
778
                ->execute();
779
              continue;
780
            }
781
          }
782

    
783
          // Panelizers that do not have dids are just a selection of defaults
784
          // that has never actually been modified.
785
          if (empty($entity->panelizer[$view_mode]->did) && !empty($entity->panelizer[$view_mode]->name)) {
786
            $defaults[$entity->panelizer[$view_mode]->name] = $entity->panelizer[$view_mode]->name;
787
          }
788
          else {
789
            $dids[$entity->panelizer[$view_mode]->did] = $entity->panelizer[$view_mode]->did;
790
          }
791
        }
792
      }
793
    }
794

    
795
    // Load any defaults we collected.
796
    if ($defaults) {
797
      $panelizer_defaults = $this->load_default_panelizer_objects($defaults);
798
    }
799

    
800
    // if any panelizers were loaded, get their attached displays.
801
    if ($dids) {
802
      $displays = panels_load_displays($dids);
803
    }
804

    
805
    // Now, go back through our entities and assign dids and defaults
806
    // accordingly.
807
    foreach ($entities as $entity_id => $entity) {
808
      // Skip not panelized bundles.
809
      if (empty($bundles[$entity_id])) {
810
        continue;
811
      }
812
      // Check for each view mode.
813
      foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
814
        if (empty($entity->panelizer[$view_mode])) {
815
          // @todo there should be a convenience function for this.
816
          $default_key = implode(':', array($this->entity_type, $bundles[$entity_id], 'default'));
817
          if ($view_mode != 'page_manager') {
818
            $default_key .= ':' . $view_mode;
819
          }
820

    
821
          if (!empty($panelizer_defaults[$default_key])) {
822
            $entity->panelizer[$view_mode] = clone $panelizer_defaults[$default_key];
823
            // make sure this entity can't write to the default display.
824
            $entity->panelizer[$view_mode]->did = NULL;
825
          }
826
        }
827
        else if (empty($entity->panelizer[$view_mode]->display)) {
828
          if (!empty($entity->panelizer[$view_mode]->did)) {
829
            if (empty($displays[$entity->panelizer[$view_mode]->did])) {
830
              // Somehow the display for this entity has gotten lost?
831
              $entity->panelizer[$view_mode]->did = NULL;
832
              $entity->panelizer[$view_mode]->display = $this->get_default_display($bundles[$entity_id], $view_mode);
833
            }
834
            else {
835
              $entity->panelizer[$view_mode]->display = $displays[$entity->panelizer[$view_mode]->did];
836
            }
837
          }
838
          else {
839
            if (!empty($panelizer_defaults[$entity->panelizer[$view_mode]->name])) {
840
              $entity->panelizer[$view_mode]->display = $panelizer_defaults[$entity->panelizer[$view_mode]->name]->display;
841
            }
842
          }
843
        }
844
      }
845
    }
846
  }
847

    
848
  public function hook_entity_insert($entity) {
849
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
850
    if (!$this->is_panelized($bundle)) {
851
      return;
852
    }
853

    
854
    // If there's no panelizer information on the entity then there is nothing to do.
855
    if (empty($entity->panelizer)) {
856
      return;
857
    }
858

    
859
    // Allow exports or older data to be deployed successfully.
860
    if (is_object($entity->panelizer)) {
861
      $entity->panelizer = array('page_manager' => $entity->panelizer);
862
    }
863

    
864
    foreach ($entity->panelizer as $view_mode => $panelizer) {
865
      // Just a safety check to make sure we can't have a missing view mode.
866
      if (empty($view_mode)) {
867
        $view_mode = 'page_manager';
868
      }
869

    
870
      // On entity insert, we only write the display if it is not a default.
871
      // That probably means it came from an export or deploy or something
872
      // along those lines.
873
      if (empty($panelizer->name) && !empty($panelizer->display)) {
874
        // Ensure we don't accidentally overwrite existing display
875
        // data or anything silly like that.
876
        $panelizer = $this->clone_panelizer($panelizer, $entity);
877
        // First write the display
878
        panels_save_display($panelizer->display);
879

    
880
        // Make sure we have the new did.
881
        $panelizer->did = $panelizer->display->did;
882

    
883
        // Make sure there is a view mode.
884
        if (empty($panelizer->view_mode)) {
885
          $panelizer->view_mode = $view_mode;
886
        }
887

    
888
        // And write the new record.
889
        drupal_write_record('panelizer_entity', $panelizer);
890
      }
891
      else {
892
        // We write the panelizer record to record which name is being used.
893
        // And ensure the did is NULL:
894
        $panelizer->did = NULL;
895
        $panelizer->entity_type = $this->entity_type;
896
        $panelizer->entity_id = $entity_id;
897
        // The (int) ensures that entities that do not support revisions work
898
        // since the revision_id cannot be NULL.
899
        $panelizer->revision_id = (int) $revision_id;
900

    
901
        // Make sure there is a view mode.
902
        if (empty($panelizer->view_mode)) {
903
          $panelizer->view_mode = $view_mode;
904
        }
905

    
906
        drupal_write_record('panelizer_entity', $panelizer);
907
      }
908
    }
909
  }
910

    
911
  public function hook_entity_update($entity) {
912
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
913
    if (!$this->is_panelized($bundle)) {
914
      return;
915
    }
916

    
917
    // If there's no panelizer information on the entity then there is nothing to do.
918
    if (empty($entity->panelizer)) {
919
      return;
920
    }
921

    
922
    // Allow exports or older data to be deployed successfully.
923
    if (is_object($entity->panelizer)) {
924
      $entity->panelizer = array('page_manager' => $entity->panelizer);
925
    }
926

    
927
    foreach ($entity->panelizer as $view_mode => $panelizer) {
928
      // Just a safety check to make sure we can't have a missing view mode.
929
      if (empty($view_mode)) {
930
        $view_mode = 'page_manager';
931
      }
932

    
933
      if ($this->supports_revisions) {
934
        if (empty($panelizer->revision_id) || $panelizer->revision_id != $revision_id) {
935
          $panelizer->revision_id = $revision_id;
936
          $update = array();
937
        }
938
        else {
939
          $update = array('entity_type', 'revision_id', 'view_mode');
940
        }
941
      }
942
      else {
943
        if (empty($panelizer->entity_id)) {
944
          $update = array();
945
        }
946
        else {
947
          $update = array('entity_type', 'entity_id', 'view_mode');
948
        }
949
      }
950

    
951
      // The editor will set this flag if the display is modified. This lets
952
      // us know if we need to clone a new display or not.
953
      // NOTE: This means that when exporting or deploying, we need to be sure
954
      // to set the display_is_modified flag to ensure this gets written.
955
      if (!empty($panelizer->display_is_modified)) {
956
        // If this is a new entry or the entry is using a display from a default,
957
        // clone the display.
958
        if (!$update || empty($panelizer->did)) {
959
          $entity->panelizer[$view_mode] = $panelizer = $this->clone_panelizer($panelizer, $entity);
960

    
961
          // Update the cache key since we are adding a new display
962
          $panelizer->display->cache_key = implode(':', array('panelizer', $panelizer->entity_type, $panelizer->entity_id, $view_mode));
963
        }
964

    
965
        // First write the display
966
        panels_save_display($panelizer->display);
967

    
968
        // Make sure we have the did.
969
        $panelizer->did = $panelizer->display->did;
970

    
971
        // Ensure that we always write this as NULL when we have our own panel:
972
        $panelizer->name = NULL;
973

    
974
        // Make sure there is a view mode.
975
        if (empty($panelizer->view_mode)) {
976
          $panelizer->view_mode = $view_mode;
977
        }
978

    
979
        // And write the new record.
980
        return drupal_write_record('panelizer_entity', $panelizer, $update);
981
      }
982
      else {
983
        $panelizer->entity_type = $this->entity_type;
984
        $panelizer->entity_id = $entity_id;
985
        // The (int) ensures that entities that do not support revisions work
986
        // since the revision_id cannot be NULL.
987
        $panelizer->revision_id = (int) $revision_id;
988

    
989
        // Make sure there is a view mode.
990
        if (empty($panelizer->view_mode)) {
991
          $panelizer->view_mode = $view_mode;
992
        }
993

    
994
        drupal_write_record('panelizer_entity', $panelizer, $update);
995
      }
996
    }
997
  }
998

    
999
  public function hook_entity_delete($entity) {
1000
    $this->delete_entity_panelizer($entity);
1001
  }
1002

    
1003
  public function hook_field_attach_delete_revision($entity) {
1004
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1005

    
1006
    // Locate and delete all displays associated with the entity.
1007
    $revisions = db_query("SELECT revision_id, did FROM {panelizer_entity} WHERE entity_type = '$this->entity_type' AND entity_id = :id", array(':id' => $entity_id))->fetchAllAssoc('revision_id');
1008

    
1009
    // It is possible to have the same did on multiple revisions, if none of
1010
    // those revisions modified the display. Be careful NOT to delete a display
1011
    // that might be in use by another revision.
1012
    $seen = array();
1013
    foreach ($revisions as $info) {
1014
      if ($info->revision_id != $revision_id) {
1015
        $seen[$info->did] = TRUE;
1016
      }
1017
    }
1018

    
1019
    if (!empty($revisions[$revision_id]->did) && empty($seen[$revisions[$revision_id]->did])) {
1020
      panels_delete_display($revisions[$revision_id]->did);
1021
    }
1022

    
1023
    db_delete('panelizer_entity')
1024
      ->condition('entity_type', $this->entity_type)
1025
      ->condition('entity_id', $entity_id)
1026
      ->condition('revision_id', $revision_id)
1027
      ->execute();
1028
  }
1029

    
1030
  public function hook_field_attach_form($entity, &$form, &$form_state, $langcode) {
1031
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1032

    
1033
    // We'll store the form array here so that we can tell at the end if we
1034
    // have any and need to add our fieldset.
1035
    $widgets = array();
1036

    
1037
    foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
1038
      $view_bundle = $bundle . '.' . $view_mode;
1039

    
1040
      // Ignore view modes that don't have a choice or already have their
1041
      // own custom panel set up.
1042
      if (!$this->has_panel_choice($view_bundle) || !empty($entity->panelizer[$view_mode]->did)) {
1043
        continue;
1044
      }
1045

    
1046
      $panelizers = $this->get_default_panelizer_objects($view_bundle);
1047

    
1048
      $options = array();
1049
      foreach ($panelizers as $name => $panelizer) {
1050
        if (empty($panelizer->disabled)) {
1051
          $options[$name] = $panelizer->title ? $panelizer->title : t('Default');
1052
        }
1053
      }
1054

    
1055
      if (!empty($entity->panelizer[$view_mode]->name)) {
1056
        $name = $entity->panelizer[$view_mode]->name;
1057
      }
1058
      else {
1059
        if ($this->has_default_panel($view_bundle)) {
1060
          $name = implode(':', array($this->entity_type, $bundle, 'default'));
1061
          if ($view_mode != 'page_manager') {
1062
            $name .= ':' . $view_mode;
1063
          }
1064
        }
1065
        else {
1066
          $name = '';
1067
        }
1068
      }
1069

    
1070
      if (!$this->has_default_panel($view_bundle)) {
1071
        $options = array('' => t('-- No panel --')) + $options;
1072
      }
1073

    
1074
      $widgets[$view_mode]['name'] = array(
1075
        '#title' => $view_mode_info['label'],
1076
        '#type' => 'select',
1077
        '#options' => $options,
1078
        '#default_value' => $name,
1079
        // Put these here because submit does not get a real entity with
1080
        // the actual *(&)ing panelizer.
1081
        '#revision_id' => isset($entity->panelizer[$view_mode]->revision_id) ? $entity->panelizer[$view_mode]->revision_id : NULL,
1082
        '#entity_id' => isset($entity->panelizer[$view_mode]->entity_id) ? $entity->panelizer[$view_mode]->entity_id : NULL,
1083
      );
1084
    }
1085

    
1086
    if ($widgets) {
1087
      $form_state['panelizer has choice'] = TRUE;
1088
      $form['panelizer'] = array(
1089
        '#type' => 'fieldset',
1090
        '#access' => $this->panelizer_access('choice', $entity, $view_mode),
1091
        '#title' => t('Panelizer'),
1092
        '#collapsible' => TRUE,
1093
        '#collapsed' => TRUE,
1094
        '#group' => 'additional_settings',
1095
        '#attributes' => array(
1096
          'class' => array('panelizer-entity-options'),
1097
        ),
1098
        '#attached' => array(
1099
          'js' => array(ctools_attach_js('panelizer-vertical-tabs', 'panelizer')),
1100
        ),
1101
        '#weight' => -10,
1102
        '#tree' => TRUE,
1103
      ) + $widgets;
1104
    }
1105
  }
1106

    
1107
  public function hook_field_attach_submit($entity, &$form, &$form_state) {
1108
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1109
    if (!empty($form_state['panelizer has choice'])) {
1110
      foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
1111
        if (isset($form_state['values']['panelizer'][$view_mode]['name'])) {
1112
          $entity->panelizer[$view_mode] = $this->get_default_panelizer_object($bundle . '.' . $view_mode, $form_state['values']['panelizer'][$view_mode]['name']);
1113
          $entity->panelizer[$view_mode]->did = NULL;
1114

    
1115
          // Ensure original values are maintained:
1116
          $entity->panelizer[$view_mode]->entity_id = $form['panelizer'][$view_mode]['name']['#entity_id'];
1117
          $entity->panelizer[$view_mode]->revision_id = $form['panelizer'][$view_mode]['name']['#revision_id'];
1118
        }
1119
      }
1120
    }
1121
  }
1122

    
1123
  /**
1124
   * Determine if the entity allows revisions.
1125
   *
1126
   * @param $entity
1127
   *   The entity to test.
1128
   *
1129
   * @return array
1130
   *   An array containing two boolean values. The first one lets the system
1131
   *   know whether or not the entity currently allows revisions. The second
1132
   *   one lets us know if the user has access to control whether or not a
1133
   *   new revision is created.
1134
   */
1135
  public function entity_allows_revisions($entity) {
1136
    return array(FALSE, FALSE);
1137
  }
1138

    
1139
  /**
1140
   * Create a new, scrubbed version of a panelizer object.
1141
   */
1142
  public function clone_panelizer($panelizer, $entity) {
1143
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1144
    $panelizer_clone = clone $panelizer;
1145

    
1146
    // In order to ensure we don't actually use and modify the default display,
1147
    // we export and re-import it.
1148
    $code = panels_export_display($panelizer->display);
1149
    ob_start();
1150
    eval($code);
1151
    ob_end_clean();
1152

    
1153
    $panelizer_clone->display = $display;
1154
    $panelizer_clone->did = NULL;
1155
    $panelizer_clone->name = NULL;
1156
    $panelizer_clone->entity_type = $this->entity_type;
1157
    $panelizer_clone->entity_id = $entity_id;
1158
    // The (int) ensures that entities that do not support revisions work
1159
    // since the revision_id cannot be NULL.
1160
    $panelizer_clone->revision_id = (int) $revision_id;
1161

    
1162
    return $panelizer_clone;
1163
  }
1164

    
1165
  function access_admin($entity, $op = NULL, $view_mode = NULL) {
1166
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1167
    if ($view_mode) {
1168
      $bundle .= '.' . $view_mode;
1169
    }
1170
    else {
1171
      $view_mode = 'page_manager';
1172
    }
1173

    
1174
    if (!$this->is_panelized($bundle)) {
1175
      return FALSE;
1176
    }
1177

    
1178
    return $this->panelizer_access($op, $entity, $view_mode) && $this->entity_access('update', $entity);
1179
  }
1180

    
1181
  /**
1182
   * Determine if the user has access to the panelizer operation for this type.
1183
   */
1184
  function panelizer_access($op, $bundle, $view_mode) {
1185
    if (is_object($bundle)) {
1186
      $entity = $bundle;
1187
      list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1188

    
1189
      // If there is an $op, this must actually be panelized in order to pass.
1190
      // If there is no op, then the settings page can provide us a "panelize it!"
1191
      // page even if there is no panel.
1192
      if ($op && $op != 'overview' && $op != 'settings' && $op != 'choice' && empty($entity->panelizer[$view_mode])) {
1193
        return FALSE;
1194
      }
1195
    }
1196

    
1197
    return user_access('administer panelizer') || user_access("administer panelizer $this->entity_type $bundle $op");
1198
  }
1199

    
1200
  /**
1201
   * Switched page callback to give the overview page
1202
   */
1203
  function page_overview($js, $input, $entity) {
1204
    $header = array(
1205
      t('View mode'),
1206
      t('Status'),
1207
      t('Operations'),
1208
    );
1209

    
1210
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1211

    
1212
    $rows = array();
1213

    
1214
    $base_url = $this->entity_base_url($entity);
1215

    
1216
    foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
1217
      if (!$this->is_panelized($bundle . '.' . $view_mode)) {
1218
        continue;
1219
      }
1220

    
1221
      $row = array();
1222
      $row[] = $view_mode_info['label'];
1223
      $panelized = TRUE;
1224

    
1225
      if (!empty($entity->panelizer[$view_mode]->name)) {
1226
        ctools_include('export');
1227
        $panelizer = ctools_export_crud_load('panelizer_defaults', $entity->panelizer[$view_mode]->name);
1228
        $status = !empty($panelizer->title) ? check_plain($panelizer->title) : t('Default');
1229
      }
1230
      else if (!empty($entity->panelizer[$view_mode]->did)) {
1231
        $status = t('Custom');
1232
      }
1233
      else {
1234
        $status = t('Not panelized');
1235
        $panelized = FALSE;
1236
      }
1237
      $row[] = $status;
1238

    
1239
      if ($panelized) {
1240
        $links_array = array();
1241
        foreach (panelizer_operations() as $path => $operation) {
1242
          if ($this->panelizer_access($path, $entity, $view_mode)) {
1243
            $links_array[$path] = array(
1244
              'title' => $operation['link title'],
1245
              'href' => $base_url . '/' . $view_mode . '/' . $path,
1246
            );
1247
          }
1248
        }
1249
        if ($status == t('Custom')) {
1250
          $links_array['reset'] = array(
1251
            'title' => t('reset'),
1252
            'href' => $base_url . '/' . $view_mode . '/reset',
1253
          );
1254
        }
1255
      }
1256
      else {
1257
        $links_array = array(
1258
          'panelize' => array(
1259
            'title' => t('panelize'),
1260
            'href' => $base_url . '/' . $view_mode,
1261
          ),
1262
        );
1263
      }
1264

    
1265
      // Allow applications to add additional panelizer tabs.
1266
      drupal_alter('panelizer_overview_links', $links_array, $this->entity_type, $entity, $view_mode, $status, $panelized);
1267

    
1268
      $links = theme('links', array(
1269
        'links' => $links_array,
1270
        'attributes' => array('class' => array('links', 'inline')),
1271
      ));
1272

    
1273
      $row[] = $links;
1274
      $rows[] = $row;
1275
    }
1276

    
1277
    $output = theme('table', array('header' => $header, 'rows' => $rows));
1278
    return $output;
1279
  }
1280

    
1281
  /**
1282
   * Provides the base panelizer URL for an entity.
1283
   */
1284
  function entity_base_url($entity, $view_mode = NULL) {
1285
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1286

    
1287
    $bits = explode('/', $this->plugin['entity path']);
1288
    foreach ($bits as $count => $bit) {
1289
      if (strpos($bit, '%') === 0) {
1290
        $bits[$count] = $entity_id;
1291
      }
1292
    }
1293

    
1294
    $bits[] = 'panelizer';
1295
    if ($view_mode) {
1296
      $bits[] = $view_mode;
1297
    }
1298
    $base_url = implode('/', $bits);
1299

    
1300
    return $base_url;
1301
  }
1302

    
1303
  /**
1304
   * Provides a wrapper for the panelizer page output.
1305
   *
1306
   * Drupal only supports 2 levels of tabs, but we need a 3rd
1307
   * level. We will fake it.
1308
   */
1309
  function wrap_entity_panelizer_pages($entity, $view_mode, $output) {
1310
    $base_url = $this->entity_base_url($entity, $view_mode);
1311
    return $this->make_fake_tabs($base_url, $entity, $view_mode, $output);
1312
  }
1313

    
1314
  /**
1315
   * Provides a wrapper for the panelizer page output.
1316
   *
1317
   * Drupal only supports 2 levels of tabs, but we need a 3rd
1318
   * level. We will fake it.
1319
   */
1320
  function wrap_default_panelizer_pages($bundle, $output) {
1321
    list($bundle, $view_mode) = explode('.', $bundle);
1322
    $base_url = $this->entity_admin_root . '/panelizer/' . $view_mode;
1323
    // We have to sub in the bundle if this is set.
1324
    if (is_numeric($this->entity_admin_bundle)) {
1325
      $bits = explode('/', $base_url);
1326
      $bits[$this->entity_admin_bundle] = $bundle;
1327
      $base_url = implode('/', $bits);
1328
    }
1329

    
1330
    return $this->make_fake_tabs($base_url, $bundle, $view_mode, $output);
1331
  }
1332

    
1333
  /**
1334
   * Create some fake tabs that are attached to a page output.
1335
   */
1336
  function make_fake_tabs($base_url, $bundle, $view_mode, $output) {
1337
    $links_array = array();
1338
    foreach (panelizer_operations() as $path => $operation) {
1339
      if ($this->panelizer_access($path, $bundle, $view_mode)) {
1340
        $links_array[$path] = array(
1341
          'title' => t($operation['menu title']),
1342
          'href' => $base_url . '/' . $path,
1343
        );
1344
      }
1345
    }
1346

    
1347
    // Allow applications to add additional panelizer tabs.
1348
    drupal_alter('panelizer_tab_links', $links_array, $this->entity_type, $bundle, $view_mode);
1349

    
1350
    // Only render if > 1 link, just like core.
1351
    if (count($links_array) <= 1) {
1352
      return $output;
1353
    }
1354

    
1355
    // These fake tabs are pretty despicable, but they'll do.
1356
    $links = '<div class="tabs clearfix">' . theme('links', array(
1357
      'links' => $links_array,
1358
      'attributes' => array('class' => array('tabs', 'secondary')),
1359
    )) . '</div>';
1360

    
1361
    if (is_array($output)) {
1362
      // Use array addition because forms will already be sorted so
1363
      // #weight may not be effective.
1364
      $output = array(
1365
        'panelizer_links' => array(
1366
          '#markup' => $links,
1367
          '#weight' => -10000,
1368
        ),
1369
      ) + $output;
1370
    }
1371
    else {
1372
      $output = $links . $output;
1373
    }
1374

    
1375
    return $output;
1376
  }
1377

    
1378
  /**
1379
   * Switched page callback to give the settings form.
1380
   */
1381
  function page_reset($js, $input, $entity, $view_mode) {
1382
    $panelizer = $entity->panelizer[$view_mode];
1383

    
1384
    $form_state = array(
1385
      'entity' => $entity,
1386
      'panelizer' => $panelizer,
1387
      'view_mode' => $view_mode,
1388
      'no_redirect' => TRUE,
1389
    );
1390

    
1391
    ctools_include('common', 'panelizer');
1392
    $output = drupal_build_form('panelizer_reset_entity_form', $form_state);
1393
    if (!empty($form_state['executed'])) {
1394
      drupal_set_message(t('Panelizer information has been reset.'));
1395
      $this->delete_entity_panelizer($entity, $view_mode);
1396
      drupal_goto(dirname(dirname($_GET['q'])));
1397
    }
1398

    
1399
    return $output;
1400
  }
1401

    
1402
  /**
1403
   * Switched page callback to give the settings form.
1404
   */
1405
  function page_settings($js, $input, $entity, $view_mode) {
1406
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1407

    
1408
    if (empty($entity->panelizer[$view_mode])) {
1409
      // If this entity is not yet panelized, and there is no default panel
1410
      // to do configuration, give them the option of panelizing it.
1411
      if ($this->has_default_panel($bundle . '.' . $view_mode)) {
1412
        return MENU_NOT_FOUND;
1413
      }
1414

    
1415
      // Set the form to the Panelize It! form.
1416
      $form_id = 'panelizer_panelize_entity_form';
1417

    
1418
      // Fetch a special default panelizer that is only accessible with the
1419
      // default_anyway flag.
1420
      $panelizer = $this->get_internal_default_panelizer($bundle, $view_mode);
1421
      $panelizer->name = NULL;
1422
    }
1423
    else {
1424
      $form_id = 'panelizer_settings_form';
1425
      $panelizer = $entity->panelizer[$view_mode];
1426
    }
1427

    
1428
    $form_state = array(
1429
      'entity' => $entity,
1430
      'revision info' => $this->entity_allows_revisions($entity),
1431
      'panelizer' => $panelizer,
1432
      'view_mode' => $view_mode,
1433
      'no_redirect' => TRUE,
1434
    );
1435

    
1436
    ctools_include('common', 'panelizer');
1437
    $output = drupal_build_form($form_id, $form_state);
1438
    if (!empty($form_state['executed'])) {
1439
      drupal_set_message(t('The settings have been updated.'));
1440
      $entity->panelizer[$view_mode] = $form_state['panelizer'];
1441
      // Make sure that entity_save knows that the panelizer settings
1442
      // are modified and must be made local to the entity.
1443
      if (empty($panelizer->did) || !empty($panelizer->name)) {
1444
        $panelizer->display_is_modified = TRUE;
1445
      }
1446
      $this->entity_save($entity);
1447

    
1448
      drupal_goto($_GET['q']);
1449
    }
1450

    
1451
    return $this->wrap_entity_panelizer_pages($entity, $view_mode, $output);
1452
  }
1453

    
1454
  function page_context($js, $input, $entity, $view_mode) {
1455
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1456

    
1457
    $cache_key = $entity_id . '.' . $view_mode;
1458
    $panelizer = panelizer_context_cache_get($this->entity_type, $cache_key);
1459

    
1460
    if (empty($panelizer)) {
1461
      return MENU_NOT_FOUND;
1462
    }
1463

    
1464
    $form_state = array(
1465
      'panelizer' => &$panelizer,
1466
      'entity' => $entity,
1467
      'revision info' => $this->entity_allows_revisions($entity),
1468
      'panelizer type' => $this->entity_type,
1469
      'cache key' => $cache_key,
1470
      'no_redirect' => TRUE,
1471
    );
1472

    
1473
    ctools_include('common', 'panelizer');
1474
    $output = drupal_build_form('panelizer_default_context_form', $form_state);
1475
    if (!empty($form_state['executed'])) {
1476
      if (!empty($form_state['clicked_button']['#write'])) {
1477
        drupal_set_message(t('The settings have been updated.'));
1478
        $entity->panelizer[$view_mode] = $form_state['panelizer'];
1479
        $this->entity_save($entity);
1480
      }
1481
      else {
1482
        drupal_set_message(t('Changes have been discarded.'));
1483
      }
1484

    
1485
      panelizer_context_cache_clear($this->entity_type, $cache_key);
1486
      drupal_goto($_GET['q']);
1487
    }
1488

    
1489
    return $this->wrap_entity_panelizer_pages($entity, $view_mode, $output);
1490
  }
1491

    
1492
  function page_layout($js, $input, $entity, $view_mode, $step = NULL, $layout = NULL) {
1493
    $panelizer = $entity->panelizer[$view_mode];
1494
    if (empty($panelizer)) {
1495
      return MENU_NOT_FOUND;
1496
    }
1497

    
1498
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1499

    
1500
    $display = $panelizer->display;
1501
    $display->context = $this->get_contexts($panelizer, $entity);
1502

    
1503
    $path = $this->entity_base_url($entity, $view_mode);
1504

    
1505
    $form_state = array(
1506
      'entity' => $entity,
1507
      'revision info' => $this->entity_allows_revisions($entity),
1508
      'display' => $display,
1509
      'wizard path' => $path . '/layout/%step',
1510
      'allowed_layouts' => 'panelizer_' . $this->entity_type . ':' . $bundle,
1511
    );
1512

    
1513
    ctools_include('common', 'panelizer');
1514
    $output = panelizer_change_layout_wizard($form_state, $step, $layout);
1515
    if (!empty($form_state['complete'])) {
1516
      $entity->panelizer[$view_mode]->display = $form_state['display'];
1517
      $entity->panelizer[$view_mode]->display_is_modified = TRUE;
1518
      $this->entity_save($entity);
1519
      drupal_set_message(t('The layout has been changed.'));
1520
      drupal_goto($path . '/content');
1521
    }
1522

    
1523
    return $this->wrap_entity_panelizer_pages($entity, $view_mode, $output);
1524
  }
1525

    
1526
  function page_content($js, $input, $entity, $view_mode) {
1527
    $panelizer = $entity->panelizer[$view_mode];
1528
    if (empty($panelizer)) {
1529
      return MENU_NOT_FOUND;
1530
    }
1531

    
1532
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1533

    
1534
    $form_state = array(
1535
      'entity' => $entity,
1536
      'revision info' => $this->entity_allows_revisions($entity),
1537
      'display cache' => panels_edit_cache_get(implode(':', array('panelizer', $this->entity_type, $entity_id, $view_mode))),
1538
      'no_redirect' => TRUE,
1539
    );
1540

    
1541
    ctools_include('common', 'panelizer');
1542
    $output = drupal_build_form('panelizer_edit_content_form', $form_state);
1543
    if (!empty($form_state['executed'])) {
1544
      if (!empty($form_state['clicked_button']['#save-display'])) {
1545
        drupal_set_message(t('The settings have been updated.'));
1546
        $entity->panelizer[$view_mode]->display = $form_state['display'];
1547
        $entity->panelizer[$view_mode]->display_is_modified = TRUE;
1548
        $this->entity_save($entity);
1549
      }
1550
      else {
1551
        drupal_set_message(t('Changes have been discarded.'));
1552
      }
1553

    
1554
      panels_edit_cache_clear($form_state['display cache']);
1555
      drupal_goto($_GET['q']);
1556
    }
1557

    
1558
    $output = $this->wrap_entity_panelizer_pages($entity, $view_mode, $output);
1559

    
1560
    ctools_set_no_blocks(FALSE);
1561
    drupal_set_page_content($output);
1562
    $page = element_info('page');
1563
    return $page;
1564
  }
1565

    
1566
  /**
1567
   * Delete panelizers associated with the entity.
1568
   *
1569
   * @param object $entity
1570
   *   The entity.
1571
   * @param $view_mode
1572
   *   The view mode to delete. If not specified, all view modes will be
1573
   *   deleted.
1574
   */
1575
  function delete_entity_panelizer($entity, $view_mode = NULL) {
1576
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
1577

    
1578
    if (empty($view_mode)) {
1579
      // Locate and delete all displays associated with the entity.
1580
      $dids = db_query("SELECT did FROM {panelizer_entity} WHERE entity_type = '$this->entity_type' AND entity_id = :id", array(':id' => $entity_id))->fetchCol();
1581
    }
1582
    else {
1583
      $dids = db_query("SELECT did FROM {panelizer_entity} WHERE entity_type = '$this->entity_type' AND entity_id = :id AND view_mode = :view_mode", array(':id' => $entity_id, ':view_mode' => $view_mode))->fetchCol();
1584
    }
1585

    
1586
    foreach (array_unique($dids) as $did) {
1587
      panels_delete_display($did);
1588
    }
1589

    
1590
    $delete = db_delete('panelizer_entity')
1591
      ->condition('entity_type', $this->entity_type)
1592
      ->condition('entity_id', $entity_id);
1593

    
1594
    if ($view_mode) {
1595
      $delete->condition('view_mode', $view_mode);
1596
    }
1597

    
1598
    $delete->execute();
1599
  }
1600

    
1601
  /**
1602
   * Determine if a bundle is panelized.
1603
   */
1604
  public function is_panelized($bundle) {
1605
    if (strpos($bundle, '.') === FALSE) {
1606
      return !empty($this->plugin['bundles'][$bundle]) && !empty($this->plugin['bundles'][$bundle]['status']);
1607
    }
1608
    else {
1609
      list($bundle, $view_mode) = explode('.', $bundle);
1610
      return !empty($this->plugin['bundles'][$bundle]) && !empty($this->plugin['bundles'][$bundle]['status']) && !empty($this->plugin['bundles'][$bundle]['view modes'][$view_mode]['status']);
1611
    }
1612
  }
1613

    
1614
  /**
1615
   * Determine if a bundle has a default panel.
1616
   *
1617
   * @param $bundle
1618
   *   A $bundle.$view_mode combo string. If no view mode is specified
1619
   *   then the 'page_manager' view mode will be assumed.
1620
   */
1621
  public function has_default_panel($bundle) {
1622
    if (strpos($bundle, '.') === FALSE) {
1623
      $bundle .= '.page_manager';
1624
    }
1625
    list($bundle, $view_mode) = explode('.', $bundle);
1626

    
1627
    return $this->is_panelized($bundle) && !empty($this->plugin['bundles'][$bundle]['view modes'][$view_mode]['default']);
1628
  }
1629

    
1630
  /**
1631
   * Determine if a bundle is allowed choices.
1632
   */
1633
  public function has_panel_choice($bundle) {
1634
    if (strpos($bundle, '.') === FALSE) {
1635
      $bundle .= '.page_manager';
1636
    }
1637
      list($bundle, $view_mode) = explode('.', $bundle);
1638

    
1639
    return $this->is_panelized($bundle) && !empty($this->plugin['bundles'][$bundle]['view modes'][$view_mode]['choice']);
1640
  }
1641

    
1642
  /**
1643
   * Get the default panels, keyed by names.
1644
   */
1645
  public function load_default_panelizer_objects($names) {
1646
    ctools_include('export');
1647
    $panelizers = ctools_export_load_object('panelizer_defaults', 'names', $names);
1648
    return $panelizers;
1649
  }
1650

    
1651
  /**
1652
   * Get the default panelizers for the given bundle.
1653
   */
1654
  public function get_default_panelizer_objects($bundle) {
1655
    if (strpos($bundle, '.') !== FALSE) {
1656
      list($bundle, $view_mode) = explode('.', $bundle);
1657
    }
1658
    $conditions = array(
1659
      'panelizer_type' => $this->entity_type,
1660
      'panelizer_key' => $bundle,
1661
    );
1662

    
1663
    if (!empty($view_mode)) {
1664
      $conditions['view_mode'] = $view_mode;
1665
    }
1666

    
1667
    ctools_include('export');
1668
    return ctools_export_load_object('panelizer_defaults', 'conditions', $conditions);
1669
  }
1670

    
1671
  /**
1672
   * Determine if the current user has access to the $panelizer.
1673
   */
1674
  public function access_default_panelizer_object($panelizer) {
1675
    // Automatically true for this, regardless of anything else.
1676
    if (user_access('administer panelizer')) {
1677
      return TRUE;
1678
    }
1679

    
1680
    ctools_include('context');
1681
    return user_access("administer panelizer $this->entity_type $panelizer->panelizer_key defaults") && ctools_access($panelizer->access, $this->get_contexts($panelizer));
1682
  }
1683

    
1684
  /**
1685
   * Implements a delegated hook_panelizer_defaults().
1686
   *
1687
   * This makes sure that all panelized entities configured to have a
1688
   * default actually have one.
1689
   */
1690
  public function hook_panelizer_defaults(&$panelizers) {
1691
    // For features integration, if they have modified a default and put
1692
    // it into the database, we do not want to show one as a default.
1693
    // Otherwise, features can't latch onto it.
1694
    $default_names = &drupal_static('panelizer_defaults_in_database', NULL);
1695
    if (!isset($default_names)) {
1696
      $default_names = drupal_map_assoc(db_query("SELECT name FROM {panelizer_defaults} WHERE name LIKE '%:default&'")->fetchCol());
1697
    }
1698

    
1699
    $entity_info = entity_get_info($this->entity_type);
1700

    
1701
    foreach ($this->plugin['bundles'] as $bundle => $info) {
1702
      // Don't bother if there are no
1703
      if (empty($info['status'])) {
1704
        continue;
1705
      }
1706

    
1707
      foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
1708
        if (!empty($info['view modes'][$view_mode]['status']) && !empty($info['view modes'][$view_mode]['default'])) {
1709
          $panelizer = $this->get_internal_default_panelizer($bundle, $view_mode);
1710
          if (empty($default_names[$panelizer->name]) && !isset($panelizers[$panelizer->name])) {
1711
            $panelizers[$panelizer->name] = $panelizer;
1712
          }
1713
        }
1714
      }
1715
    }
1716
  }
1717

    
1718
  /**
1719
   * An internal representation of a panelizer object, used to seed when
1720
   * we have none and want something to get started.
1721
   */
1722
  public function get_internal_default_panelizer($bundle, $view_mode) {
1723
    ctools_include('export');
1724
    $load_name = implode(':', array($this->entity_type, $bundle, 'default'));
1725
    $panelizer = ctools_export_crud_new('panelizer_defaults');
1726
    $panelizer->name = $load_name;
1727
    // Attach the view mode to the name, which is specially generated
1728
    // to ignore the specialty "page_manager" view mode.
1729
    if ($view_mode != 'page_manager') {
1730
      $panelizer->name .= ':' . $view_mode;
1731
    }
1732

    
1733
    $panelizer->panelizer_type = $this->entity_type;
1734
    $panelizer->panelizer_key = $bundle;
1735
    $panelizer->view_mode = $view_mode;
1736
    $panelizer->display = $this->get_default_display($bundle, $view_mode);
1737
    $panelizer->api_version = 1;
1738
    $panelizer->title = t('Default');
1739

    
1740
    return $panelizer;
1741
  }
1742

    
1743
  /**
1744
   * Load the named default panel for the bundle.
1745
   */
1746
  public function get_default_panelizer_object($bundle, $name) {
1747
    if (strpos($bundle, '.') !== FALSE) {
1748
      list($bundle, $view_mode) = explode('.', $bundle);
1749
    }
1750
    else {
1751
      $view_mode = 'page_manager';
1752
    }
1753

    
1754
    // If the name is not in the format of entitytype:bundle:name which is
1755
    // the machine name used, split that out automatically.
1756
    if (strpos($name, ':') === FALSE) {
1757
      $name = implode(':', array($this->entity_type, $bundle, $name));
1758
      // This is the default view mode and older defaults won't have this,
1759
      // so we don't enforce it.
1760
      if ($view_mode != 'page_manager') {
1761
        $name .= ':' . $view_mode;
1762
      }
1763
    }
1764

    
1765
    ctools_include('export');
1766
    return ctools_export_crud_load('panelizer_defaults', $name);
1767
  }
1768

    
1769
  /**
1770
   * Provide a default display for newly panelized entities.
1771
   *
1772
   * This should be implemented by the entity plugin.
1773
   */
1774
  function get_default_display($bundle, $view_mode) {
1775
    // This is a straight up empty display.
1776
    $display = panels_new_display();
1777
    $display->layout = 'flexible';
1778

    
1779
    $panes = array();
1780
    foreach (field_info_instances($this->entity_type, $bundle) as $field_name => $instance) {
1781
      $view_mode_settings = field_view_mode_settings($this->entity_type, $bundle);
1782
      $actual_mode = (!empty($view_mode_settings[$view_mode]['custom_settings']) ? $view_mode : 'default');
1783
      $field_display = $instance['display'][$actual_mode];
1784

    
1785
      $pane = panels_new_pane('entity_field', $this->entity_type . ':' . $field_name, TRUE);
1786
      $pane->configuration['formatter'] = $field_display['type'];
1787
      $pane->configuration['formatter_settings'] = $field_display['settings'];
1788
      $pane->configuration['label'] = $field_display['label'];
1789
      $pane->configuration['context'] = 'panelizer';
1790
      $panes[] = array(
1791
        '#pane' => $pane,
1792
        '#weight' => $field_display['weight'],
1793
      );
1794
    }
1795

    
1796
    // Use our #weights to sort these so they appear in whatever order the
1797
    // normal field configuration put them in.
1798
    uasort($panes, 'element_sort');
1799
    foreach ($panes as $pane) {
1800
      $display->add_pane($pane['#pane'], 'center');
1801
    }
1802

    
1803
    return $display;
1804
  }
1805

    
1806
  /**
1807
   * Get a panelizer object for the key.
1808
   *
1809
   * This must be implemented for each entity type.
1810
   */
1811
//  function get_panelizer_object($entity_id) {
1812
//  }
1813

    
1814
  /**
1815
   * Add entity specific form to the Panelizer settings form.
1816
   *
1817
   * This is primarily to allow bundle selection per entity type.
1818
   */
1819
  public function settings_form(&$form, &$form_state) {
1820
    // Add entity settings
1821
    // @todo change theme function name
1822
    $form['entities'][$this->entity_type] = array(
1823
      '#theme' => 'panelizer_settings_page_table',
1824
      '#header' => array(
1825
        array('data' => $this->entity_bundle_label(), 'width' => '15%'),
1826
        t('Panelize'),
1827
        t('Provide default panel'),
1828
        t('Allow panel choice'),
1829
        array('data' => t('Operations'), 'width' => '50%'),
1830
      ),
1831
      '#columns' => array('title', 'status', 'default', 'choice', 'links'),
1832
    );
1833

    
1834
    $entity_info = entity_get_info($this->entity_type);
1835

    
1836
    $bundles = $entity_info['bundles'];
1837

    
1838
    drupal_alter('panelizer_default_types', $bundles, $this->entity_type);
1839

    
1840
    foreach ($bundles as $bundle => $bundle_info) {
1841
      $base_url = 'admin/config/content/panelizer/' . $this->entity_type . '/' . $bundle;
1842
      $bundle_id = str_replace(array('][', '_', ' '), '-', '#edit-entities-' . $this->entity_type . '-' . $bundle . '-0');
1843

    
1844
      // Add the widgets that apply only to the bundle.
1845
      $form['entities'][$this->entity_type][$bundle][0]['title'] = array(
1846
        '#markup' => '<strong>' . $bundle_info['label'] . '</strong>',
1847
      );
1848

    
1849
      $form['entities'][$this->entity_type][$bundle][0]['status'] = array(
1850
        '#type' => 'checkbox',
1851
        '#default_value' => !empty($this->plugin['bundles'][$bundle]['status']),
1852
      );
1853

    
1854
      // Set proper allowed content link for entire bundle based on status
1855
      if (!empty($this->plugin['bundles'][$bundle]['status'])) {
1856
        $links_array = array(
1857
          'settings' => array(
1858
            'title' => t('allowed content'),
1859
            'href' => $base_url . '/allowed',
1860
          ),
1861
        );
1862
        $links = theme('links', array(
1863
          'links' => $links_array,
1864
          'attributes' => array('class' => array('links', 'inline')),
1865
        ));
1866
      }
1867
      else {
1868
        $links = t('Save to access allowed content');
1869
      }
1870

    
1871
      $form['entities'][$this->entity_type][$bundle][0]['links']['basic'] = array(
1872
        '#type' => 'item',
1873
        '#title' => $links,
1874
        '#states' => array(
1875
          'visible' => array(
1876
            $bundle_id . '-status' => array('checked' => TRUE),
1877
          ),
1878
        ),
1879
      );
1880

    
1881
      foreach ($this->plugin['view modes'] as $view_mode => $view_mode_info) {
1882
        if (isset($this->plugin['view mode status'][$bundle][$view_mode]) && empty($this->plugin['view mode status'][$bundle][$view_mode])) {
1883
          continue;
1884
        }
1885

    
1886
        $base_id = str_replace(array('][', '_', ' '), '-', '#edit-entities-' . $this->entity_type . '-' . $bundle . '-' . $view_mode);
1887
        $base_url = 'admin/config/content/panelizer/' . $this->entity_type . '/' . $bundle . '.' . $view_mode;
1888

    
1889
        if (!empty($this->plugin['bundles'][$bundle]['view modes'][$view_mode]) && is_array($this->plugin['bundles'][$bundle]['view modes'][$view_mode])) {
1890
          $settings = $this->plugin['bundles'][$bundle]['view modes'][$view_mode];
1891
        }
1892
        else {
1893
          $settings = array(
1894
            'status' => FALSE,
1895
            'default' => FALSE,
1896
            'choice' => FALSE
1897
          );
1898
        }
1899

    
1900
        if (empty($view_mode_info['panelizer special'])) {
1901
          $form['entities'][$this->entity_type][$bundle][$view_mode]['title'] = array(
1902
            '#markup' => '&nbsp;&nbsp;&nbsp;&nbsp;' . $view_mode_info['label'],
1903
          );
1904
        }
1905
        else {
1906
          $form['entities'][$this->entity_type][$bundle][$view_mode]['title'] = array(
1907
            '#markup' => '<strong>' . $bundle_info['label'] . '</strong>',
1908
          );
1909
        }
1910

    
1911
        $form['entities'][$this->entity_type][$bundle][$view_mode]['status'] = array(
1912
          '#type' => 'checkbox',
1913
          '#default_value' => !empty($settings['status']),
1914
          '#states' => array(
1915
            'visible' => array(
1916
              $bundle_id . '-status' => array('checked' => TRUE),
1917
            ),
1918
          ),
1919
        );
1920

    
1921
        $form['entities'][$this->entity_type][$bundle][$view_mode]['default'] = array(
1922
          '#type' => 'checkbox',
1923
          '#default_value' => !empty($settings['default']),
1924
          '#states' => array(
1925
            'visible' => array(
1926
              $bundle_id . '-status' => array('checked' => TRUE),
1927
              $base_id . '-status' => array('checked' => TRUE),
1928
            ),
1929
          ),
1930
        );
1931

    
1932
        $form['entities'][$this->entity_type][$bundle][$view_mode]['choice'] = array(
1933
          '#type' => 'checkbox',
1934
          '#default_value' => !empty($settings['choice']),
1935
          '#states' => array(
1936
            'visible' => array(
1937
              $bundle_id . '-status' => array('checked' => TRUE),
1938
              $base_id . '-status' => array('checked' => TRUE),
1939
            ),
1940
          ),
1941
        );
1942

    
1943
        $form['entities'][$this->entity_type][$bundle][$view_mode]['links'] = array(
1944
          '#prefix' => '<div class="container-inline">',
1945
          '#suffix' => '</div>',
1946
        );
1947

    
1948
        // Panelize is enabled and a default panel will be provided
1949
        if (!empty($settings['status']) && !empty($settings['default']) && empty($settings['choice'])) {
1950
          $links_array = array();
1951
          foreach (panelizer_operations() as $path => $operation) {
1952
            $links_array[$path] = array(
1953
              'title' => $operation['link title'],
1954
              'href' => $base_url . '/' . $path,
1955
            );
1956
          }
1957

    
1958
          $links = theme('links', array(
1959
            'links' => $links_array,
1960
            'attributes' => array('class' => array('links', 'inline')),
1961
          ));
1962
        }
1963
        else {
1964
          $links = t('Save to access default panel');
1965
        }
1966

    
1967
        $form['entities'][$this->entity_type][$bundle][$view_mode]['links']['default'] = array(
1968
          '#type' => 'item',
1969
          '#title' => $links,
1970
          '#states' => array(
1971
            'visible' => array(
1972
              $bundle_id . '-status' => array('checked' => TRUE),
1973
              $base_id . '-status' => array('checked' => TRUE),
1974
              $base_id . '-default' => array('checked' => TRUE),
1975
              $base_id . '-choice' => array('checked' => FALSE),
1976
            ),
1977
          ),
1978
        );
1979

    
1980
        if (!empty($settings['status']) && !empty($settings['choice'])) {
1981
          $links_array = array(
1982
            'list' => array(
1983
              'title' => t('list'),
1984
              'href' => $base_url . '/list',
1985
            ),
1986
          );
1987

    
1988
          $links = theme('links', array(
1989
            'links' => $links_array,
1990
            'attributes' => array('class' => array('links', 'inline')),
1991
          ));
1992
        }
1993
        else {
1994
          $links = t('Save to access panel list');
1995
        }
1996

    
1997
        $form['entities'][$this->entity_type][$bundle][$view_mode]['links']['default2'] = array(
1998
          '#type' => 'item',
1999
          '#title' => $links,
2000
          '#states' => array(
2001
            'visible' => array(
2002
              $bundle_id . '-status' => array('checked' => TRUE),
2003
              $base_id . '-status' => array('checked' => TRUE),
2004
              $base_id . '-choice' => array('checked' => TRUE),
2005
            ),
2006
          ),
2007
        );
2008
      }
2009
    }
2010
  }
2011

    
2012
  /**
2013
   * Validate entity specific settings on the Panelizer settings form.
2014
   */
2015
  public function settings_form_validate(&$form, &$form_state) {
2016

    
2017
  }
2018

    
2019
  /**
2020
   * Submit entity specific settings on the Panelizer settings form.
2021
   */
2022
  public function settings_form_submit(&$form, &$form_state) {
2023
    if (empty($form_state['values']['entities'][$this->entity_type])) {
2024
      return;
2025
    }
2026

    
2027
    foreach ($form_state['values']['entities'][$this->entity_type] as $bundle => $values) {
2028
      // Rewrite our settings because they're not quite in the right format in
2029
      // the form.
2030
      $settings = array('status' => $values[0]['status'], 'view modes' => array());
2031
      foreach ($values as $view_mode => $data) {
2032
        if ($view_mode) {
2033
          $settings['view modes'][$view_mode] = $data;
2034
        }
2035
      }
2036
      variable_set('panelizer_defaults_' . $this->entity_type . '_' . $bundle, $settings);
2037
    }
2038

    
2039
    // @todo if we enable caching of the plugins, which we should, this
2040
    // needs to clear that cache so they get reloaded.
2041
  }
2042

    
2043
  /**
2044
   * Render the panels display for a given panelizer entity.
2045
   *
2046
   * @param stdClass $entity
2047
   *   A fully-loaded entity object controlled by panelizer.
2048
   * @param array $args
2049
   *   Optional array of arguments to pass to the panels display.
2050
   * @param string $address
2051
   *   An optional address to send to the renderer to use for addressable
2052
   *   content.
2053
   *
2054
   * @return array
2055
   *   If the entity isn't panelized, this returns NULL. Otherwise, it returns an
2056
   *   associative array as meant for use with CTools with the following keys:
2057
   *   - 'content': String containing the rendered panels display output.
2058
   *   - 'no_blocks': Boolean defining if the panels display wants to hide core
2059
   *      blocks or not when being rendered.
2060
   */
2061
  function render_entity($entity, $view_mode, $langcode = NULL, $args = array(), $address = NULL) {
2062
    if (empty($entity->panelizer[$view_mode]) || empty($entity->panelizer[$view_mode]->display)) {
2063
      return FALSE;
2064
    }
2065
    list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
2066

    
2067
    $panelizer = $entity->panelizer[$view_mode];
2068
    $display = $panelizer->display;
2069

    
2070
    $display->context = $this->get_contexts($panelizer, $entity);
2071
    $display->args = $args;
2072
    $display->css_id = $panelizer->css_id;
2073

    
2074
    // This means the IPE will use our cache which means it will get appropriate
2075
    // allowed content should it be selected.
2076
    $display->cache_key = implode(':', array('panelizer', $this->entity_type, $entity_id, $view_mode));
2077

    
2078
    // Check to see if there is any CSS.
2079
    if (!empty($panelizer->css)) {
2080
      ctools_include('css');
2081
      $filename = ctools_css_retrieve($display->cache_key);
2082
      if (!$filename) {
2083
        $filename = ctools_css_store($display->cache_key, $panelizer->css);
2084
      }
2085
      drupal_add_css($filename, array('group' => CSS_THEME));
2086
    }
2087

    
2088
    if ($view_mode == 'page_manager') {
2089
      // We think this is handled as a page, so set the current page display.
2090
      panels_get_current_page_display($display);
2091
    }
2092

    
2093
    // Allow applications to alter the panelizer and the display before rendering them.
2094
    drupal_alter('panelizer_pre_render', $panelizer, $display, $entity);
2095

    
2096
    ctools_include('plugins', 'panels');
2097
    $renderer = panels_get_renderer($panelizer->pipeline, $display);
2098

    
2099
    // If the IPE is enabled, but the user does not have access to edit
2100
    // the entity, load the standard renderer instead.
2101

    
2102
    // use class_parents so we don't try to autoload the class we
2103
    // are testing.
2104
    $parents = class_parents($renderer);
2105
    if (!empty($parents['panels_renderer_editor']) && (!$this->panelizer_access('content', $entity, $view_mode) || !$this->entity_access('update', $entity))) {
2106
      $renderer = panels_get_renderer_handler('standard', $display);
2107
    }
2108

    
2109
    $renderer->address = $address;
2110

    
2111
    $info = array(
2112
      'content' => panels_render_display($display, $renderer),
2113
      'no_blocks' => !empty($panelizer->no_blocks),
2114
    );
2115

    
2116
    $info['classes_array'] = array();
2117

    
2118
    if (!empty($panelizer->css_class)) {
2119
      ctools_include('cleanstring');
2120
      foreach (explode(' ', $panelizer->css_class) as $class) {
2121
        $class = ctools_context_keyword_substitute($class, array(), $display->context);
2122
        if ($class) {
2123
          $info['classes_array'][] = ctools_cleanstring($class);
2124
        }
2125
      }
2126
    }
2127

    
2128
    if (!empty($parents['panels_renderer_editor'])) {
2129
      ctools_add_css('panelizer-ipe', 'panelizer');
2130
      ctools_add_js('panelizer-ipe', 'panelizer');
2131
      drupal_add_js(drupal_get_path('module', 'panelizer') . "/js/panelizer-ipe.js", array('group' => JS_LIBRARY));
2132
    }
2133

    
2134
    $info['title'] = $panelizer->display->get_title();
2135
    return $info;
2136
  }
2137

    
2138
  /**
2139
   * Fetch an object array of CTools contexts from panelizer information.
2140
   */
2141
  public function get_contexts($panelizer, $entity = NULL) {
2142
    ctools_include('context');
2143
    if (empty($panelizer->base_contexts)) {
2144
      $panelizer->base_contexts = $this->get_base_contexts($entity);
2145
    }
2146

    
2147
    $contexts = ctools_context_load_contexts($panelizer);
2148
    return $contexts;
2149
  }
2150

    
2151
  /**
2152
   * Callback to get the base context for a panelized entity
2153
   */
2154
  public function get_base_contexts($entity = NULL) {
2155
    ctools_include('context');
2156
    if ($entity) {
2157
      $context = ctools_context_create('entity:' . $this->entity_type, $entity);
2158
    }
2159
    else {
2160
      $context = ctools_context_create_empty('entity:' . $this->entity_type);
2161
      // The placeholder is needed to create the form used for the live
2162
      // preview.
2163
      $context->placeholder = array(
2164
        'type' => 'context',
2165
        'conf' => array(
2166
          'name' => $this->entity_type,
2167
          'identifier' => $this->entity_identifier($entity),
2168
          'keyword' => $this->entity_type,
2169
          'context_settings' => array(),
2170
        ),
2171
      );
2172
    }
2173

    
2174
    $context->identifier = $this->entity_identifier($entity);
2175
    $context->keyword = $this->entity_type;
2176
    return array('panelizer' => $context);
2177
  }
2178

    
2179
  /**
2180
   * Get the visible identifier if the identity.
2181
   *
2182
   * This is overridable because it can be a bit awkward using the
2183
   * default label.
2184
   */
2185
  public function entity_identifier($entity) {
2186
    $entity_info = entity_get_info($this->entity_type);
2187
    return t('This @entity', array('@entity' => $entity_info['label']));
2188
  }
2189

    
2190
  // Admin screens use a title callback for admin pages. This is used
2191
  // to fill in that title.
2192
  public function get_bundle_title($bundle) {
2193
    $entity_info = entity_get_info($this->entity_type);
2194

    
2195
    return isset($entity_info['bundles'][$bundle]['label']) ? $entity_info['bundles'][$bundle]['label'] : '';
2196
  }
2197

    
2198
  /**
2199
   * Get the name of bundles on the entity.
2200
   *
2201
   * Entity API doesn't give us a way to determine this, so the class must
2202
   * do this.
2203
   *
2204
   * @return
2205
   *   A translated, safe string.
2206
   */
2207
  public function entity_bundle_label() {
2208
    $entity_info = entity_get_info($this->entity_type);
2209
    return t('@entity bundle', array('@entity' => $entity_info['label']));
2210
  }
2211

    
2212
  /**
2213
   * Fetch the entity out of a build for hook_entity_view.
2214
   *
2215
   * @param $build
2216
   *   The render array that contains the entity.
2217
   */
2218
  public function get_entity_view_entity($build) {
2219
    $element = '#' . $this->entity_type;
2220
    if (isset($build[$element])) {
2221
      return $build[$element];
2222
    }
2223
    else if (isset($build['#entity'])) {
2224
      return $build['#entity'];
2225
    }
2226
  }
2227

    
2228
  /**
2229
   * Implement views support for panelizer entity types.
2230
   */
2231
  public function hook_views_data_alter(&$items) {
2232
    $entity_info = entity_get_info($this->entity_type);
2233
    if (!empty($entity_info['base table'])) {
2234
      $table = $entity_info['base table'];
2235
      $items[$table]['panelizer_link'] = array(
2236
        'field' => array(
2237
          'title' => t('Panelizer link'),
2238
          'help' => t('Provide a link to panelizer-related operations on the content.'),
2239
          'handler' => 'panelizer_handler_field_link',
2240
          'entity_type' => $this->entity_type,
2241
        ),
2242
      );
2243
      $items[$table]['panelizer_status'] = array(
2244
        'field' => array(
2245
          'title' => t('Panelizer status'),
2246
          'help' => t('Display whether an entity is panelized and which panelizer option it is using.'),
2247
          'handler' => 'panelizer_handler_panelizer_status',
2248
          'entity_type' => $this->entity_type,
2249
        ),
2250
      );
2251

    
2252
      // Join on revision id if possible or entity id if not.
2253
      if (!empty($entity_info['entity keys']['revision'])) {
2254
        $id_field = $entity_info['entity keys']['revision'];
2255
        $field = 'revision_id';
2256
      }
2257
      else {
2258
        $id_field = $entity_info['entity keys']['id'];
2259
        $field = 'entity_id';
2260
      }
2261

    
2262
      $items['panelizer_entity_' . $table]['table']['join'] = array(
2263
        $table => array(
2264
          'handler' => 'views_join',
2265
          'table' => 'panelizer_entity',
2266
          'left_table' => $table,
2267
          'left_field' => $id_field,
2268
          'field' => $field,
2269
          'extra' => array(array(
2270
            'field' => 'entity_type',
2271
            'value' => $this->entity_type,
2272
            'operator' => '=',
2273
          )),
2274
        ),
2275
      );
2276

    
2277
      $items['panelizer_entity_' . $table]['table']['group'] = $items[$table]['table']['group'];
2278

    
2279
      $items['panelizer_entity_' . $table]['name'] = array(
2280
        'filter' => array(
2281
          'title' => t('Panelizer status'),
2282
          'help' => t('Filter based upon panelizer status.'),
2283
          'handler' => 'panelizer_handler_filter_panelizer_status',
2284
          'entity_type' => $this->entity_type,
2285
        ),
2286
      );
2287
    }
2288
  }
2289

    
2290
  /**
2291
   * Preprocess the entity view mode template.
2292
   */
2293
  public function preprocess_panelizer_view_mode(&$vars, $entity, $element, $panelizer, $info) {
2294
    $vars['classes_array'][] = drupal_html_class($this->entity_type);
2295
    $vars['classes_array'][] = drupal_html_class($this->entity_type . '-' . $element['#view_mode']);
2296
    $vars['classes_array'][] = drupal_html_class($this->entity_type . '-' . $element['#panelizer_bundle']);
2297
    $vars['classes_array'][] = drupal_html_class($this->entity_type . '-' . $element['#panelizer_entity_id']);
2298
    if (!empty($entity->preview)) {
2299
      $vars['classes_array'][] = drupal_html_class($this->entity_type . '-preview');
2300
    }
2301

    
2302
    if (!empty($panelizer->title_element)) {
2303
      $vars['title_element'] = $panelizer->title_element;
2304
    }
2305
    else {
2306
      $vars['title_element'] = 'h2';
2307
    }
2308

    
2309
    $vars['content'] = $info['content'];
2310
    if (!empty($info['title'])) {
2311
      $vars['title'] = $info['title'];
2312
    }
2313

    
2314
    if (!empty($info['classes_array'])) {
2315
      $vars['classes_array'] = array_merge($vars['classes_array'], $info['classes_array']);
2316
    }
2317

    
2318
    if (!empty($panelizer->link_to_entity)) {
2319
      list($entity_id, $revision_id, $bundle) = entity_extract_ids($this->entity_type, $entity);
2320

    
2321
      $bits = explode('/', $this->plugin['entity path']);
2322
      foreach ($bits as $count => $bit) {
2323
        if (strpos($bit, '%') === 0) {
2324
          $bits[$count] = $entity_id;
2325
        }
2326
      }
2327
      $vars['entity_url'] = url(implode('/', $bits));
2328
    }
2329
  }
2330
}
2331

    
2332
function panelizer_entity_default_bundle_form_submit($form, &$form_state) {
2333
  $bundle = $form['panelizer']['#bundle'];
2334
  $type_location = $form['panelizer']['#location'];
2335
  $form_state['panelizer_entity_handler']->add_bundle_setting_form_submit($form, $form_state, $bundle, $type_location);
2336
}