Projet

Général

Profil

Paste
Télécharger (16,1 ko) Statistiques
| Branche: | Révision:

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

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
  function render() {
11
    $output = parent::render();
12
    return "<div id='panels-ipe-display-{$this->clean_key}' class='panels-ipe-display-container'>$output</div>";
13
  }
14

    
15
  function add_meta() {
16
    ctools_include('display-edit', 'panels');
17
    ctools_include('content');
18

    
19
    if (empty($this->display->cache_key)) {
20
      $this->cache = panels_edit_cache_get_default($this->display);
21
    }
22
    // @todo we may need an else to load the cache, but I am not sure we
23
    // actually need to load it if we already have our cache key, and doing
24
    // so is a waste of resources.
25

    
26
    ctools_include('cleanstring');
27
    $this->clean_key = ctools_cleanstring($this->display->cache_key);
28
    $button = array(
29
      '#type' => 'link',
30
      '#title' => t('Customize this page'),
31
      '#href' => $this->get_url('save_form'),
32
      '#options' => array('query' => drupal_get_destination()),
33
      '#id' => 'panels-ipe-customize-page',
34
      '#attributes' => array(
35
        'class' => array('panels-ipe-startedit', 'panels-ipe-pseudobutton'),
36
      ),
37
      '#ajax' => array(
38
        'progress' => 'throbber',
39
        'ipe_cache_key' => $this->clean_key,
40
      ),
41
      '#prefix' => '<div class="panels-ipe-pseudobutton-container">',
42
      '#suffix' => '</div>',
43
    );
44

    
45
    panels_ipe_toolbar_add_button($this->clean_key, 'panels-ipe-startedit', $button);
46

    
47
    // @todo this actually should be an IPE setting instead.
48
    if (user_access('change layouts in place editing')) {
49
      $button = array(
50
        '#type' => 'link',
51
        '#title' => t('Change layout'),
52
        '#href' => $this->get_url('change_layout'),
53
        '#options' => array('query' => drupal_get_destination()),
54
        '#attributes' => array(
55
          'class' => array('panels-ipe-change-layout', 'panels-ipe-pseudobutton', 'ctools-modal-layout'),
56
        ),
57
        '#ajax' => array(
58
          'progress' => 'throbber',
59
          'ipe_cache_key' => $this->clean_key,
60
        ),
61

    
62
      '#prefix' => '<div class="panels-ipe-pseudobutton-container">',
63
      '#suffix' => '</div>',
64
      );
65

    
66
      panels_ipe_toolbar_add_button($this->clean_key, 'panels-ipe-change-layout', $button);
67
    }
68

    
69
    ctools_include('ajax');
70
    ctools_include('modal');
71
    ctools_modal_add_js();
72

    
73
    ctools_add_css('panels_dnd', 'panels');
74
    ctools_add_css('panels_admin', 'panels');
75
    ctools_add_js('panels_ipe', 'panels_ipe');
76
    ctools_add_css('panels_ipe', 'panels_ipe');
77

    
78
    drupal_add_js(array('PanelsIPECacheKeys' => array($this->clean_key)), 'setting');
79

    
80
    drupal_add_library('system', 'ui.draggable');
81
    drupal_add_library('system', 'ui.droppable');
82
    drupal_add_library('system', 'ui.sortable');
83

    
84
    parent::add_meta();
85
  }
86

    
87
  /**
88
   * Override & call the parent, then pass output through to the dnd wrapper
89
   * theme function.
90
   *
91
   * @param $pane
92
   */
93
  function render_pane(&$pane) {
94
    $output = parent::render_pane($pane);
95
    if (empty($output)) {
96
      return;
97
    }
98

    
99
    // If there are region locks, add them.
100
    if (!empty($pane->locks['type']) && $pane->locks['type'] == 'regions') {
101
      static $key = NULL;
102
      $javascript = &drupal_static('drupal_add_js', array());
103

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

    
110
        // These are just added via [] so we have to grab the last one
111
        // and reference it.
112
        $keys = array_keys($javascript['settings']['data']);
113
        $key = end($keys);
114
      }
115
      else {
116
        $javascript['settings']['data'][$key]['Panels']['RegionLock'][$pane->pid] = $pane->locks['regions'];
117
      }
118

    
119
    }
