Projet

Général

Profil

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

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

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
    // Temporarily change $_GET['q'] so that panes think the current path is
131
    // the original path when rendering.
132
    $ajax_path = $_GET['q'];
133
    if (!empty($_GET['destination'])) {
134
      $_GET['q'] = $_GET['destination'];
135
    }
136

    
137
    $output = parent::render_pane($pane);
138

    
139
    // Reset $_GET['q'] to the AJAX path.
140
    $_GET['q'] = $ajax_path;
141

    
142
    if (empty($output)) {
143
      return;
144
    }
145
    if (!$this->access()) {
146
      return $output;
147
    }
148

    
149
    // If there are region locks, add them.
150
    if (!empty($pane->locks['type']) && $pane->locks['type'] == 'regions') {
151
      static $key = NULL;
152
      $javascript = &drupal_static('drupal_add_js', array());
153

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

    
160
        // These are just added via [] so we have to grab the last one
161
        // and reference it.
162
        $keys = array_keys($javascript['settings']['data']);
163
        $key = end($keys);
164
      }
165
      else {
166
        $javascript['settings']['data'][$key]['Panels']['RegionLock'][$pane->pid] = $pane->locks['regions'];
167
      }
168

    
169
    }
170

    
171
    if (empty($pane->IPE_empty)) {
172
      // Add an inner layer wrapper to the pane content before placing it into
173
      // draggable portlet.
174
      $output = "<div class=\"panels-ipe-portlet-content\">$output</div>";
175
    }
176
    else {
177
      $output = "<div class=\"panels-ipe-portlet-content panels-ipe-empty-pane\">$output</div>";
178
    }
179
    // Hand it off to the plugin/theme for placing draggers/buttons.
180
    $output = theme('panels_ipe_pane_wrapper', array('output' => $output, 'pane' => $pane, 'display' => $this->display, 'renderer' => $this));
181

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

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

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

    
194
    // Set to admin mode just for this to ensure all panes are represented.
195
    $this->admin = TRUE;
196
    $panes = parent::prepare_panes($panes);
197
    $this->admin = FALSE;
198
  }
199

    
200
  function render_pane_content(&$pane) {
201
    if (!$this->access()) {
202
      return parent::render_pane_content($pane);
203
    }
204

    
205
    if (!empty($pane->shown) && panels_pane_access($pane, $this->display)) {
206
      $content = parent::render_pane_content($pane);
207
    }
208
    // Ensure that empty panes have some content.
209
    if (empty($content) || empty($content->content)) {
210
      if (empty($content)) {
211
        $content = new stdClass();
212
      }
213

    
214
      // Get the administrative title.
215
      $content_type = ctools_get_content_type($pane->type);
216
      $title = ctools_content_admin_title($content_type, $pane->subtype, $pane->configuration, $this->display->context);
217

    
218
      $content->content = t('Placeholder for empty or inaccessible "@title"', array('@title' => html_entity_decode($title, ENT_QUOTES)));
219
      // Add these to prevent notices.
220
      $content->type = 'panels_ipe';
221
      $content->subtype = 'panels_ipe';
222
      $pane->IPE_empty = TRUE;
223
    }
224

    
225
    return $content;
226
  }
227

    
228
  /**
229
   * Add an 'empty' pane placeholder above all the normal panes.
230
   *
231
   * @param $region_id
232
   * @param $panes
233
   */
234
  function render_region($region_id, $panes) {
235
    if (!$this->access()) {
236
      return parent::render_region($region_id, $panes);
237
    }
238

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

    
242
    // Wrap the placeholder in some guaranteed markup.
243
    $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>";
244

    
245
    $output = parent::render_region($region_id, $panes);
246
    $output = theme('panels_ipe_region_wrapper', array('output' => $output, 'region_id' => $region_id, 'display' => $this->display, 'controls' => $control, 'renderer' => $this));
247
    $classes = 'panels-ipe-region';
248

    
249
    return "<div id='panels-ipe-regionid-$region_id' class='panels-ipe-region'>$output</div>";
250
  }
251

    
252
  /**
253
   * This is a generic lock test.
254
   */
255
  function ipe_test_lock($url, $break) {
256
    if (!empty($this->cache->locked)) {
257
      if ($break != 'break') {
258
        $account  = user_load($this->cache->locked->uid);
259
        $name     = format_username($account);
260
        $lock_age = format_interval(time() - $this->cache->locked->updated);
261

    
262
        $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));
263

    
264
        $this->commands[] = array(
265
          'command' => 'unlockIPE',
266
          'message' => $message,
267
          'break_path' => url($this->get_url($url, 'break')),
268
          'key' => $this->clean_key,
269
        );
270
        return TRUE;
271
      }
272

    
273
      // Break the lock.
274
      panels_edit_cache_break_lock($this->cache);
275
    }
276
  }
277

    
278
  /**
279
   *
280
   */
