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 85ad3d82 Assos Assos
<?php
2
3 64156087 Assos Assos
/**
4
 * @file
5
 */
6
7 85ad3d82 Assos Assos
/**
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 136a805a Assos Assos
  // 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 85ad3d82 Assos Assos
  function render() {
40
    $output = parent::render();
41 136a805a Assos Assos
    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 85ad3d82 Assos Assos
  }
46
47
  function add_meta() {
48 136a805a Assos Assos
    if (!$this->access()) {
49
      return parent::add_meta();
50
    }
51
52 85ad3d82 Assos Assos
    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 e4c061ad Assos Assos
      '#options' => array('query' => drupal_get_destination()),
68 85ad3d82 Assos Assos
      '#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 136a805a Assos Assos
    panels_ipe_toolbar_add_button($this->display->cache_key, 'panels-ipe-startedit', $button);
81 85ad3d82 Assos Assos
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 e4c061ad Assos Assos
        '#options' => array('query' => drupal_get_destination()),
89 85ad3d82 Assos Assos
        '#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 64156087 Assos Assos
        '#prefix' => '<div class="panels-ipe-pseudobutton-container">',
98
        '#suffix' => '</div>',
99 85ad3d82 Assos Assos
      );
100
101 136a805a Assos Assos
      panels_ipe_toolbar_add_button($this->display->cache_key, 'panels-ipe-change-layout', $button);
102 85ad3d82 Assos Assos
    }
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 136a805a Assos Assos
    ctools_add_js('panels-base', 'panels');
111 85ad3d82 Assos Assos
    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 136a805a Assos Assos
    if (!$this->access()) {
135
      return $output;
136
    }
137 85ad3d82 Assos Assos
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 64156087 Assos Assos
      // draggable portlet.
163 85ad3d82 Assos Assos
      $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 64156087 Assos Assos
    // Hand it off to the plugin/theme for placing draggers/buttons.
169 85ad3d82 Assos Assos
    $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 5a7e6170 Florent Torregrosa
  function prepare_panes($panes) {
179 136a805a Assos Assos
    if (!$this->access()) {
180
      return parent::prepare_panes($panes);
181
    }
182
183 5a7e6170 Florent Torregrosa
    // 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 85ad3d82 Assos Assos
  function render_pane_content(&$pane) {
190 136a805a Assos Assos
    if (!$this->access()) {
191
      return parent::render_pane_content($pane);
192
    }
193
194 5a7e6170 Florent Torregrosa
    if (!empty($pane->shown) && panels_pane_access($pane, $this->display)) {
195
      $content = parent::render_pane_content($pane);
196
    }
197 85ad3d82 Assos Assos
    // Ensure that empty panes have some content.
198
    if (empty($content) || empty($content->content)) {
199 5a7e6170 Florent Torregrosa
      if (empty($content)) {
200
        $content = new stdClass();
201
      }
202
203 85ad3d82 Assos Assos
      // 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 5a7e6170 Florent Torregrosa
      $content->content = t('Placeholder for empty or inaccessible "@title"', array('@title' => html_entity_decode($title, ENT_QUOTES)));
208 85ad3d82 Assos Assos
      // 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 136a805a Assos Assos
    if (!$this->access()) {
225
      return parent::render_region($region_id, $panes);
226
    }
227
228 85ad3d82 Assos Assos
    // 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 64156087 Assos Assos
  }
266 136a805a Assos Assos
267 64156087 Assos Assos
  /**
268
   *
269
   */
270 136a805a Assos Assos
  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 64156087 Assos Assos
276 136a805a Assos Assos
      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 85ad3d82 Assos Assos
  }
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 64156087 Assos Assos
    // by the Panels IPE without refreshing the page.
310 85ad3d82 Assos Assos
    $_POST['ajax_html_ids'] = array();
311
312
    $form_state = array(
313 5a7e6170 Florent Torregrosa
      'renderer' => $this,
314 85ad3d82 Assos Assos
      'display' => &$this->display,
315
      'content_types' => $this->cache->content_types,
316
      'rerender' => FALSE,
317
      'no_redirect' => TRUE,
318 64156087 Assos Assos
      // Panels needs this to make sure that the layout gets callbacks.
319 85ad3d82 Assos Assos
      '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 5a7e6170 Florent Torregrosa
        'lockPath' => url($this->get_url('unlock_ipe')),
332 85ad3d82 Assos Assos
      );
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 2545992a Assos Assos
      $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 64156087 Assos Assos
359
      $storage_id = $this->cache->display->storage_id;
360
      cache_clear_all('panels_mini_load:' . $storage_id, 'cache_panels', TRUE);
361 85ad3d82 Assos Assos
    }
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 64156087 Assos Assos
    // Filter out builders.
397 85ad3d82 Assos Assos
    $layouts = array_filter($layouts, '_panels_builder_filter');
398
399 136a805a Assos Assos
    // Let other modules filter the layouts.
400
    drupal_alter('panels_layouts_available', $layouts);
401
402 64156087 Assos Assos
    // Define the current layout.
403 85ad3d82 Assos Assos
    $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 5a7e6170 Florent Torregrosa
      'lockPath' => url($this->get_url('unlock_ipe')),
412 85ad3d82 Assos Assos
    );
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 64156087 Assos Assos
    // by the Panels IPE without refreshing the page.
428 85ad3d82 Assos Assos
    $_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 5a7e6170 Florent Torregrosa
        $this->display->skip_cache = TRUE;
441 85ad3d82 Assos Assos
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 5a7e6170 Florent Torregrosa
    $this->commands[] = array(
488
      'command' => 'insertNewPane',
489
      'regionId' => $pane->panel,
490
      'renderedPane' => $this->render_pane($pane),
491
    );
492 85ad3d82 Assos Assos
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
493 5a7e6170 Florent Torregrosa
    $this->commands[] = array(
494
      'command' => 'addNewPane',
495
      'key' => $this->clean_key,
496
    );
497 85ad3d82 Assos Assos
  }
498 64156087 Assos Assos
499 85ad3d82 Assos Assos
}
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 64156087 Assos Assos
  //   $display->cache_key = isset($display->cache_key) ? $display->cache_key : $display->did;
508 85ad3d82 Assos Assos
  // 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 5a7e6170 Florent Torregrosa
    '#attributes' => array('class' => array('panels-ipe-save')),
535 85ad3d82 Assos Assos
    '#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 5a7e6170 Florent Torregrosa
    '#attributes' => array('class' => array('panels-ipe-cancel')),
542 85ad3d82 Assos Assos
    '#value' => t('Cancel'),
543
  );
544
  return $form;
545
}