120

    
121
    if (empty($pane->IPE_empty)) {
122
      // Add an inner layer wrapper to the pane content before placing it into
123
      // draggable portlet
124
      $output = "<div class=\"panels-ipe-portlet-content\">$output</div>";
125
    }
126
    else {
127
      $output = "<div class=\"panels-ipe-portlet-content panels-ipe-empty-pane\">$output</div>";
128
    }
129
    // Hand it off to the plugin/theme for placing draggers/buttons
130
    $output = theme('panels_ipe_pane_wrapper', array('output' => $output, 'pane' => $pane, 'display' => $this->display, 'renderer' => $this));
131

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

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

    
139
  function prepare_panes($panes) {
140
    // Set to admin mode just for this to ensure all panes are represented.
141
    $this->admin = TRUE;
142
    $panes = parent::prepare_panes($panes);
143
    $this->admin = FALSE;
144
  }
145

    
146
  function render_pane_content(&$pane) {
147
    if (!empty($pane->shown) && panels_pane_access($pane, $this->display)) {
148
      $content = parent::render_pane_content($pane);
149
    }
150
    // Ensure that empty panes have some content.
151
    if (empty($content) || empty($content->content)) {
152
      if (empty($content)) {
153
        $content = new stdClass();
154
      }
155

    
156
      // Get the administrative title.
157
      $content_type = ctools_get_content_type($pane->type);
158
      $title = ctools_content_admin_title($content_type, $pane->subtype, $pane->configuration, $this->display->context);
159

    
160
      $content->content = t('Placeholder for empty or inaccessible "@title"', array('@title' => html_entity_decode($title, ENT_QUOTES)));
161
      // Add these to prevent notices.
162
      $content->type = 'panels_ipe';
163
      $content->subtype = 'panels_ipe';
164
      $pane->IPE_empty = TRUE;
165
    }
166

    
167
    return $content;
168
  }
169

    
170
  /**
171
   * Add an 'empty' pane placeholder above all the normal panes.
172
   *
173
   * @param $region_id
174
   * @param $panes
175
   */
176
  function render_region($region_id, $panes) {
177
    // Generate this region's 'empty' placeholder pane from the IPE plugin.
178
    $empty_ph = theme('panels_ipe_placeholder_pane', array('region_id' => $region_id, 'region_title' => $this->plugins['layout']['regions'][$region_id]));
179

    
180
    // Wrap the placeholder in some guaranteed markup.
181
    $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>";
182

    
183
    $output = parent::render_region($region_id, $panes);
184
    $output = theme('panels_ipe_region_wrapper', array('output' => $output, 'region_id' => $region_id, 'display' => $this->display, 'controls' => $control, 'renderer' => $this));
185
    $classes = 'panels-ipe-region';
186

    
187
    return "<div id='panels-ipe-regionid-$region_id' class='panels-ipe-region'>$output</div>";
188
  }
189

    
190
  /**
191
   * This is a generic lock test.
192
   */
193
  function ipe_test_lock($url, $break) {
194
    if (!empty($this->cache->locked)) {
195
      if ($break != 'break') {
196
        $account  = user_load($this->cache->locked->uid);
197
        $name     = format_username($account);
198
        $lock_age = format_interval(time() - $this->cache->locked->updated);
199

    
200
        $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));
201

    
202
        $this->commands[] = array(
203
          'command' => 'unlockIPE',
204
          'message' => $message,
205
          'break_path' => url($this->get_url($url, 'break')),
206
          'key' => $this->clean_key,
207
        );
208
        return TRUE;
209
      }
210

    
211
      // Break the lock.
212
      panels_edit_cache_break_lock($this->cache);
213
    }
214
  }
215

    
216
  /**
217
   * AJAX callback to unlock the IPE.
218
   *
219
   * This is called whenever something server side determines that editing
220
   * has stopped and cleans up no longer needed locks.
221
   *
222
   * It has no visible return value as this is considered a background task
223
   * and the client side has already given all indications that things are
224
   * now in a 'normal' state.
225
   */
226
  function ajax_unlock_ipe() {
227
    panels_edit_cache_clear($this->cache);
228
    $this->commands[] = array();
229
  }
