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 85ad3d82 Assos Assos
<?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 e4c061ad Assos Assos
      '#options' => array('query' => drupal_get_destination()),
33 85ad3d82 Assos Assos
      '#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 e4c061ad Assos Assos
        '#options' => array('query' => drupal_get_destination()),
54 85ad3d82 Assos Assos
        '#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 5a7e6170 Florent Torregrosa
  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 85ad3d82 Assos Assos
  function render_pane_content(&$pane) {
147 5a7e6170 Florent Torregrosa
    if (!empty($pane->shown) && panels_pane_access($pane, $this->display)) {
148
      $content = parent::render_pane_content($pane);
149
    }
150 85ad3d82 Assos Assos
    // Ensure that empty panes have some content.
151
    if (empty($content) || empty($content->content)) {
152 5a7e6170 Florent Torregrosa
      if (empty($content)) {
153
        $content = new stdClass();
154
      }
155
156 85ad3d82 Assos Assos
      // 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 5a7e6170 Florent Torregrosa
      $content->content = t('Placeholder for empty or inaccessible "@title"', array('@title' => html_entity_decode($title, ENT_QUOTES)));
161 85ad3d82 Assos Assos
      // 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 5a7e6170 Florent Torregrosa
      'renderer' => $this,
246 85ad3d82 Assos Assos
      '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 5a7e6170 Florent Torregrosa
        'lockPath' => url($this->get_url('unlock_ipe')),
264 85ad3d82 Assos Assos
      );
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 5a7e6170 Florent Torregrosa
      'lockPath' => url($this->get_url('unlock_ipe')),
335 85ad3d82 Assos Assos
    );
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 5a7e6170 Florent Torregrosa
        $this->display->skip_cache = TRUE;
364 85ad3d82 Assos Assos
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 5a7e6170 Florent Torregrosa
    $this->commands[] = array(
411
      'command' => 'insertNewPane',
412
      'regionId' => $pane->panel,
413
      'renderedPane' => $this->render_pane($pane),
414
    );
415 85ad3d82 Assos Assos
    $this->commands[] = ajax_command_changed("#panels-ipe-display-{$this->clean_key}");
416 5a7e6170 Florent Torregrosa
    $this->commands[] = array(
417
      'command' => 'addNewPane',
418
      'key' => $this->clean_key,
419
    );
420 85ad3d82 Assos Assos
  }
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 5a7e6170 Florent Torregrosa
    '#attributes' => array('class' => array('panels-ipe-save')),
458 85ad3d82 Assos Assos
    '#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 5a7e6170 Florent Torregrosa
    '#attributes' => array('class' => array('panels-ipe-cancel')),
465 85ad3d82 Assos Assos
    '#value' => t('Cancel'),
466
  );
467
  return $form;
468
}