281
  function get_panels_storage_op_for_ajax($method) {
282
    switch ($method) {
283
      case 'ajax_unlock_ipe':
284
      case 'ajax_save_form':
285
        return 'update';
286

    
287
      case 'ajax_change_layout':
288
      case 'ajax_set_layout':
289
        return 'change layout';
290
    }
291

    
292
    return parent::get_panels_storage_op_for_ajax($method);
293
  }
294

    
295
  /**
296
   * AJAX callback to unlock the IPE.
297
   *
298
   * This is called whenever something server side determines that editing
299
   * has stopped and cleans up no longer needed locks.
300
   *
301
   * It has no visible return value as this is considered a background task
302
   * and the client side has already given all indications that things are
303
   * now in a 'normal' state.
304
   */
305
  function ajax_unlock_ipe() {
306
    panels_edit_cache_clear($this->cache);
307
    $this->commands[] = array();
308
  }
309

    
310
  /**
311
   * AJAX entry point to create the controller form for an IPE.
312
   */
313
  function ajax_save_form($break = NULL) {
314
    if ($this->ipe_test_lock('save-form', $break)) {
315
      return;
316
    }
317

    
318
    // Reset the $_POST['ajax_html_ids'] values to preserve
319
    // proper IDs on form elements when they are rebuilt
320
    // by the Panels IPE without refreshing the page.
321
    $_POST['ajax_html_ids'] = array();
322

    
323
    $form_state = array(
324
      'renderer' => $this,
325
      'display' => &$this->display,
326
      'content_types' => $this->cache->content_types,
327
      'rerender' => FALSE,
328
      'no_redirect' => TRUE,
329
      // Panels needs this to make sure that the layout gets callbacks.
330
      'layout' => $this->plugins['layout'],
331
    );
332

    
333
    $output = drupal_build_form('panels_ipe_edit_control_form', $form_state);
334
    if (empty($form_state['executed'])) {
335
      // At this point, we want to save the cache to ensure that we have a lock.
336
      $this->cache->ipe_locked = TRUE;
337
      panels_edit_cache_set($this->cache);
338
      $this->commands[] = array(
339
        'command' => 'initIPE',
340
        'key' => $this->clean_key,
341
        'data' => drupal_render($output),
342
        'lockPath' => url($this->get_url('unlock_ipe')),
343
      );
344
      return;
345
    }
346

    
347
    // Check to see if we have a lock that was broken. If so we need to
348
    // inform the user and abort.
349
    if (empty($this->cache->ipe_locked)) {
350
      $this->commands[] = ajax_command_alert(t('A lock you had has been externally broken, and all your changes have been reverted.'));
351
      $this->commands[] = array(
352
        'command' => 'cancelIPE',
353
        'key' => $this->clean_key,
354
      );
355
      return;
356
    }
357

    
358
    // Otherwise it was submitted.
359
    if (!empty($form_state['clicked_button']['#save-display'])) {
360
      // Saved. Save the cache.
361
      panels_edit_cache_save($this->cache);
362
      // A rerender should fix IDs on added panes as well as ensure style changes are
363
      // rendered.
364
      $this->meta_location = 'inline';
365
      $this->commands[] = ajax_command_replace("#panels-ipe-display-{$this->clean_key}", panels_render_display($this->display, $this));
366
      $buttons = &drupal_static('panels_ipe_toolbar_buttons', array());
367
      $output = theme('panels_ipe_toolbar', array('buttons' => $buttons));
368
      $this->commands[] = ajax_command_replace('#panels-ipe-control-container', $output);
369

    
370
      $storage_id = $this->cache->display->storage_id;
371
      cache_clear_all('panels_mini_load:' . $storage_id, 'cache_panels', TRUE);
372
    }
373
    else {
374
      // Cancelled. Clear the cache.
375
      panels_edit_cache_clear($this->cache);
376
    }
377

    
378
    $this->commands[] = array(
379
      'command' => 'endIPE',
380
      'key' => $this->clean_key,
381
    );
382
  }
383

    
384
  /**
385
   * AJAX entry point to create the controller form for an IPE.
386
   */
387
  function ajax_change_layout($break = NULL) {
388
    if ($this->ipe_test_lock('change_layout', $break)) {
389
      return;
390
    }
391

    
392
    // At this point, we want to save the cache to ensure that we have a lock.
393
    $this->cache->ipe_locked = TRUE;
394
    panels_edit_cache_set($this->cache);
395

    
396
    ctools_include('plugins', 'panels');
397
    ctools_include('common', 'panels');
398

    
399
    // @todo figure out a solution for this, it's critical
400
    if (isset($this->display->allowed_layouts)) {
401
      $layouts = $this->display->allowed_layouts;
402
    }
403
    else {
404
      $layouts = panels_common_get_allowed_layouts('panels_page');
405
    }
406

    
407
    // Filter out builders.
408
    $layouts = array_filter($layouts, '_panels_builder_filter');
409

    
410
    // Let other modules filter the layouts.
411
    drupal_alter('panels_layouts_available', $layouts);
412

    
413
    // Define the current layout.
414
    $current_layout = $this->plugins['layout']['name'];
415

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

    
418
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
419
    $this->commands[] = array(
420
      'command' => 'IPEsetLockState',
421
      'key' => $this->clean_key,
422
      'lockPath' => url($this->get_url('unlock_ipe')),
423
    );
424
  }