230

    
231
  /**
232
   * AJAX entry point to create the controller form for an IPE.
233
   */
234
  function ajax_save_form($break = NULL) {
235
    if ($this->ipe_test_lock('save-form', $break)) {
236
      return;
237
    }
238

    
239
    // Reset the $_POST['ajax_html_ids'] values to preserve
240
    // proper IDs on form elements when they are rebuilt
241
    // by the Panels IPE without refreshing the page
242
    $_POST['ajax_html_ids'] = array();
243

    
244
    $form_state = array(
245
      'renderer' => $this,
246
      'display' => &$this->display,
247
      'content_types' => $this->cache->content_types,
248
      'rerender' => FALSE,
249
      'no_redirect' => TRUE,
250
      // Panels needs this to make sure that the layout gets callbacks
251
      'layout' => $this->plugins['layout'],
252
    );
253

    
254
    $output = drupal_build_form('panels_ipe_edit_control_form', $form_state);
255
    if (empty($form_state['executed'])) {
256
      // At this point, we want to save the cache to ensure that we have a lock.
257
      $this->cache->ipe_locked = TRUE;
258
      panels_edit_cache_set($this->cache);
259
      $this->commands[] = array(
260
        'command' => 'initIPE',
261
        'key' => $this->clean_key,
262
        'data' => drupal_render($output),
263
        'lockPath' => url($this->get_url('unlock_ipe')),
264
      );
265
      return;
266
    }
267

    
268
    // Check to see if we have a lock that was broken. If so we need to
269
    // inform the user and abort.
270
    if (empty($this->cache->ipe_locked)) {
271
      $this->commands[] = ajax_command_alert(t('A lock you had has been externally broken, and all your changes have been reverted.'));
272
      $this->commands[] = array(
273
        'command' => 'cancelIPE',
274
        'key' => $this->clean_key,
275
      );
276
      return;
277
    }
278

    
279
    // Otherwise it was submitted.
280
    if (!empty($form_state['clicked_button']['#save-display'])) {
281
      // Saved. Save the cache.
282
      panels_edit_cache_save($this->cache);
283
      // A rerender should fix IDs on added panes as well as ensure style changes are
284
      // rendered.
285
      $this->meta_location = 'inline';
286
      $this->commands[] = ajax_command_replace("#panels-ipe-display-{$this->clean_key}", panels_render_display($this->display, $this));
287
    }
288
    else {
289
      // Cancelled. Clear the cache.
290
      panels_edit_cache_clear($this->cache);
291
    }
292

    
293
    $this->commands[] = array(
294
      'command' => 'endIPE',
295
      'key' => $this->clean_key,
296
    );
297
  }
298

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

    
307
    // At this point, we want to save the cache to ensure that we have a lock.
308
    $this->cache->ipe_locked = TRUE;
309
    panels_edit_cache_set($this->cache);
310

    
311
    ctools_include('plugins', 'panels');
312
    ctools_include('common', 'panels');
313

    
314
    // @todo figure out a solution for this, it's critical
315
    if (isset($this->display->allowed_layouts)) {
316
      $layouts = $this->display->allowed_layouts;
317
    }
318
    else {
319
      $layouts = panels_common_get_allowed_layouts('panels_page');
320
    }
321

    
322
    // Filter out builders
323
    $layouts = array_filter($layouts, '_panels_builder_filter');
324

    
325
    // Define the current layout
326
    $current_layout = $this->plugins['layout']['name'];
327

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

    
330
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
331
    $this->commands[] = array(
332
      'command' => 'IPEsetLockState',
333
      'key' => $this->clean_key,
334
      'lockPath' => url($this->get_url('unlock_ipe')),
335
    );
336
  }
