Projet

Général

Profil

Paste
Télécharger (18 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / panels / panels_ipe / plugins / display_renderers / panels_renderer_ipe.class.php @ 64156087

1
<?php
2

    
3
/**
4
 * @file
5
 */
6

    
7
/**
8
 * Renderer class for all In-Place Editor (IPE) behavior.
9
 */
10
class panels_renderer_ipe extends panels_renderer_editor {
11
  // The IPE operates in normal render mode, not admin mode.
12
  var $admin = FALSE;
13

    
14
  // Whether or not the user has access.
15
  var $access = NULL;
16

    
17
  function invoke_panels_ipe_access() {
18
    if (user_access('bypass access in place editing')) {
19
      return TRUE;
20
    }
21
    // Modules can return TRUE, FALSE or NULL, for allowed, disallowed,
22
    // or don't care - respectively. On the first FALSE, we deny access,
23
    // otherwise allow.
24
    foreach (module_invoke_all('panels_ipe_access', $this->display) as $result) {
25
      if ($result === FALSE) {
26
        return FALSE;
27
      }
28
    }
29
    return TRUE;
30
  }
31

    
32
  function access() {
33
    if (is_null($this->access)) {
34
      $this->access = $this->invoke_panels_ipe_access();
35
    }
36
    return $this->access;
37
  }
38

    
39
  function render() {
40
    $output = parent::render();
41
    if ($this->access()) {
42
      return "<div id='panels-ipe-display-{$this->clean_key}' class='panels-ipe-display-container'>$output</div>";
43
    }
44
    return $output;
45
  }
46

    
47
  function add_meta() {
48
    if (!$this->access()) {
49
      return parent::add_meta();
50
    }
51

    
52
    ctools_include('display-edit', 'panels');
53
    ctools_include('content');
54

    
55
    if (empty($this->display->cache_key)) {
56
      $this->cache = panels_edit_cache_get_default($this->display);
57
    }
58
    // @todo we may need an else to load the cache, but I am not sure we
59
    // actually need to load it if we already have our cache key, and doing
60
    // so is a waste of resources.
61
    ctools_include('cleanstring');
62
    $this->clean_key = ctools_cleanstring($this->display->cache_key);
63
    $button = array(
64
      '#type' => 'link',
65
      '#title' => t('Customize this page'),
66
      '#href' => $this->get_url('save_form'),
67
      '#options' => array('query' => drupal_get_destination()),
68
      '#id' => 'panels-ipe-customize-page',
69
      '#attributes' => array(
70
        'class' => array('panels-ipe-startedit', 'panels-ipe-pseudobutton'),
71
      ),
72
      '#ajax' => array(
73
        'progress' => 'throbber',
74
        'ipe_cache_key' => $this->clean_key,
75
      ),
76
      '#prefix' => '<div class="panels-ipe-pseudobutton-container">',
77
      '#suffix' => '</div>',
78
    );
79

    
80
    panels_ipe_toolbar_add_button($this->display->cache_key, 'panels-ipe-startedit', $button);
81

    
82
    // @todo this actually should be an IPE setting instead.
83
    if (user_access('change layouts in place editing')) {
84
      $button = array(
85
        '#type' => 'link',
86
        '#title' => t('Change layout'),
87
        '#href' => $this->get_url('change_layout'),
88
        '#options' => array('query' => drupal_get_destination()),
89
        '#attributes' => array(
90
          'class' => array('panels-ipe-change-layout', 'panels-ipe-pseudobutton', 'ctools-modal-layout'),
91
        ),
92
        '#ajax' => array(
93
          'progress' => 'throbber',
94
          'ipe_cache_key' => $this->clean_key,
95
        ),
96

    
97
        '#prefix' => '<div class="panels-ipe-pseudobutton-container">',
98
        '#suffix' => '</div>',
99
      );
100

    
101
      panels_ipe_toolbar_add_button($this->display->cache_key, 'panels-ipe-change-layout', $button);
102
    }
103

    
104
    ctools_include('ajax');
105
    ctools_include('modal');
106
    ctools_modal_add_js();
107

    
108
    ctools_add_css('panels_dnd', 'panels');
109
    ctools_add_css('panels_admin', 'panels');
110
    ctools_add_js('panels-base', 'panels');
111
    ctools_add_js('panels_ipe', 'panels_ipe');
112
    ctools_add_css('panels_ipe', 'panels_ipe');
113

    
114
    drupal_add_js(array('PanelsIPECacheKeys' => array($this->clean_key)), 'setting');
115

    
116
    drupal_add_library('system', 'ui.draggable');
117
    drupal_add_library('system', 'ui.droppable');
118
    drupal_add_library('system', 'ui.sortable');
119

    
120
    parent::add_meta();
121
  }
122

    
123
  /**
124
   * Override & call the parent, then pass output through to the dnd wrapper
125
   * theme function.
126
   *
127
   * @param $pane
128
   */
129
  function render_pane(&$pane) {
130
    $output = parent::render_pane($pane);
131
    if (empty($output)) {
132
      return;
133
    }
134
    if (!$this->access()) {
135
      return $output;
136
    }
137

    
138
    // If there are region locks, add them.
139
    if (!empty($pane->locks['type']) && $pane->locks['type'] == 'regions') {
140
      static $key = NULL;
141
      $javascript = &drupal_static('drupal_add_js', array());
142

    
143
      // drupal_add_js breaks as we add these, but we can't just lump them
144
      // together because panes can be rendered independently. So game the system:
145
      if (empty($key)) {
146
        $settings['Panels']['RegionLock'][$pane->pid] = $pane->locks['regions'];
147
        drupal_add_js($settings, 'setting');
148

    
149
        // These are just added via [] so we have to grab the last one
150
        // and reference it.
151
        $keys = array_keys($javascript['settings']['data']);
152
        $key = end($keys);
153
      }
154
      else {
155
        $javascript['settings']['data'][$key]['Panels']['RegionLock'][$pane->pid] = $pane->locks['regions'];
156
      }
157

    
158
    }
159

    
160
    if (empty($pane->IPE_empty)) {
161
      // Add an inner layer wrapper to the pane content before placing it into
162
      // draggable portlet.
163
      $output = "<div class=\"panels-ipe-portlet-content\">$output</div>";
164
    }
165
    else {
166
      $output = "<div class=\"panels-ipe-portlet-content panels-ipe-empty-pane\">$output</div>";
167
    }
168
    // Hand it off to the plugin/theme for placing draggers/buttons.
169
    $output = theme('panels_ipe_pane_wrapper', array('output' => $output, 'pane' => $pane, 'display' => $this->display, 'renderer' => $this));
170

    
171
    if (!empty($pane->locks['type']) && $pane->locks['type'] == 'immovable') {
172
      return "<div id=\"panels-ipe-paneid-{$pane->pid}\" class=\"panels-ipe-nodrag panels-ipe-portlet-wrapper panels-ipe-portlet-marker\">" . $output . "</div>";
173
    }
174

    
175
    return "<div id=\"panels-ipe-paneid-{$pane->pid}\" class=\"panels-ipe-portlet-wrapper panels-ipe-portlet-marker\">" . $output . "</div>";
176
  }
177

    
178
  function prepare_panes($panes) {
179
    if (!$this->access()) {
180
      return parent::prepare_panes($panes);
181
    }
182

    
183
    // Set to admin mode just for this to ensure all panes are represented.
184
    $this->admin = TRUE;
185
    $panes = parent::prepare_panes($panes);
186
    $this->admin = FALSE;
187
  }
188

    
189
  function render_pane_content(&$pane) {
190
    if (!$this->access()) {
191
      return parent::render_pane_content($pane);
192
    }
193

    
194
    if (!empty($pane->shown) && panels_pane_access($pane, $this->display)) {
195
      $content = parent::render_pane_content($pane);
196
    }
197
    // Ensure that empty panes have some content.
198
    if (empty($content) || empty($content->content)) {
199
      if (empty($content)) {
200
        $content = new stdClass();
201
      }
202

    
203
      // Get the administrative title.
204
      $content_type = ctools_get_content_type($pane->type);
205
      $title = ctools_content_admin_title($content_type, $pane->subtype, $pane->configuration, $this->display->context);
206

    
207
      $content->content = t('Placeholder for empty or inaccessible "@title"', array('@title' => html_entity_decode($title, ENT_QUOTES)));
208
      // Add these to prevent notices.
209
      $content->type = 'panels_ipe';
210
      $content->subtype = 'panels_ipe';
211
      $pane->IPE_empty = TRUE;
212
    }
213

    
214
    return $content;
215
  }
216

    
217
  /**
218
   * Add an 'empty' pane placeholder above all the normal panes.
219
   *
220
   * @param $region_id
221
   * @param $panes
222
   */
223
  function render_region($region_id, $panes) {
224
    if (!$this->access()) {
225
      return parent::render_region($region_id, $panes);
226
    }
227

    
228
    // Generate this region's 'empty' placeholder pane from the IPE plugin.
229
    $empty_ph = theme('panels_ipe_placeholder_pane', array('region_id' => $region_id, 'region_title' => $this->plugins['layout']['regions'][$region_id]));
230

    
231
    // Wrap the placeholder in some guaranteed markup.
232
    $control = '<div class="panels-ipe-placeholder panels-ipe-on panels-ipe-portlet-marker panels-ipe-portlet-static">' . $empty_ph . theme('panels_ipe_add_pane_button', array('region_id' => $region_id, 'display' => $this->display, 'renderer' => $this)) . "</div>";
233

    
234
    $output = parent::render_region($region_id, $panes);
235
    $output = theme('panels_ipe_region_wrapper', array('output' => $output, 'region_id' => $region_id, 'display' => $this->display, 'controls' => $control, 'renderer' => $this));
236
    $classes = 'panels-ipe-region';
237

    
238
    return "<div id='panels-ipe-regionid-$region_id' class='panels-ipe-region'>$output</div>";
239
  }
240

    
241
  /**
242
   * This is a generic lock test.
243
   */
244
  function ipe_test_lock($url, $break) {
245
    if (!empty($this->cache->locked)) {
246
      if ($break != 'break') {
247
        $account  = user_load($this->cache->locked->uid);
248
        $name     = format_username($account);
249
        $lock_age = format_interval(time() - $this->cache->locked->updated);
250

    
251
        $message = t("This panel is being edited by user !user, and is therefore locked from editing by others. This lock is !age old.\n\nClick OK to break this lock and discard any changes made by !user.", array('!user' => $name, '!age' => $lock_age));
252

    
253
        $this->commands[] = array(
254
          'command' => 'unlockIPE',
255
          'message' => $message,
256
          'break_path' => url($this->get_url($url, 'break')),
257
          'key' => $this->clean_key,
258
        );
259
        return TRUE;
260
      }
261

    
262
      // Break the lock.
263
      panels_edit_cache_break_lock($this->cache);
264
    }
265
  }
266

    
267
  /**
268
   *
269
   */
270
  function get_panels_storage_op_for_ajax($method) {
271
    switch ($method) {
272
      case 'ajax_unlock_ipe':
273
      case 'ajax_save_form':
274
        return 'update';
275

    
276
      case 'ajax_change_layout':
277
      case 'ajax_set_layout':
278
        return 'change layout';
279
    }
280

    
281
    return parent::get_panels_storage_op_for_ajax($method);
282
  }
283

    
284
  /**
285
   * AJAX callback to unlock the IPE.
286
   *
287
   * This is called whenever something server side determines that editing
288
   * has stopped and cleans up no longer needed locks.
289
   *
290
   * It has no visible return value as this is considered a background task
291
   * and the client side has already given all indications that things are
292
   * now in a 'normal' state.
293
   */
294
  function ajax_unlock_ipe() {
295
    panels_edit_cache_clear($this->cache);
296
    $this->commands[] = array();
297
  }
298

    
299
  /**
300
   * AJAX entry point to create the controller form for an IPE.
301
   */
302
  function ajax_save_form($break = NULL) {
303
    if ($this->ipe_test_lock('save-form', $break)) {
304
      return;
305
    }
306

    
307
    // Reset the $_POST['ajax_html_ids'] values to preserve
308
    // proper IDs on form elements when they are rebuilt
309
    // by the Panels IPE without refreshing the page.
310
    $_POST['ajax_html_ids'] = array();
311

    
312
    $form_state = array(
313
      'renderer' => $this,
314
      'display' => &$this->display,
315
      'content_types' => $this->cache->content_types,
316
      'rerender' => FALSE,
317
      'no_redirect' => TRUE,
318
      // Panels needs this to make sure that the layout gets callbacks.
319
      'layout' => $this->plugins['layout'],
320
    );
321

    
322
    $output = drupal_build_form('panels_ipe_edit_control_form', $form_state);
323
    if (empty($form_state['executed'])) {
324
      // At this point, we want to save the cache to ensure that we have a lock.
325
      $this->cache->ipe_locked = TRUE;
326
      panels_edit_cache_set($this->cache);
327
      $this->commands[] = array(
328
        'command' => 'initIPE',
329
        'key' => $this->clean_key,
330
        'data' => drupal_render($output),
331
        'lockPath' => url($this->get_url('unlock_ipe')),
332
      );
333
      return;
334
    }
335

    
336
    // Check to see if we have a lock that was broken. If so we need to
337
    // inform the user and abort.
338
    if (empty($this->cache->ipe_locked)) {
339
      $this->commands[] = ajax_command_alert(t('A lock you had has been externally broken, and all your changes have been reverted.'));
340
      $this->commands[] = array(
341
        'command' => 'cancelIPE',
342
        'key' => $this->clean_key,
343
      );
344
      return;
345
    }
346

    
347
    // Otherwise it was submitted.
348
    if (!empty($form_state['clicked_button']['#save-display'])) {
349
      // Saved. Save the cache.
350
      panels_edit_cache_save($this->cache);
351
      // A rerender should fix IDs on added panes as well as ensure style changes are
352
      // rendered.
353
      $this->meta_location = 'inline';
354
      $this->commands[] = ajax_command_replace("#panels-ipe-display-{$this->clean_key}", panels_render_display($this->display, $this));
355
      $buttons = &drupal_static('panels_ipe_toolbar_buttons', array());
356
      $output = theme('panels_ipe_toolbar', array('buttons' => $buttons));
357
      $this->commands[] = ajax_command_replace('#panels-ipe-control-container', $output);
358

    
359
      $storage_id = $this->cache->display->storage_id;
360
      cache_clear_all('panels_mini_load:' . $storage_id, 'cache_panels', TRUE);
361
    }
362
    else {
363
      // Cancelled. Clear the cache.
364
      panels_edit_cache_clear($this->cache);
365
    }
366

    
367
    $this->commands[] = array(
368
      'command' => 'endIPE',
369
      'key' => $this->clean_key,
370
    );
371
  }
372

    
373
  /**
374
   * AJAX entry point to create the controller form for an IPE.
375
   */
376
  function ajax_change_layout($break = NULL) {
377
    if ($this->ipe_test_lock('change_layout', $break)) {
378
      return;
379
    }
380

    
381
    // At this point, we want to save the cache to ensure that we have a lock.
382
    $this->cache->ipe_locked = TRUE;
383
    panels_edit_cache_set($this->cache);
384

    
385
    ctools_include('plugins', 'panels');
386
    ctools_include('common', 'panels');
387

    
388
    // @todo figure out a solution for this, it's critical
389
    if (isset($this->display->allowed_layouts)) {
390
      $layouts = $this->display->allowed_layouts;
391
    }
392
    else {
393
      $layouts = panels_common_get_allowed_layouts('panels_page');
394
    }
395

    
396
    // Filter out builders.
397
    $layouts = array_filter($layouts, '_panels_builder_filter');
398

    
399
    // Let other modules filter the layouts.
400
    drupal_alter('panels_layouts_available', $layouts);
401

    
402
    // Define the current layout.
403
    $current_layout = $this->plugins['layout']['name'];
404

    
405
    $output = panels_common_print_layout_links($layouts, $this->get_url('set_layout'), array('attributes' => array('class' => array('use-ajax'))), $current_layout);
406

    
407
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
408
    $this->commands[] = array(
409
      'command' => 'IPEsetLockState',
410
      'key' => $this->clean_key,
411
      'lockPath' => url($this->get_url('unlock_ipe')),
412
    );
413
  }
414

    
415
  function ajax_set_layout($layout) {
416
    ctools_include('context');
417
    ctools_include('display-layout', 'panels');
418
    $form_state = array(
419
      'layout' => $layout,
420
      'display' => $this->display,
421
      'finish' => t('Save'),
422
      'no_redirect' => TRUE,
423
    );
424

    
425
    // Reset the $_POST['ajax_html_ids'] values to preserve
426
    // proper IDs on form elements when they are rebuilt
427
    // by the Panels IPE without refreshing the page.
428
    $_POST['ajax_html_ids'] = array();
429

    
430
    $output = drupal_build_form('panels_change_layout', $form_state);
431
    $output = drupal_render($output);
432
    if (!empty($form_state['executed'])) {
433
      if (isset($form_state['back'])) {
434
        return $this->ajax_change_layout();
435
      }
436

    
437
      if (!empty($form_state['clicked_button']['#save-display'])) {
438
        // Saved. Save the cache.
439
        panels_edit_cache_save($this->cache);
440
        $this->display->skip_cache = TRUE;
441

    
442
        // Since the layout changed, we have to update these things in the
443
        // renderer in order to get the right settings.
444
        $layout = panels_get_layout($this->display->layout);
445
        $this->plugins['layout'] = $layout;
446
        if (!isset($layout['regions'])) {
447
          $this->plugins['layout']['regions'] = panels_get_regions($layout, $this->display);
448
        }
449

    
450
        $this->meta_location = 'inline';
451

    
452
        $this->commands[] = ajax_command_replace("#panels-ipe-display-{$this->clean_key}", panels_render_display($this->display, $this));
453
        $this->commands[] = ctools_modal_command_dismiss();
454
        return;
455
      }
456
    }
457

    
458
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
459
  }
460

    
461
  /**
462
   * Create a command array to redraw a pane.
463
   */
464
  function command_update_pane($pid) {
465
    if (is_object($pid)) {
466
      $pane = $pid;
467
    }
468
    else {
469
      $pane = $this->display->content[$pid];
470
    }
471

    
472
    $this->commands[] = ajax_command_replace("#panels-ipe-paneid-$pane->pid", $this->render_pane($pane));
473
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
474
  }
475

    
476
  /**
477
   * Create a command array to add a new pane.
478
   */
479
  function command_add_pane($pid) {
480
    if (is_object($pid)) {
481
      $pane = $pid;
482
    }
483
    else {
484
      $pane = $this->display->content[$pid];
485
    }
486

    
487
    $this->commands[] = array(
488
      'command' => 'insertNewPane',
489
      'regionId' => $pane->panel,
490
      'renderedPane' => $this->render_pane($pane),
491
    );
492
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
493
    $this->commands[] = array(
494
      'command' => 'addNewPane',
495
      'key' => $this->clean_key,
496
    );
497
  }
498

    
499
}
500

    
501
/**
502
 * FAPI callback to create the Save/Cancel form for the IPE.
503
 */
504
function panels_ipe_edit_control_form($form, &$form_state) {
505
  $display = &$form_state['display'];
506
  // @todo -- this should be unnecessary as we ensure cache_key is set in add_meta()
507
  //   $display->cache_key = isset($display->cache_key) ? $display->cache_key : $display->did;
508
  // Annoyingly, theme doesn't have access to form_state so we have to do this.
509
  $form['#display'] = $display;
510

    
511
  $layout = panels_get_layout($display->layout);
512
  $layout_panels = panels_get_regions($layout, $display);
513

    
514
  $form['panel'] = array('#tree' => TRUE);
515
  $form['panel']['pane'] = array('#tree' => TRUE);
516

    
517
  foreach ($layout_panels as $panel_id => $title) {
518
    // Make sure we at least have an empty array for all possible locations.
519
    if (!isset($display->panels[$panel_id])) {
520
      $display->panels[$panel_id] = array();
521
    }
522

    
523
    $form['panel']['pane'][$panel_id] = array(
524
      // Use 'hidden' instead of 'value' so the js can access it.
525
      '#type' => 'hidden',
526
      '#default_value' => implode(',', (array) $display->panels[$panel_id]),
527
    );
528
  }
529

    
530
  $form['buttons']['submit'] = array(
531
    '#type' => 'submit',
532
    '#value' => t('Save'),
533
    '#id' => 'panels-ipe-save',
534
    '#attributes' => array('class' => array('panels-ipe-save')),
535
    '#submit' => array('panels_edit_display_form_submit'),
536
    '#save-display' => TRUE,
537
  );
538
  $form['buttons']['cancel'] = array(
539
    '#type' => 'submit',
540
    '#id' => 'panels-ipe-cancel',
541
    '#attributes' => array('class' => array('panels-ipe-cancel')),
542
    '#value' => t('Cancel'),
543
  );
544
  return $form;
545
}