Projet

Général

Profil

Paste
Télécharger (17,8 ko) Statistiques
| Branche: | Révision:

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

1
<?php
2

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

    
10
  // Whether or not the user has access.
11
  var $access = NULL;
12

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

    
28
  function access() {
29
    if (is_null($this->access)) {
30
      $this->access = $this->invoke_panels_ipe_access();
31
    }
32
    return $this->access;
33
  }
34

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

    
43
  function add_meta() {
44
    if (!$this->access()) {
45
      return parent::add_meta();
46
    }
47

    
48
    ctools_include('display-edit', 'panels');
49
    ctools_include('content');
50

    
51
    if (empty($this->display->cache_key)) {
52
      $this->cache = panels_edit_cache_get_default($this->display);
53
    }
54
    // @todo we may need an else to load the cache, but I am not sure we
55
    // actually need to load it if we already have our cache key, and doing
56
    // so is a waste of resources.
57

    
58
    ctools_include('cleanstring');
59
    $this->clean_key = ctools_cleanstring($this->display->cache_key);
60
    $button = array(
61
      '#type' => 'link',
62
      '#title' => t('Customize this page'),
63
      '#href' => $this->get_url('save_form'),
64
      '#options' => array('query' => drupal_get_destination()),
65
      '#id' => 'panels-ipe-customize-page',
66
      '#attributes' => array(
67
        'class' => array('panels-ipe-startedit', 'panels-ipe-pseudobutton'),
68
      ),
69
      '#ajax' => array(
70
        'progress' => 'throbber',
71
        'ipe_cache_key' => $this->clean_key,
72
      ),
73
      '#prefix' => '<div class="panels-ipe-pseudobutton-container">',
74
      '#suffix' => '</div>',
75
    );
76

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

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

    
94
      '#prefix' => '<div class="panels-ipe-pseudobutton-container">',
95
      '#suffix' => '</div>',
96
      );
97

    
98
      panels_ipe_toolbar_add_button($this->display->cache_key, 'panels-ipe-change-layout', $button);
99
    }
100

    
101
    ctools_include('ajax');
102
    ctools_include('modal');
103
    ctools_modal_add_js();
104

    
105
    ctools_add_css('panels_dnd', 'panels');
106
    ctools_add_css('panels_admin', 'panels');
107
    ctools_add_js('panels-base', 'panels');
108
    ctools_add_js('panels_ipe', 'panels_ipe');
109
    ctools_add_css('panels_ipe', 'panels_ipe');
110

    
111
    drupal_add_js(array('PanelsIPECacheKeys' => array($this->clean_key)), 'setting');
112

    
113
    drupal_add_library('system', 'ui.draggable');
114
    drupal_add_library('system', 'ui.droppable');
115
    drupal_add_library('system', 'ui.sortable');
116

    
117
    parent::add_meta();
118
  }
119

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

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

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

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

    
155
    }
156

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

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

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

    
175
  function prepare_panes($panes) {
176
    if (!$this->access()) {
177
      return parent::prepare_panes($panes);
178
    }
179

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

    
186
  function render_pane_content(&$pane) {
187
    if (!$this->access()) {
188
      return parent::render_pane_content($pane);
189
    }
190

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

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

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

    
211
    return $content;
212
  }
213

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

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

    
228
    // Wrap the placeholder in some guaranteed markup.
229
    $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>";
230

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

    
235
    return "<div id='panels-ipe-regionid-$region_id' class='panels-ipe-region'>$output</div>";
236
  }
237

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

    
248
        $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));
249

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

    
259
      // Break the lock.
260
      panels_edit_cache_break_lock($this->cache);
261
    }
262
  } 
263

    
264
  function get_panels_storage_op_for_ajax($method) {
265
    switch ($method) {
266
      case 'ajax_unlock_ipe':
267
      case 'ajax_save_form':
268
        return 'update';
269
      case 'ajax_change_layout':
270
      case 'ajax_set_layout':
271
        return 'change layout';
272
    }
273

    
274
    return parent::get_panels_storage_op_for_ajax($method);
275
  }
276

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

    
292
  /**
293
   * AJAX entry point to create the controller form for an IPE.
294
   */
295
  function ajax_save_form($break = NULL) {
296
    if ($this->ipe_test_lock('save-form', $break)) {
297
      return;
298
    }
299

    
300
    // Reset the $_POST['ajax_html_ids'] values to preserve
301
    // proper IDs on form elements when they are rebuilt
302
    // by the Panels IPE without refreshing the page
303
    $_POST['ajax_html_ids'] = array();
304

    
305
    $form_state = array(
306
      'renderer' => $this,
307
      'display' => &$this->display,
308
      'content_types' => $this->cache->content_types,
309
      'rerender' => FALSE,
310
      'no_redirect' => TRUE,
311
      // Panels needs this to make sure that the layout gets callbacks
312
      'layout' => $this->plugins['layout'],
313
    );
314

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

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

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

    
357
    $this->commands[] = array(
358
      'command' => 'endIPE',
359
      'key' => $this->clean_key,
360
    );
361
  }
362

    
363
  /**
364
   * AJAX entry point to create the controller form for an IPE.
365
   */