337

    
338
  function ajax_set_layout($layout) {
339
    ctools_include('context');
340
    ctools_include('display-layout', 'panels');
341
    $form_state = array(
342
      'layout' => $layout,
343
      'display' => $this->display,
344
      'finish' => t('Save'),
345
      'no_redirect' => TRUE,
346
    );
347

    
348
    // Reset the $_POST['ajax_html_ids'] values to preserve
349
    // proper IDs on form elements when they are rebuilt
350
    // by the Panels IPE without refreshing the page
351
    $_POST['ajax_html_ids'] = array();
352

    
353
    $output = drupal_build_form('panels_change_layout', $form_state);
354
    $output = drupal_render($output);
355
    if (!empty($form_state['executed'])) {
356
      if (isset($form_state['back'])) {
357
        return $this->ajax_change_layout();
358
      }
359

    
360
      if (!empty($form_state['clicked_button']['#save-display'])) {
361
        // Saved. Save the cache.
362
        panels_edit_cache_save($this->cache);
363
        $this->display->skip_cache = TRUE;
364

    
365
        // Since the layout changed, we have to update these things in the
366
        // renderer in order to get the right settings.
367
        $layout = panels_get_layout($this->display->layout);
368
        $this->plugins['layout'] = $layout;
369
        if (!isset($layout['regions'])) {
370
          $this->plugins['layout']['regions'] = panels_get_regions($layout, $this->display);
371
        }
372

    
373
        $this->meta_location = 'inline';
374

    
375
        $this->commands[] = ajax_command_replace("#panels-ipe-display-{$this->clean_key}", panels_render_display($this->display, $this));
376
        $this->commands[] = ctools_modal_command_dismiss();
377
        return;
378
      }
379
    }
380

    
381
    $this->commands[] = ctools_modal_command_display(t('Change layout'), $output);
382
  }
383

    
384
  /**
385
   * Create a command array to redraw a pane.
386
   */
387
  function command_update_pane($pid) {
388
    if (is_object($pid)) {
389
      $pane = $pid;
390
    }
391
    else {
392
      $pane = $this->display->content[$pid];
393
    }
394

    
395
    $this->commands[] = ajax_command_replace("#panels-ipe-paneid-$pane->pid", $this->render_pane($pane));
396
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
397
  }
398

    
399
  /**
400
   * Create a command array to add a new pane.
401
   */
402
  function command_add_pane($pid) {
403
    if (is_object($pid)) {
404
      $pane = $pid;
405
    }
406
    else {
407
      $pane = $this->display->content[$pid];
408
    }
409

    
410
    $this->commands[] = array(
411
      'command' => 'insertNewPane',
412
      'regionId' => $pane->panel,
413
      'renderedPane' => $this->render_pane($pane),
414
    );
415
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
416
    $this->commands[] = array(
417
      'command' => 'addNewPane',
418
      'key' => $this->clean_key,
419
    );
420
  }
421
}
422

    
423
/**
424
 * FAPI callback to create the Save/Cancel form for the IPE.
425
 */
426
function panels_ipe_edit_control_form($form, &$form_state) {
427
  $display = &$form_state['display'];
428
  // @todo -- this should be unnecessary as we ensure cache_key is set in add_meta()
429
//  $display->cache_key = isset($display->cache_key) ? $display->cache_key : $display->did;
430

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

    
434
  $layout = panels_get_layout($display->layout);
435
  $layout_panels = panels_get_regions($layout, $display);
436

    
437
  $form['panel'] = array('#tree' => TRUE);
438
  $form['panel']['pane'] = array('#tree' => TRUE);
439

    
440
  foreach ($layout_panels as $panel_id => $title) {
441
    // Make sure we at least have an empty array for all possible locations.
442
    if (!isset($display->panels[$panel_id])) {
443
      $display->panels[$panel_id] = array();
444
    }
445

    
446
    $form['panel']['pane'][$panel_id] = array(
447
      // Use 'hidden' instead of 'value' so the js can access it.
448
      '#type' => 'hidden',
449
      '#default_value' => implode(',', (array) $display->panels[$panel_id]),
450
    );
451
  }
452

    
453
  $form['buttons']['submit'] = array(
454
    '#type' => 'submit',
455
    '#value' => t('Save'),
456
    '#id' => 'panels-ipe-save',
457
    '#attributes' => array('class' => array('panels-ipe-save')),
458
    '#submit' => array('panels_edit_display_form_submit'),
459
    '#save-display' => TRUE,
460
  );
461
  $form['buttons']['cancel'] = array(
462
    '#type' => 'submit',
463
    '#id' => 'panels-ipe-cancel',
464
    '#attributes' => array('class' => array('panels-ipe-cancel')),
465
    '#value' => t('Cancel'),
466
  );
467
  return $form;
468
}