Projet

Général

Profil

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

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

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
    }
349
    else {
350
      // Cancelled. Clear the cache.
351
      panels_edit_cache_clear($this->cache);
352
    }
353

    
354
    $this->commands[] = array(
355
      'command' => 'endIPE',
356
      'key' => $this->clean_key,
357
    );
358
  }
359

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

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

    
372
    ctools_include('plugins', 'panels');
373
    ctools_include('common', 'panels');
374

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

    
383
    // Filter out builders
384
    $layouts = array_filter($layouts, '_panels_builder_filter');
385

    
386
    // Let other modules filter the layouts.
387
    drupal_alter('panels_layouts_available', $layouts);
388

    
389
    // Define the current layout
390
    $current_layout = $this->plugins['layout']['name'];
391

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

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

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

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

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

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

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

    
437
        $this->meta_location = 'inline';
438

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

    
445
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
446
  }
447

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

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

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

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

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

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

    
498
  $layout = panels_get_layout($display->layout);
499
  $layout_panels = panels_get_regions($layout, $display);
500

    
501
  $form['panel'] = array('#tree' => TRUE);
502
  $form['panel']['pane'] = array('#tree' => TRUE);
503

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

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

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