425

    
426
  function ajax_set_layout($layout) {
427
    ctools_include('context');
428
    ctools_include('display-layout', 'panels');
429
    $form_state = array(
430
      'layout' => $layout,
431
      'display' => $this->display,
432
      'finish' => t('Save'),
433
      'no_redirect' => TRUE,
434
    );
435

    
436
    // Reset the $_POST['ajax_html_ids'] values to preserve
437
    // proper IDs on form elements when they are rebuilt
438
    // by the Panels IPE without refreshing the page.
439
    $_POST['ajax_html_ids'] = array();
440

    
441
    $output = drupal_build_form('panels_change_layout', $form_state);
442
    $output = drupal_render($output);
443
    if (!empty($form_state['executed'])) {
444
      if (isset($form_state['back'])) {
445
        return $this->ajax_change_layout();
446
      }
447

    
448
      if (!empty($form_state['clicked_button']['#save-display'])) {
449
        // Saved. Save the cache.
450
        panels_edit_cache_save($this->cache);
451
        $this->display->skip_cache = TRUE;
452

    
453
        // Since the layout changed, we have to update these things in the
454
        // renderer in order to get the right settings.
455
        $layout = panels_get_layout($this->display->layout);
456
        $this->plugins['layout'] = $layout;
457
        if (!isset($layout['regions'])) {
458
          $this->plugins['layout']['regions'] = panels_get_regions($layout, $this->display);
459
        }
460

    
461
        $this->meta_location = 'inline';
462

    
463
        $this->commands[] = ajax_command_replace("#panels-ipe-display-{$this->clean_key}", panels_render_display($this->display, $this));
464
        $this->commands[] = ctools_modal_command_dismiss();
465
        return;
466
      }
467
    }
468

    
469
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
470
  }
471

    
472
  /**
473
   * Create a command array to redraw a pane.
474
   */
475
  function command_update_pane($pid) {
476
    if (is_object($pid)) {
477
      $pane = $pid;
478
    }
479
    else {
480
      $pane = $this->display->content[$pid];
481
    }
482

    
483
    $this->commands[] = ajax_command_replace("#panels-ipe-paneid-$pane->pid", $this->render_pane($pane));
484
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
485
  }
486

    
487
  /**
488
   * Create a command array to add a new pane.
489
   */
490
  function command_add_pane($pid) {
491
    if (is_object($pid)) {
492
      $pane = $pid;
493
    }
494
    else {
495
      $pane = $this->display->content[$pid];
496
    }
497

    
498
    $this->commands[] = array(
499
      'command' => 'insertNewPane',
500
      'regionId' => $pane->panel,
501
      'renderedPane' => $this->render_pane($pane),
502
    );
503
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
504
    $this->commands[] = array(
505
      'command' => 'addNewPane',
506
      'key' => $this->clean_key,
507
    );
508
  }
509

    
510
}
511

    
512
/**
513
 * FAPI callback to create the Save/Cancel form for the IPE.
514
 */
515
function panels_ipe_edit_control_form($form, &$form_state) {
516
  $display = &$form_state['display'];
517
  // @todo -- this should be unnecessary as we ensure cache_key is set in add_meta()
518
  //   $display->cache_key = isset($display->cache_key) ? $display->cache_key : $display->did;
519
  // Annoyingly, theme doesn't have access to form_state so we have to do this.
520
  $form['#display'] = $display;
521

    
522
  $layout = panels_get_layout($display->layout);
523
  $layout_panels = panels_get_regions($layout, $display);
524

    
525
  $form['panel'] = array('#tree' => TRUE);
526
  $form['panel']['pane'] = array('#tree' => TRUE);
527

    
528
  foreach ($layout_panels as $panel_id => $title) {
529
    // Make sure we at least have an empty array for all possible locations.
530
    if (!isset($display->panels[$panel_id])) {
531
      $display->panels[$panel_id] = array();
532
    }
533

    
534
    $form['panel']['pane'][$panel_id] = array(
535
      // Use 'hidden' instead of 'value' so the js can access it.
536
      '#type' => 'hidden',
537
      '#default_value' => implode(',', (array) $display->panels[$panel_id]),
538
    );
539
  }
540

    
541
  $form['buttons']['submit'] = array(
542
    '#type' => 'submit',
543
    '#value' => t('Save'),
544
    '#id' => 'panels-ipe-save',
545
    '#attributes' => array('class' => array('panels-ipe-save')),
546
    '#submit' => array('panels_edit_display_form_submit'),
547
    '#save-display' => TRUE,
548
  );
549
  $form['buttons']['cancel'] = array(
550
    '#type' => 'submit',
551
    '#id' => 'panels-ipe-cancel',
552
    '#attributes' => array('class' => array('panels-ipe-cancel')),
553
    '#value' => t('Cancel'),
554
  );
555
  return $form;
556
}