366
  function ajax_change_layout($break = NULL) {
367
    if ($this->ipe_test_lock('change_layout', $break)) {
368
      return;
369
    }
370

    
371
    // At this point, we want to save the cache to ensure that we have a lock.
372
    $this->cache->ipe_locked = TRUE;
373
    panels_edit_cache_set($this->cache);
374

    
375
    ctools_include('plugins', 'panels');
376
    ctools_include('common', 'panels');
377

    
378
    // @todo figure out a solution for this, it's critical
379
    if (isset($this->display->allowed_layouts)) {
380
      $layouts = $this->display->allowed_layouts;
381
    }
382
    else {
383
      $layouts = panels_common_get_allowed_layouts('panels_page');
384
    }
385

    
386
    // Filter out builders
387
    $layouts = array_filter($layouts, '_panels_builder_filter');
388

    
389
    // Let other modules filter the layouts.
390
    drupal_alter('panels_layouts_available', $layouts);
391

    
392
    // Define the current layout
393
    $current_layout = $this->plugins['layout']['name'];
394

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

    
397
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
398
    $this->commands[] = array(
399
      'command' => 'IPEsetLockState',
400
      'key' => $this->clean_key,
401
      'lockPath' => url($this->get_url('unlock_ipe')),
402
    );
403
  }
404

    
405
  function ajax_set_layout($layout) {
406
    ctools_include('context');
407
    ctools_include('display-layout', 'panels');
408
    $form_state = array(
409
      'layout' => $layout,
410
      'display' => $this->display,
411
      'finish' => t('Save'),
412
      'no_redirect' => TRUE,
413
    );
414

    
415
    // Reset the $_POST['ajax_html_ids'] values to preserve
416
    // proper IDs on form elements when they are rebuilt
417
    // by the Panels IPE without refreshing the page
418
    $_POST['ajax_html_ids'] = array();
419

    
420
    $output = drupal_build_form('panels_change_layout', $form_state);
421
    $output = drupal_render($output);
422
    if (!empty($form_state['executed'])) {
423
      if (isset($form_state['back'])) {
424
        return $this->ajax_change_layout();
425
      }
426

    
427
      if (!empty($form_state['clicked_button']['#save-display'])) {
428
        // Saved. Save the cache.
429
        panels_edit_cache_save($this->cache);
430
        $this->display->skip_cache = TRUE;
431

    
432
        // Since the layout changed, we have to update these things in the
433
        // renderer in order to get the right settings.
434
        $layout = panels_get_layout($this->display->layout);
435
        $this->plugins['layout'] = $layout;
436
        if (!isset($layout['regions'])) {
437
          $this->plugins['layout']['regions'] = panels_get_regions($layout, $this->display);
438
        }
439

    
440
        $this->meta_location = 'inline';
441

    
442
        $this->commands[] = ajax_command_replace("#panels-ipe-display-{$this->clean_key}", panels_render_display($this->display, $this));
443
        $this->commands[] = ctools_modal_command_dismiss();
444
        return;
445
      }
446
    }
447

    
448
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
449
  }
450

    
451
  /**
452
   * Create a command array to redraw a pane.
453
   */
454
  function command_update_pane($pid) {
455
    if (is_object($pid)) {
456
      $pane = $pid;
457
    }
458
    else {
459
      $pane = $this->display->content[$pid];
460
    }
461

    
462
    $this->commands[] = ajax_command_replace("#panels-ipe-paneid-$pane->pid", $this->render_pane($pane));
463
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
464
  }
465

    
466
  /**
467
   * Create a command array to add a new pane.
468
   */
469
  function command_add_pane($pid) {
470
    if (is_object($pid)) {
471
      $pane = $pid;
472
    }
473
    else {
474
      $pane = $this->display->content[$pid];
475
    }
476

    
477
    $this->commands[] = array(
478
      'command' => 'insertNewPane',
479
      'regionId' => $pane->panel,
480
      'renderedPane' => $this->render_pane($pane),
481
    );
482
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
483
    $this->commands[] = array(
484
      'command' => 'addNewPane',
485
      'key' => $this->clean_key,
486
    );
487
  }
488
}
489

    
490
/**
491
 * FAPI callback to create the Save/Cancel form for the IPE.
492
 */
493
function panels_ipe_edit_control_form($form, &$form_state) {
494
  $display = &$form_state['display'];
495
  // @todo -- this should be unnecessary as we ensure cache_key is set in add_meta()
496
//  $display->cache_key = isset($display->cache_key) ? $display->cache_key : $display->did;
497

    
498
  // Annoyingly, theme doesn't have access to form_state so we have to do this.
499
  $form['#display'] = $display;
500

    
501
  $layout = panels_get_layout($display->layout);
502
  $layout_panels = panels_get_regions($layout, $display);
503

    
504
  $form['panel'] = array('#tree' => TRUE);
505
  $form['panel']['pane'] = array('#tree' => TRUE);
506

    
507
  foreach ($layout_panels as $panel_id => $title) {
508
    // Make sure we at least have an empty array for all possible locations.
509
    if (!isset($display->panels[$panel_id])) {
510
      $display->panels[$panel_id] = array();
511
    }
512

    
513
    $form['panel']['pane'][$panel_id] = array(
514
      // Use 'hidden' instead of 'value' so the js can access it.
515
      '#type' => 'hidden',
516
      '#default_value' => implode(',', (array) $display->panels[$panel_id]),
517
    );
518
  }
519

    
520
  $form['buttons']['submit'] = array(
521
    '#type' => 'submit',
522
    '#value' => t('Save'),
523
    '#id' => 'panels-ipe-save',
524
    '#attributes' => array('class' => array('panels-ipe-save')),
525
    '#submit' => array('panels_edit_display_form_submit'),
526
    '#save-display' => TRUE,
527
  );
528
  $form['buttons']['cancel'] = array(
529
    '#type' => 'submit',
530
    '#id' => 'panels-ipe-cancel',
531
    '#attributes' => array('class' => array('panels-ipe-cancel')),
532
    '#value' => t('Cancel'),
533
  );
534
  return $form;
535
}