Projet

Général

Profil

Paste
Télécharger (61,9 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / panels / plugins / layouts / flexible / flexible.inc @ e4c061ad

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * Implementation of hook_panels_layouts()
5
 */
6
// Plugin definition
7
$plugin = array(
8
  'title' => t('Flexible'),
9
  'category' => t('Builders'),
10
  'icon' => 'flexible.png',
11
  'theme' => 'panels_flexible',
12
  'admin theme' => 'panels_flexible_admin',
13
  'css' => 'flexible.css',
14
  'admin css' => 'flexible-admin.css',
15
  'settings form' => 'panels_flexible_settings_form',
16
  'settings submit' => 'panels_flexible_settings_submit',
17
  'settings validate' => 'panels_flexible_settings_validate',
18
  'regions function' => 'panels_flexible_panels',
19
  'hook menu' => 'panels_flexible_menu',
20
21
  // Reuisable layout Builder specific directives
22
  'builder' => TRUE,
23
  'builder tab title' => 'Add flexible layout', // menu so translated elsewhere
24
25
  'get child' => 'panels_flexible_get_sublayout',
26
  'get children' => 'panels_flexible_get_sublayouts',
27
28
  // Define ajax callbacks
29
  'ajax' => array(
30
    'settings' => 'panels_ajax_flexible_edit_settings',
31
    'add' => 'panels_ajax_flexible_edit_add',
32
    'remove' => 'panels_ajax_flexible_edit_remove',
33
    'resize' => 'panels_ajax_flexible_edit_resize',
34
    'reuse' => 'panels_ajax_flexible_edit_reuse',
35
  ),
36
);
37
38
/**
39
 * Merge the main flexible plugin with a layout to create a sub plugin.
40
 *
41
 * This is used for both panels_flexible_get_sublayout and
42
 * panels_flexible_get_sublayouts.
43
 */
44
function panels_flexible_merge_plugin($plugin, $layout) {
45
  $plugin['name'] = 'flexible:' . $layout->name;
46
  $plugin['category'] = !empty($layout->category) ? check_plain($layout->category) : t('Miscellaneous');
47
  $plugin['title'] = check_plain($layout->admin_title);
48
  $plugin['description'] = check_plain($layout->admin_description);
49
  $plugin['layout'] = $layout;
50
  $plugin['builder'] = FALSE;
51
  $plugin['builder tab title'] = NULL;
52
  return $plugin;
53
}
54
55
/**
56
 * Callback to provide a single stored flexible layout.
57
 */
58
function panels_flexible_get_sublayout($plugin, $layout_name, $sublayout_name) {
59
  // Do not worry about caching; Panels is handling that for us.
60
  ctools_include('export');
61
  $item = ctools_export_crud_load('panels_layout', $sublayout_name);
62
  if ($item) {
63
    return panels_flexible_merge_plugin($plugin, $item);
64
  }
65
}
66
67
/**
68
 * Callback to provide all stored flexible layouts.
69
 */
70
function panels_flexible_get_sublayouts($plugin, $layout_name) {
71
  $layouts[$layout_name] = $plugin;
72
  ctools_include('export');
73
  $items = ctools_export_load_object('panels_layout', 'conditions', array('plugin' => 'flexible'));
74
  foreach ($items as $name => $item) {
75
    $layouts['flexible:' . $name] = panels_flexible_merge_plugin($plugin, $item);
76
  }
77
78
  return $layouts;
79
}
80
81
/**
82
 * Convert settings from old style to new, or provide defaults for
83
 * empty settings.
84
 * @param <type> $settings
85
 */
86
function panels_flexible_convert_settings(&$settings, &$layout) {
87
  // This indicates that this is a layout that they used the checkbox
88
  // on. The layout is still 'flexible' but it's actually pointing
89
  // to another stored one and we have to load it.
90
  if (!empty($settings['layout'])) {
91
    $layout = panels_get_layout('flexible:' . $settings['layout']);
92
  }
93
94
  if (!empty($layout['layout'])) {
95
    $settings = $layout['layout']->settings;
96
    if ($settings) {
97
      return $settings;
98
    }
99
  }
100
101
  if (empty($settings)) {
102
    // set up a default
103
    $settings = array(
104
      'items' => array(
105
        // The 'canvas' is a special row that does not get rendered
106
        // normally, but is used to contain the columns.
107
        'canvas' => array(
108
          'type' => 'row',
109
          'contains' => 'column',
110
          'children' => array('main'),
111
          'parent' => NULL,
112
        ),
113
        'main' => array(
114
          'type' => 'column',
115
          'width' => 100,
116
          'width_type' => '%',
117
          'children' => array('main-row'),
118
          'parent' => 'canvas',
119
        ),
120
        'main-row' => array(
121
          'type' => 'row',
122
          'contains' => 'region',
123
          'children' => array('center'),
124
          'parent' => 'main',
125
        ),
126
        'center' => array(
127
          'type' => 'region',
128
          'title' => t('Center'),
129
          'width' => 100,
130
          'width_type' => '%',
131
          'parent' => 'main-row',
132
        ),
133
      ),
134
    );
135
  }
136
  else if (!isset($settings['items'])) {
137
    // Convert an old style flexible to a new style flexible.
138
    $old = $settings;
139
    $settings = array();
140
    $settings['items']['canvas'] = array(
141
      'type' => 'row',
142
      'contains' => 'column',
143
      'children' => array(),
144
      'parent' => NULL,
145
    );
146
    // add the left sidebar column, row and region if it exists.
147
    if (!empty($old['sidebars']['left'])) {
148
      $settings['items']['canvas']['children'][] = 'sidebar-left';
149
      $settings['items']['sidebar-left'] = array(
150
        'type' => 'column',
151
        'width' => $old['sidebars']['left_width'],
152
        'width_type' => $old['sidebars']['width_type'],
153
        'children' => array('sidebar-left-row'),
154
        'parent' => 'canvas',
155
      );
156
      $settings['items']['sidebar-left-row'] = array(
157
        'type' => 'row',
158
        'contains' => 'region',
159
        'children' => array('sidebar_left'),
160
        'parent' => 'sidebar-left',
161
      );
162
      $settings['items']['sidebar_left'] = array(
163
        'type' => 'region',
164
        'title' => t('Left sidebar'),
165
        'width' => 100,
166
        'width_type' => '%',
167
        'parent' => 'sidebar-left-row',
168
      );
169
    }
170
171
    $settings['items']['canvas']['children'][] = 'main';
172
173
    if (!empty($old['sidebars']['right'])) {
174
      $settings['items']['canvas']['children'][] = 'sidebar-right';
175
      $settings['items']['sidebar-right'] = array(
176
        'type' => 'column',
177
        'width' => $old['sidebars']['right_width'],
178
        'width_type' => $old['sidebars']['width_type'],
179
        'children' => array('sidebar-right-row'),
180
        'parent' => 'canvas',
181
      );
182
      $settings['items']['sidebar-right-row'] = array(
183
        'type' => 'row',
184
        'contains' => 'region',
185
        'children' => array('sidebar_right'),
186
        'parent' => 'sidebar-right',
187
      );
188
      $settings['items']['sidebar_right'] = array(
189
        'type' => 'region',
190
        'title' => t('Right sidebar'),
191
        'width' => 100,
192
        'width_type' => '%',
193
        'parent' => 'sidebar-right-row',
194
      );
195
    }
196
197
    // Add the main column.
198
    $settings['items']['main'] = array(
199
      'type' => 'column',
200
      'width' => 100,
201
      'width_type' => '%',
202
      'children' => array(),
203
      'parent' => 'canvas',
204
    );
205
206
    // Add rows and regions.
207
    for ($row = 1; $row <= intval($old['rows']); $row++) {
208
      // Create entry for the row
209
      $settings['items']["row_$row"] = array(
210
        'type' => 'row',
211
        'contains' => 'region',
212
        'children' => array(),
213
        'parent' => 'main',
214
      );
215
      // Add the row to the parent's children
216
      $settings['items']['main']['children'][] = "row_$row";
217
218
      for ($col = 1; $col <= intval($old["row_$row"]['columns']); $col++) {
219
        // Create entry for the region
220
        $settings['items']["row_${row}_$col"] = array(
221
          'type' => 'region',
222
          'width' => $old["row_$row"]["width_$col"],
223
          'width_type' => '%',
224
          'parent' => "row_$row",
225
        );
226
        // Add entry for the region to the row's children
227
        $settings['items']["row_$row"]['children'][] = "row_${row}_$col";
228
229
        // Apply the proper title to the region
230
        if (!empty($old["row_$row"]['names'][$col - 1])) {
231
          $settings['items']["row_${row}_$col"]['title'] = $old["row_$row"]['names'][$col - 1];
232
        }
233
        else {
234
          $settings['items']["row_${row}_$col"]['title'] = t("Row @row, Column @col", array('@row' => $row, '@col' => $col));
235
        }
236
      }
237
    }
238
  }
239
  else if (isset($settings['canvas'])) {
240
    // Convert the old 'canvas' to the new canvas row.
241
    $settings['items']['canvas'] = array(
242
      'type' => 'row',
243
      'contains' => 'column',
244
      'children' => $settings['canvas'],
245
      'parent' => NULL,
246
    );
247
    unset($settings['canvas']);
248
  }
249
}
250
251
/**
252
 * Define the actual list of columns and rows for this flexible panel.
253
 */
254
function panels_flexible_panels($display, $settings, $layout) {
255
  $items = array();
256
  panels_flexible_convert_settings($settings, $layout);
257
  foreach ($settings['items'] as $id => $item) {
258
    // Remove garbage values.
259
    if (!isset($item['type'])) {
260
      unset($items[$id]);
261
    }
262
    else if ($item['type'] == 'region') {
263
      $items[$id] = $item['title'];
264
    }
265
  }
266
267
  return $items;
268
}
269
270
/**
271
 * Create a renderer object.
272
 *
273
 * The renderer object contains data that is passed around from function
274
 * to function allowing us to render our CSS and HTML easily.
275
 *
276
 * @todo Convert the functions to methods and make this properly OO.
277
 */
278
function panels_flexible_create_renderer($admin, $css_id, $content, $settings, &$display, $layout, $handler) {
279
  $renderer = new stdClass;
280
  $renderer->settings = $settings;
281
  $renderer->content = $content;
282
  $renderer->css_id = $css_id;
283
  $renderer->did = &$display->did;
284
  if ($admin) {
285
    // always scale in admin mode.
286
    $renderer->scale_base = 99.0;
287
  }
288
  else {
289
    $renderer->scale_base = !empty($settings['items']['canvas']['no_scale']) ? 100.0 : 99.0;
290
  }
291
  $renderer->id_str = $css_id ? 'id="' . $css_id . '"' : '';
292
  $renderer->admin = $admin;
293
  $renderer->handler = $handler;
294
295
  // Set up basic classes for all of our components.
296
  $renderer->name                 = !empty($layout['layout']) ? $layout['layout']->name : $display->did;
297
  $renderer->base_class           = $renderer->name;
298
  $renderer->item_class['column'] = 'panels-flexible-column';
299
  $renderer->item_class['row']    = 'panels-flexible-row';
300
  $renderer->item_class['region'] = 'panels-flexible-region';
301
  $renderer->base['canvas']       = 'panels-flexible-' . $renderer->base_class;
302
303
  // Override these if selected from the UI and not in admin mode.
304
  if (!$admin) {
305
    if (!empty($settings['items']['canvas']['class'])) {
306
      $renderer->base_class = $settings['items']['canvas']['class'];
307
      $renderer->base['canvas'] = $renderer->base_class;
308
    }
309
    if (!empty($settings['items']['canvas']['column_class'])) {
310
      $renderer->item_class['column'] = $settings['items']['canvas']['column_class'];
311
    }
312
    if (!empty($settings['items']['canvas']['row_class'])) {
313
      $renderer->item_class['row'] = $settings['items']['canvas']['row_class'];
314
    }
315
    if (!empty($settings['items']['canvas']['region_class'])) {
316
      $renderer->item_class['region'] = $settings['items']['canvas']['region_class'];
317
    }
318
  }
319
320
  // Get the separation values out of the canvas settings.
321
  $renderer->column_separation = !empty($settings['items']['canvas']['column_separation']) ? $settings['items']['canvas']['column_separation'] : '0.5em';
322
323
  $renderer->region_separation = !empty($settings['items']['canvas']['region_separation']) ? $settings['items']['canvas']['region_separation'] : '0.5em';
324
325
  $renderer->row_separation = !empty($settings['items']['canvas']['row_separation']) ? $settings['items']['canvas']['row_separation'] : '0.5em';
326
327
  // Make some appended classes so it's easier to reference them.
328
329
  $renderer->base['column'] = $renderer->item_class['column'] . '-' . $renderer->base_class;
330
  $renderer->base['row']    = $renderer->item_class['row'] . '-' . $renderer->base_class;
331
  $renderer->base['region'] = $renderer->item_class['region'] . '-' . $renderer->base_class;
332
333
  if ($renderer->name != 'new') {
334
    // Use v2 to guarantee all CSS gets regenerated to account for changes in
335
    // how some divs will be rendered.
336
    $renderer->css_cache_name = 'flexiblev2:' . $renderer->name;
337
    if ($admin) {
338
      ctools_include('css');
339
      ctools_css_clear($renderer->css_cache_name);
340
    }
341
  }
342
  return $renderer;
343
}
344
345
/**
346
 * Draw the flexible layout.
347
 */
348
function theme_panels_flexible($vars) {
349
  $css_id = $vars['css_id'];
350
  $content = $vars['content'];
351
  $settings = $vars['settings'];
352
  $display = $vars['display'];
353
  $layout = $vars['layout'];
354
  $handler = $vars['renderer'];
355
356
  panels_flexible_convert_settings($settings, $layout);
357
358
  $renderer = panels_flexible_create_renderer(FALSE, $css_id, $content, $settings, $display, $layout, $handler);
359
360
  // CSS must be generated because it reports back left/middle/right
361
  // positions.
362
  $css = panels_flexible_render_css($renderer);
363
364
  if (!empty($renderer->css_cache_name) && empty($display->editing_layout)) {
365
    ctools_include('css');
366
    // Generate an id based upon rows + columns:
367
    $filename = ctools_css_retrieve($renderer->css_cache_name);
368
    if (!$filename) {
369
      $filename = ctools_css_store($renderer->css_cache_name, $css, FALSE);
370
    }
371
372
    // Give the CSS to the renderer to put where it wants.
373
    if ($handler) {
374
      $handler->add_css($filename, 'module', 'all', FALSE);
375
    }
376
    else {
377
      drupal_add_css($filename);
378
    }
379
  }
380
  else {
381
    // If the id is 'new' we can't reliably cache the CSS in the filesystem
382
    // because the display does not truly exist, so we'll stick it in the
383
    // head tag. We also do this if we've been told we're in the layout
384
    // editor so that it always gets fresh CSS.
385
    drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE));
386
  }
387
388
  // Also store the CSS on the display in case the live preview or something
389
  // needs it
390
  $display->add_css = $css;
391
392
  $output = "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clearfix\" $renderer->id_str>\n";
393
  $output .= "<div class=\"panel-flexible-inside " . $renderer->base['canvas'] . "-inside\">\n";
394
395
  $output .= panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], $renderer->base['canvas']);
396
397
  // Wrap the whole thing up nice and snug
398
  $output .= "</div>\n</div>\n";
399
400
  return $output;
401
}
402
403
/**
404
 * Draw the flexible layout.
405
 */
406
function theme_panels_flexible_admin($vars) {
407
  $css_id = $vars['css_id'];
408
  $content = $vars['content'];
409
  $settings = $vars['settings'];
410
  $display = $vars['display'];
411
  $layout = $vars['layout'];
412
  $handler = $vars['renderer'];
413
414
  // We never draw stored flexible layouts in admin mode; they must be edited
415
  // from the stored layout UI at that point.
416
  if (!empty($layout['layout'])) {
417
    return theme_panels_flexible(array('css_id' => $css_id, 'content' => $content, 'settings' => $settings, 'display' => $display, 'layout' => $layout, 'renderer' => $handler));
418
  }
419
420
  panels_flexible_convert_settings($settings, $layout);
421
  $renderer = panels_flexible_create_renderer(TRUE, $css_id, $content, $settings, $display, $layout, $handler);
422
423
  $css = panels_flexible_render_css($renderer);
424
425
  // For the administrative view, add CSS directly to head.
426
  drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE));
427
428
  if (empty($display->editing_layout)) {
429
    $output = '<input type="submit" id="panels-flexible-toggle-layout" class="form-submit" value ="' .
430
      t('Show layout designer') . '">';
431
    if (user_access('administer panels layouts')) {
432
      $output .= '<input type="hidden" class="panels-flexible-reuse-layout-url" value="' . url($handler->get_url('layout', 'reuse'), array('absolute' => TRUE)) . '">';
433
      $output .= '<input type="submit" id="panels-flexible-reuse-layout" class="form-submit ctools-use-modal" value ="' .
434
        t('Reuse layout') . '">';
435
    }
436
    $output .= "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clearfix panel-flexible-admin panel-flexible-no-edit-layout\" $renderer->id_str>\n";
437
  }
438
  else {
439
    $output = "<div class=\"panel-flexible " . $renderer->base['canvas'] . " clearfix panel-flexible-admin panel-flexible-edit-layout\" $renderer->id_str>\n";
440
  }
441
  $output .= "<div class=\"panel-flexible-inside " . $renderer->base['canvas'] . "-inside \">\n";
442
443
  $content = panels_flexible_render_items($renderer, $settings['items']['canvas']['children'], $renderer->base['row'] . '-canvas');
444
  $output .= panels_flexible_render_item($renderer, $settings['items']['canvas'], $content, 'canvas', 0, 0, TRUE);
445
446
  // Wrap the whole thing up nice and snug
447
  $output .= "</div>\n</div>\n";
448
449
  drupal_add_js($layout['path'] . '/flexible-admin.js');
450
  drupal_add_js(array('flexible' => array('resize' => url($handler->get_url('layout', 'resize'), array('absolute' => TRUE)))), 'setting');
451
  return $output;
452
}
453
454
/**
455
 * Render a piece of a flexible layout.
456
 */
457
function panels_flexible_render_items($renderer, $list, $owner_id) {
458
  $output = '';
459
  $groups = array('left' => '', 'middle' => '', 'right' => '');
460
  $max = count($list) - 1;
461
  $prev = NULL;
462
463
  foreach ($list as $position => $id) {
464
    $item = $renderer->settings['items'][$id];
465
    $location = isset($renderer->positions[$id]) ? $renderer->positions[$id] : 'middle';
466
467
    if ($renderer->admin && $item['type'] != 'row' && $prev ) {
468
      $groups[$location] .= panels_flexible_render_splitter($renderer, $prev, $id);
469
    }
470
471
    switch ($item['type']) {
472
      case 'column':
473
        $content = panels_flexible_render_items($renderer, $item['children'], $renderer->base['column'] . '-' . $id);
474 e4c061ad Assos Assos
        if (empty($renderer->settings['items'][$id]['hide_empty']) || trim($content)) {
475
          $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max);
476
        }
477 85ad3d82 Assos Assos
        break;
478
      case 'row':
479
        $content = panels_flexible_render_items($renderer, $item['children'], $renderer->base['row'] . '-' . $id);
480 e4c061ad Assos Assos
        if (empty($renderer->settings['items'][$id]['hide_empty']) || trim($content)) {
481
          $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max, TRUE);
482
        }
483 85ad3d82 Assos Assos
        break;
484
      case 'region':
485 e4c061ad Assos Assos
        if (empty($renderer->settings['items'][$id]['hide_empty'])) {
486
          $content = isset($renderer->content[$id]) ? $renderer->content[$id] : "&nbsp;";
487
        }
488
        else {
489
          $content = isset($renderer->content[$id]) ? trim($renderer->content[$id]) : "";
490
        }
491
        if (empty($renderer->settings['items'][$id]['hide_empty']) || $content) {
492
          $groups[$location] .= panels_flexible_render_item($renderer, $item, $content, $id, $position, $max);
493
        }
494 85ad3d82 Assos Assos
        break;
495
    }
496
497
    // If all items are fixed then we have a special splitter on the right to
498
    // control the overall width.
499
    if (!empty($renderer->admin) && $max == $position && $location == 'left') {
500
      $groups[$location] .= panels_flexible_render_splitter($renderer, $id, NULL);
501
    }
502
    $prev = $id;
503
  }
504
505
  $group_count = count(array_filter($groups));
506
507
  // Render each group. We only render the group div if we're in admin mode
508
  // or if there are multiple groups.
509
  foreach ($groups as $position => $content) {
510
    if (!empty($content) || $renderer->admin) {
511
      if ($group_count > 1 || $renderer->admin) {
512
        $output .= '<div class="' . $owner_id . '-' . $position . '">' . $content . '</div>';
513
      }
514
      else {
515
        $output .= $content;
516
      }
517
    }
518
  }
519
520
  return $output;
521
}
522
523
/**
524
 * Render a column in the flexible layout.
525
 */
526
function panels_flexible_render_item($renderer, $item, $content, $id, $position, $max, $clear = FALSE) {
527
528
  // If we are rendering a row and there is just one row, we don't need to
529
  // render the row unless there is fixed_width content inside it.
530
  if (empty($renderer->admin) && $item['type'] == 'row' && $max == 0) {
531
    $fixed = FALSE;
532
    foreach ($item['children'] as $id) {
533
      if ($renderer->settings['items'][$id]['width_type'] != '%') {
534
        $fixed = TRUE;
535
        break;
536
      }
537
    }
538
539
    if (!$fixed) {
540
      return $content;
541
    }
542
  }
543
544
  // If we are rendering a column and there is just one column, we don't
545
  // need to render the column unless it has a fixed_width.
546
  if (empty($renderer->admin) && $item['type'] == 'column' && $max == 0 && $item['width_type'] == '%') {
547
    return $content;
548
  }
549
550
  $base = $renderer->item_class[$item['type']];
551
  $output = '<div class="' . $base . ' ' . $renderer->base[$item['type']] . '-' . $id;
552
  if ($position == 0) {
553
    $output .= ' ' . $base . '-first';
554
  }
555
  if ($position == $max) {
556
    $output .= ' ' . $base . '-last';
557
  }
558
  if ($clear) {
559
    $output .= ' clearfix';
560
  }
561
562
  if (isset($item['class'])) {
563
    $output .= ' ' . check_plain($item['class']);
564
  }
565
566
  $output .= '">' . "\n";
567
568
  if (!empty($renderer->admin)) {
569
    $output .= panels_flexible_render_item_links($renderer, $id, $item);
570
  }
571
572
  $output .= '  <div class="inside ' . $base . '-inside ' . $base . '-' . $renderer->base_class . '-' . $id . '-inside';
573
  if ($position == 0) {
574
    $output .= ' ' . $base . '-inside-first';
575
  }
576
  if ($position == $max) {
577
    $output .= ' ' . $base . '-inside-last';
578
  }
579
  if ($clear) {
580
    $output .= ' clearfix';
581
  }
582
583
  $output .= "\">\n";
584
  $output .= $content;
585
  $output .= '  </div>' . "\n";
586
  $output .= '</div>' . "\n";
587
588
  return $output;
589
}
590
/**
591
 * Render a splitter div to place between the $left and $right items.
592
 *
593
 * If the right ID is NULL that means there isn't actually a box to the
594
 * right, but we need a splitter anyway. We'll mostly use info about the
595
 * left, but pretend it's 'fluid' so that the javascript won't actually
596
 * modify the right item.
597
 */
598
function panels_flexible_render_splitter($renderer, $left_id, $right_id) {
599
  $left = $renderer->settings['items'][$left_id];
600
601
  $left_class = $renderer->base[$left['type']] . '-' . $left_id;
602
  if ($right_id) {
603
    $right = $renderer->settings['items'][$right_id];
604
    $right_class = $renderer->base[$left['type']] . '-' . $right_id;
605
  }
606
  else {
607
    $right = $left;
608
    $right_class = $left_class;
609
  }
610
611
  $output = '<div tabindex="0"
612
    class="panels-flexible-splitter flexible-splitter-for-' . $left_class . '">';
613
614
  // Name the left object
615
  $output .= '<span class="panels-flexible-splitter-left">';
616
  $output .= '.' . $left_class;
617
  $output .= '</span>';
618
619
  $output .= '<span class="panels-flexible-splitter-left-id">';
620
  $output .= $left_id;
621
  $output .= '</span>';
622
623
  $output .= '<span class="panels-flexible-splitter-left-width ' . $left_class . '-width">';
624
  $output .= $left['width'];
625
  $output .= '</span>';
626
627
  $output .= '<span class="panels-flexible-splitter-left-scale">';
628
  $output .= isset($renderer->scale[$left_id]) ? $renderer->scale[$left_id] : 1;
629
  $output .= '</span>';
630
631
  $output .= '<span class="panels-flexible-splitter-left-width-type">';
632
  $output .= $left['width_type'];
633
  $output .= '</span>';
634
635
  // Name the right object
636
  $output .= '<span class="panels-flexible-splitter-right">';
637
  $output .= '.' . $right_class;
638
  $output .= '</span>';
639
640
  $output .= '<span class="panels-flexible-splitter-right-id">';
641
  $output .= $right_id;
642
  $output .= '</span>';
643
644
  $output .= '<span class="panels-flexible-splitter-right-width ' . $right_class . '-width">';
645
  $output .= $right['width'];
646
  $output .= '</span>';
647
648
  $output .= '<span class="panels-flexible-splitter-right-scale">';
649
  $output .= isset($renderer->scale[$right_id]) ? $renderer->scale[$right_id] : 1;
650
  $output .= '</span>';
651
652
  $output .= '<span class="panels-flexible-splitter-right-width-type">';
653
  // If there is no right, make it fluid.
654
  $output .= $right_id ? $right['width_type'] : '%';
655
  $output .= '</span>';
656
657
  $output .= '</div>';
658
  return $output;
659
}
660
661
/**
662
 * Render the dropdown links for an item.
663
 */
664
function panels_flexible_render_item_links($renderer, $id, $item) {
665
  $links = array();
666
  $remove = '';
667
  $add = '';
668
  if ($item['type'] == 'column') {
669
    $title = t('Column');
670
    $settings = t('Column settings');
671
    if (empty($item['children'])) {
672
      $remove = t('Remove column');
673
      $add = t('Add row');
674
    }
675
    else {
676
      $add = t('Add row to top');
677
      $add2 = t('Add row to bottom');
678
    }
679
  }
680
  else if ($item['type'] == 'row') {
681
    if ($id == 'canvas') {
682
      $title = t('Canvas');
683
      $settings = t('Canvas settings');
684
    }
685
    else {
686
      $title = t('Row');
687
      $settings = t('Row settings');
688
    }
689
    if (empty($item['children'])) {
690
      if ($id != 'canvas') {
691
        $remove = t('Remove row');
692
      }
693
      $add = $item['contains'] == 'region' ? t('Add region') : t('Add column');
694
    }
695
    else {
696
      $add = $item['contains'] == 'region' ? t('Add region to left') : t('Add column to left');
697
      $add2 = $item['contains'] == 'region' ? t('Add region to right') : t('Add column to right');
698
    }
699
  }
700
  else if ($item['type'] == 'region') {
701
    $title = t('Region');
702
    $settings = t('Region settings');
703
    $remove = t('Remove region');
704
  }
705
706
  if (!empty($settings)) {
707
    $links[] = array(
708
      'title' => $settings,
709
      'href' => $renderer->handler->get_url('layout', 'settings', $id),
710
      'attributes' => array('class' => array('ctools-use-modal')),
711
    );
712
  }
713
  if ($add) {
714
    $links[] = array(
715
      'title' => $add,
716
      'href' => $renderer->handler->get_url('layout', 'add', $id),
717
      'attributes' => array('class' => array('ctools-use-modal')),
718
    );
719
  }
720
  if (isset($add2)) {
721
    $links[] = array(
722
      'title' => $add2,
723
      'href' => $renderer->handler->get_url('layout', 'add', $id, 'right'),
724
      'attributes' => array('class' => array('ctools-use-modal')),
725
    );
726
  }
727
  if ($remove) {
728
    $links[] = array(
729
      'title' => $remove,
730
      'href' => $renderer->handler->get_url('layout', 'remove', $id),
731
      'attributes' => array('class' => array('use-ajax')),
732
    );
733
  }
734
735
  return theme('ctools_dropdown', array('title' => $title, 'links' => $links, 'class' => 'flexible-layout-only flexible-links flexible-title flexible-links-' . $id));
736
}
737
/**
738
 * Provide CSS for a flexible layout.
739
 */
740
function panels_flexible_render_css($renderer) {
741
  if ($renderer->admin) {
742
    $parent_class = '.' . $renderer->base['row'] . '-canvas';
743
  }
744
  else {
745
    $parent_class = '.' . $renderer->base['canvas'];
746
  }
747
  return panels_flexible_render_css_group($renderer, $renderer->settings['items']['canvas']['children'], $parent_class, 'column', 'canvas');
748
}
749
750
/**
751
 * Render the CSS for a group of items to be displayed together.
752
 *
753
 * Columns and regions, when displayed as a group, need to cooperate in
754
 * order to share margins and make sure that percent widths add up
755
 * to the right total.
756
 */
757
function panels_flexible_render_css_group($renderer, $list, $owner_id, $type, $id) {
758
  $css = array();
759
760
  // Start off with some generic CSS to properly pad regions
761 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['region']] = array(
762 85ad3d82 Assos Assos
    'padding' => '0',
763
  );
764
765 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['region'] . '-inside'] = array(
766 85ad3d82 Assos Assos
    'padding-right' => $renderer->region_separation,
767
    'padding-left' => $renderer->region_separation,
768
  );
769
770 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['region'] . '-inside-first'] = array(
771 85ad3d82 Assos Assos
    'padding-left' => '0',
772
  );
773
774 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['region'] . '-inside-last'] = array(
775 85ad3d82 Assos Assos
    'padding-right' => '0',
776
  );
777
778 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['column']] = array(
779 85ad3d82 Assos Assos
    'padding' => '0',
780
  );
781
782 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['column'] . '-inside'] = array(
783 85ad3d82 Assos Assos
    'padding-right' => $renderer->column_separation,
784
    'padding-left' => $renderer->column_separation,
785
  );
786
787 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['column'] . '-inside-first'] = array(
788 85ad3d82 Assos Assos
    'padding-left' => '0',
789
  );
790
791 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['column'] . '-inside-last'] = array(
792 85ad3d82 Assos Assos
    'padding-right' => '0',
793
  );
794
795
  // And properly pad rows too
796 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['row']] = array(
797 85ad3d82 Assos Assos
    'padding' => '0 0 ' . $renderer->row_separation . ' 0',
798
    'margin' => '0',
799
  );
800
801 5a7e6170 Florent Torregrosa
  $css[$owner_id . ' .' . $renderer->item_class['row'] . '-last'] = array(
802 85ad3d82 Assos Assos
    'padding-bottom' => '0',
803
  );
804
805
  panels_flexible_get_css_group($css, $renderer, $list, $owner_id, $type, $id);
806
807
  ctools_include('css');
808
  return ctools_css_assemble($css);
809
}
810
811
/**
812
 * Construct an array with all of the CSS properties for a group.
813
 *
814
 * This will parse down into children and produce all of the CSS needed if you
815
 * start from the top.
816
 */
817
function panels_flexible_get_css_group(&$css, $renderer, $list, $owner_id, $type, $item_id) {
818
  if ($type != 'row') {
819
    // Go through our items and break up into right/center/right groups so we
820
    // can figure out our offsets.
821
822
    // right == any items on the right that are 'fixed'.
823
    // middle == all fluid items.
824
    // right == any items on the right that are 'fixed'.
825
    $left = $middle = $right = array();
826
    $left_total = $right_total = $middle_total = 0;
827
    $current = 'left';
828
    foreach ($list as $id) {
829
      if ($renderer->settings['items'][$id]['width_type'] == 'px') {
830
        // fixed
831
        if ($current == 'left') {
832
          $left[] = $id;
833
          $renderer->positions[$id] = 'left';
834
          $left_total += $renderer->settings['items'][$id]['width'];
835
        }
836
        else {
837
          $current = 'right';
838
          $right[] = $id;
839
          $renderer->positions[$id] = 'right';
840
          $right_total += $renderer->settings['items'][$id]['width'];
841
        }
842
      }
843
      else {
844
        // fluid
845
        if ($current != 'right') {
846
          $current = 'middle';
847
          $middle[] = $id;
848
          $renderer->positions[$id] = 'middle';
849
          $middle_total += $renderer->settings['items'][$id]['width'];
850
        }
851
        // fall through: if current is 'right' and we ran into a 'fluid' then
852
        // it gets *dropped* because that is invalid.
853
      }
854
    }
855
856
    // Go through our right sides and create CSS.
857
    foreach ($left as $id) {
858
      $class = "." . $renderer->base[$type] . "-$id";
859
      $css[$class] = array(
860
        'position' => 'relative',
861
        'float' => 'left',
862
        'background-color' => 'transparent',
863
        'width' => $renderer->settings['items'][$id]['width'] . "px",
864
      );
865
    }
866
867
    // Do the same for right.
868
    $right_pixels = 0;
869
870
    foreach ($right as $id) {
871
      $class = "." . $renderer->base[$type] . "-$id";
872
      $css[$class] = array(
873
        'float' => 'left',
874
        'width' => $renderer->settings['items'][$id]['width'] . "px",
875
      );
876
    }
877
878
    $max = count($middle) - 1;
879
880
    if ($middle_total) {
881
      // Because we love IE so much, auto scale everything to 99%. This
882
      // means adding up the actual widths and then providing a multiplier
883
      // to each so that the total is 99%.
884
      $scale = $renderer->scale_base / $middle_total;
885
      foreach ($middle as $position => $id) {
886
        $class = "." . $renderer->base[$type] . "-$id";
887
        $css[$class] = array(
888
          'float' => 'left',
889
          'width' => number_format($renderer->settings['items'][$id]['width'] * $scale, 4, '.', '') . "%",
890
        );
891
892
        // Store this so we can use it later.
893
        // @todo: Store the scale, not the new width, so .js can adjust
894
        // bi-directionally.
895
        $renderer->scale[$id] = $scale;
896
      }
897
    }
898
899
    // If there is any total remaining, we need to offset the splitter
900
    // by this much too.
901
    if ($left_total) {
902
      // Add this even if it's 0 so we can handle removals.
903
      $css["$owner_id-inside"]['padding-left'] = '0px';
904
      if ($renderer->admin || count($middle)) {
905
        $css["$owner_id-middle"]['margin-left'] = $left_total . 'px';
906
        // IE hack
907
        $css["* html $owner_id-left"]['left'] = $left_total . "px";
908
        // Make this one very specific to the admin CSS so that preview
909
        // does not stomp it.
910
        $css[".panel-flexible-admin $owner_id-inside"]['padding-left'] = '0px';
911
      }
912
      else {
913
        $css["$owner_id-inside"]['margin-left'] = '-' . $left_total . 'px';
914
        $css["$owner_id-inside"]['padding-left'] = $left_total . 'px';
915
        // IE hack
916
        $css["* html $owner_id-inside"]['left'] = $left_total . "px";
917
      }
918
    }
919
    if ($right_total) {
920
      $css["$owner_id-middle"]['margin-right'] = $right_total . 'px';
921
    }
922
    $css["$owner_id-inside"]['padding-right'] = '0px';
923
  }
924
925
  // If the canvas has a fixed width set, and this is the canvas, fix the
926
  // width.
927
  if ($item_id == 'canvas') {
928
    $item = $renderer->settings['items'][$item_id];
929
930
    if (!empty($item['fixed_width']) && intval($item['fixed_width'])) {
931
      $css['.' . $renderer->base['canvas']]['width'] = intval($item['fixed_width']) . 'px';
932
    }
933
    else {
934
      $css['.' . $renderer->base['canvas']]['width'] = 'auto';
935
    }
936
  }
937
938
  // Go through each item and process children.
939
  foreach ($list as $id) {
940
    $item = $renderer->settings['items'][$id];
941
    if (empty($item['children'])) {
942
      continue;
943
    }
944
945
    if ($type == 'column') {
946
      // Columns can only contain rows.
947
      $child_type = 'row';
948
    }
949
    else {
950
      $child_type = isset($item['contains']) ? $item['contains'] : 'region';
951
    }
952
953
    $class = "." . $renderer->base[$type] . "-$id";
954
    panels_flexible_get_css_group($css, $renderer, $item['children'], $class, $child_type, $id);
955
  }
956
}
957
958
/**
959
 * AJAX responder to edit flexible settings for an item.
960
 *
961
 * $handler object
962
 *   The display renderer handler object.
963
 */
964
function panels_ajax_flexible_edit_settings($handler, $id) {
965
  $settings = &$handler->display->layout_settings;
966
  panels_flexible_convert_settings($settings, $handler->plugins['layout']);
967
968
  if (empty($settings['items'][$id])) {
969
    ctools_modal_render(t('Error'), t('Invalid item id.'));
970
  }
971
972
  $item = &$settings['items'][$id];
973
  $siblings = array();
974
975
  if ($id != 'canvas') {
976
    $siblings = $settings['items'][$item['parent']]['children'];
977
  }
978
979
980
  switch ($item['type']) {
981
    case 'column':
982
      $title = t('Configure column');
983
      break;
984
    case 'row':
985
      if ($id == 'canvas') {
986
        $title = t('Configure canvas');
987
      }
988
      else {
989
        $title = t('Configure row');
990
      }
991
      break;
992
    case 'region':
993
      $title = t('Configure region');
994
      break;
995
  }
996
997
  $form_state = array(
998
    'display' => &$handler->display,
999
    'item' => &$item,
1000
    'id' => $id,
1001
    'siblings' => $siblings,
1002
    'settings' => &$settings,
1003
    'ajax' => TRUE,
1004
    'title' => $title,
1005
    'op' => 'edit',
1006
  );
1007
1008
  $output = ctools_modal_form_wrapper('panels_flexible_config_item_form', $form_state);
1009
  if (!empty($form_state['executed'])) {
1010
    // If the width type changed then other nearby items will have
1011
    // to have their widths adjusted.
1012
    panels_edit_cache_set($handler->cache);
1013
1014
    $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
1015
    $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
1016
1017
    $output = array();
1018
    // If the item is a region, replace the title.
1019
    $class = $renderer->base[$item['type']] . '-' . $id;
1020
    if ($item['type'] == 'region') {
1021
      $output[] = ajax_command_replace(".$class h2.label",
1022
        '<h2 class="label">' . check_plain($item['title']) . '</h2>');
1023
    }
1024
1025
    // Rerender our links in case something changed.
1026
    $output[] = ajax_command_replace('.flexible-links-' . $id,
1027
      panels_flexible_render_item_links($renderer, $id, $item));
1028
1029
    // If editing the canvas, reset the CSS width
1030
    if ($id == 'canvas') {
1031
      // update canvas CSS.
1032
      $css = array(
1033
        '.' . $renderer->item_class['column'] . '-inside' => array(
1034
          'padding-left' => $renderer->column_separation,
1035
          'padding-right' => $renderer->column_separation,
1036
        ),
1037
        '.' . $renderer->item_class['region'] . '-inside' => array(
1038
          'padding-left' => $renderer->region_separation,
1039
          'padding-right' => $renderer->region_separation,
1040
        ),
1041
        '.' . $renderer->item_class['row'] => array(
1042
          'padding-bottom' => $renderer->row_separation,
1043
        ),
1044
      );
1045
      if (!empty($item['fixed_width']) && intval($item['fixed_width'])) {
1046
        $css['.' . $renderer->base['canvas']] = array('width' => intval($item['fixed_width']) . 'px');
1047
      }
1048
      else {
1049
        $css['.' . $renderer->base['canvas']] = array('width' => 'auto');
1050
      }
1051
      foreach ($css as $selector => $data) {
1052
        $output[] = ajax_command_css($selector, $data);
1053
      }
1054
    }
1055
1056
    $output[] = ctools_modal_command_dismiss();
1057
  }
1058
1059
  $handler->commands = $output;
1060
}
1061
1062
/**
1063
 * Configure a row, column or region on the flexible page.
1064
 *
1065
 * @param <type> $form_state
1066
 * @return <type>
1067
 */
1068
function panels_flexible_config_item_form($form, &$form_state) {
1069
  $display = &$form_state['display'];
1070
  $item = &$form_state['item'];
1071
  $siblings = &$form_state['siblings'];
1072
  $settings = &$form_state['settings'];
1073
  $id = &$form_state['id'];
1074
1075
  if ($item['type'] == 'region') {
1076
    $form['title'] = array(
1077
      '#title' => t('Region title'),
1078
      '#type' => 'textfield',
1079
      '#default_value' => $item['title'],
1080
      '#required' => TRUE,
1081
    );
1082
  }
1083
1084
  if ($id == 'canvas') {
1085
    $form['class'] = array(
1086
      '#title' => t('Canvas class'),
1087
      '#type' => 'textfield',
1088
      '#default_value' => isset($item['class']) ? $item['class'] : '',
1089
      '#description' => t('This class will the primary class for this layout. It will also be appended to all column, row and region_classes to ensure that layouts within layouts will not inherit CSS from each other. If left blank, the name of the layout or ID of the display will be used.'),
1090
    );
1091
1092
    $form['column_class'] = array(
1093
      '#title' => t('Column class'),
1094
      '#type' => 'textfield',
1095
      '#default_value' => isset($item['column_class']) ? $item['column_class'] : '',
1096
      '#description' => t('This class will be applied to all columns of the layout. If left blank this will be panels-flexible-column.'),
1097
    );
1098
1099
    $form['row_class'] = array(
1100
      '#title' => t('Row class'),
1101
      '#type' => 'textfield',
1102
      '#default_value' => isset($item['row_class']) ? $item['row_class'] : '',
1103
      '#description' => t('This class will be applied to all rows of the layout. If left blank this will be panels-flexible-row.'),
1104
    );
1105
1106
    $form['region_class'] = array(
1107
      '#title' => t('Region class'),
1108
      '#type' => 'textfield',
1109
      '#default_value' => isset($item['region_class']) ? $item['region_class'] : '',
1110
      '#description' => t('This class will be applied to all regions of the layout. If left blank this will be panels-flexible-region.'),
1111
    );
1112
1113
    $form['no_scale'] = array(
1114
      '#type' => 'checkbox',
1115
      '#title' => t('Scale fluid widths for IE6'),
1116
      '#description' => t('IE6 does not do well with 100% widths. If checked, width will be scaled to 99% to compensate.'),
1117
      '#default_value' => empty($item['no_scale']),
1118
    );
1119
1120
    $form['fixed_width'] = array(
1121
      '#type' => 'textfield',
1122
      '#title' => t('Fixed width'),
1123
      '#description' => t('If a value is entered, the layout canvas will be fixed to the given pixel width.'),
1124
      '#default_value' => isset($item['fixed_width']) ? $item['fixed_width'] : '',
1125
    );
1126
1127
    $form['column_separation'] = array(
1128
      '#type' => 'textfield',
1129
      '#title' => t('Column separation'),
1130
      '#description' => t('How much padding to put on columns that are that are next to other columns. Note that this is put on both columns so the real amount is doubled.'),
1131
      '#default_value' => isset($item['column_separation']) ? $item['column_separation'] : '0.5em',
1132
    );
1133
1134
    $form['region_separation'] = array(
1135
      '#type' => 'textfield',
1136
      '#title' => t('Region separation'),
1137
      '#description' => t('How much padding to put on regions that are that are next to other regions. Note that this is put on both regions so the real amount is doubled.'),
1138
      '#default_value' => isset($item['region_separation']) ? $item['region_separation'] : '0.5em',
1139
    );
1140
1141
    $form['row_separation'] = array(
1142
      '#type' => 'textfield',
1143
      '#title' => t('Row separation'),
1144
      '#description' => t('How much padding to put on beneath rows to separate them from each other. Because this is placed only on the bottom, not hte top, this is NOT doubled like column/region separation.'),
1145
      '#default_value' => isset($item['row_separation']) ? $item['row_separation'] : '0.5em',
1146
    );
1147
  }
1148
  else {
1149
    $form['class'] = array(
1150
      '#title' => t('CSS class'),
1151
      '#type' => 'textfield',
1152
      '#default_value' => isset($item['class']) ? $item['class'] : '',
1153
      '#description' => t('Enter a CSS class that will be used. This can be used to apply automatic styling from your theme, for example.'),
1154
    );
1155
1156
    if ($item['type'] != 'row') {
1157
      // Test to see if there are fluid items to the left or the right. If there
1158
      // are fluid items on both sides, this item cannot be set to fixed.
1159
      $left = $right = FALSE;
1160
      $current = 'left';
1161
      foreach ($siblings as $sibling) {
1162
        if ($sibling == $id) {
1163
          $current = 'right';
1164
        }
1165
        else if ($settings['items'][$sibling]['width_type'] == '%') {
1166
          $$current = TRUE; // Indirection.
1167
        }
1168
      }
1169
1170
      $form['width_type'] = array(
1171
        '#type' => 'select',
1172
        '#title' => t('Width'),
1173
        '#default_value' => $item['width_type'],
1174
        '#options' => array(
1175
          '%' => t('Fluid'),
1176
          'px' => t('Fixed'),
1177
        ),
1178
        '#disabled' => TRUE,
1179
      );
1180
    }
1181
    else {
1182
      $form['contains'] = array(
1183
        '#type' => 'select',
1184
        '#title' => t('Contains'),
1185
        '#default_value' => $item['contains'],
1186
        '#options' => array(
1187
          'region' => t('Regions'),
1188
          'column' => t('Columns'),
1189
        ),
1190
      );
1191
1192
      if (!empty($item['children'])) {
1193
        $form['contains']['#disabled'] = TRUE;
1194
        $form['contains']['#value'] = $item['contains'];
1195
        $form['contains']['#description'] = t('You must remove contained items to change the row container type.');
1196
      }
1197
    }
1198
  }
1199
1200 e4c061ad Assos Assos
  $form['hide_empty'] = array(
1201
    '#title' => t('Hide element if empty'),
1202
    '#type' => 'checkbox',
1203
    '#default_value' => !empty($item['hide_empty']) ? 1 : 0,
1204
  );
1205
1206 85ad3d82 Assos Assos
  $form['save'] = array(
1207
    '#type' => 'submit',
1208
    '#value' => t('Save'),
1209
  );
1210
1211
  return $form;
1212
}
1213
1214
/**
1215
 * Submit handler for editing a flexible item.
1216
 */
1217
function panels_flexible_config_item_form_submit(&$form, &$form_state) {
1218
  $item = &$form_state['item'];
1219
  if ($item['type'] == 'region') {
1220
    $item['title'] = $form_state['values']['title'];
1221
  }
1222
1223
  $item['class'] = $form_state['values']['class'];
1224
1225
  if ($form_state['id'] == 'canvas') {
1226
    $item['column_class'] = $form_state['values']['column_class'];
1227
    $item['row_class'] = $form_state['values']['row_class'];
1228
    $item['region_class'] = $form_state['values']['region_class'];
1229
    // Reverse this as the checkbox is backward from how we actually store
1230
    // it to make it simpler to default to scaling.
1231
    $item['no_scale'] = !$form_state['values']['no_scale'];
1232
    $item['fixed_width'] = $form_state['values']['fixed_width'];
1233
    $item['column_separation'] = $form_state['values']['column_separation'];
1234
    $item['region_separation'] = $form_state['values']['region_separation'];
1235
    $item['row_separation'] = $form_state['values']['row_separation'];
1236
  }
1237
  else if ($item['type'] != 'row') {
1238
    $item['width_type'] = $form_state['values']['width_type'];
1239
  }
1240
  else {
1241
    $item['contains'] = $form_state['values']['contains'];
1242
  }
1243 e4c061ad Assos Assos
  $item['hide_empty'] = $form_state['values']['hide_empty'];
1244 85ad3d82 Assos Assos
1245
}
1246
1247
/**
1248
 * AJAX responder to add a new row, column or region to a flexible layout.
1249
 */
1250
function panels_ajax_flexible_edit_add($handler, $id, $location = 'left') {
1251
  ctools_include('modal');
1252
  ctools_include('ajax');
1253
  $settings = &$handler->display->layout_settings;
1254
  panels_flexible_convert_settings($settings, $handler->plugins['layout']);
1255
1256
  if (empty($settings['items'][$id])) {
1257
    ctools_modal_render(t('Error'), t('Invalid item id.'));
1258
  }
1259
1260
  $parent = &$settings['items'][$id];
1261
1262
  switch ($parent['type']) {
1263
    case 'column':
1264
      $title = t('Add row');
1265
      // Create the new item with defaults.
1266
      $item = array(
1267
        'type' => 'row',
1268
        'contains' => 'region',
1269
        'children' => array(),
1270
        'parent' => $id,
1271
      );
1272
      break;
1273
    case 'row':
1274
      switch ($parent['contains']) {
1275
        case 'region':
1276
          $title = $location == 'left' ? t('Add region to left') : t('Add region to right');
1277
          $item = array(
1278
            'type' => 'region',
1279
            'title' => '',
1280
            'width' => 100,
1281
            'width_type' => '%',
1282
            'parent' => $id,
1283
          );
1284
          break;
1285
        case 'column':
1286
          $title = $location == 'left' ? t('Add column to left') : t('Add column to right');
1287
          $item = array(
1288
            'type' => 'column',
1289
            'width' => 100,
1290
            'width_type' => '%',
1291
            'parent' => $id,
1292
            'children' => array(),
1293
          );
1294
          break;
1295
      }
1296
      // Create the new item with defaults.
1297
      break;
1298
    case 'region':
1299
      // Cannot add items to regions.
1300
      break;
1301
  }
1302
1303
  $form_state = array(
1304
    'display' => &$handler->display,
1305
    'parent' => &$parent,
1306
    'item' => &$item,
1307
    'id' => $id,
1308
    'settings' => &$settings,
1309
    'ajax' => TRUE,
1310
    'title' => $title,
1311
    'location' => $location,
1312
  );
1313
1314
  $output = ctools_modal_form_wrapper('panels_flexible_add_item_form', $form_state);
1315
  if (!empty($form_state['executed'])) {
1316
    // If the width type changed then other nearby items will have
1317
    // to have their widths adjusted.
1318
    panels_edit_cache_set($handler->cache);
1319
    $output = array();
1320
1321
    $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
1322
    // Create a renderer object so we can render our new stuff.
1323
    $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
1324
1325
    $content = '';
1326
    if ($item['type'] == 'region') {
1327
      $handler->plugins['layout']['regions'][$form_state['key']] = $item['title'];
1328
1329
      $content = $handler->render_region($form_state['key'], array());
1330
1331
      // Manually add the hidden field that our region uses to store pane info.
1332
      $content .= '<input type="hidden" name="panel[pane][' .
1333
        $form_state['key'] . ']" value="" />';
1334
1335
    }
1336
    else {
1337
      // We need to make sure the left/middle/right divs exist inside this
1338
      // so that more stuff can be added inside it as needed.
1339
      foreach (array('left', 'middle', 'right') as $position) {
1340
        if (!empty($content) || $renderer->admin) {
1341
          $content .= '<div class="' . $renderer->base[$item['type']] . '-' . $form_state['key'] . '-' . $position . '"></div>';
1342
        }
1343
      }
1344
1345
    }
1346
1347
    // render the item
1348
    $parent_class =  $renderer->base[$parent['type']] . '-' . $id;
1349
    $item_output = panels_flexible_render_item($renderer, $item, $content, $form_state['key'], 0, 0, $item['type'] == 'row');
1350
1351
    // Get all the CSS necessary for the entire row (as width adjustments may
1352
    // have cascaded).
1353
    $css = array();
1354
    panels_flexible_get_css_group($css, $renderer, $parent['children'], '.' . $parent_class, $item['type'], $id);
1355
1356
    $position = isset($renderer->positions[$form_state['key']]) ? $renderer->positions[$form_state['key']] : 'middle';
1357
    // If there's a nearby item, add the splitter and rewrite the width
1358
    // of the nearby item as it probably got adjusted.
1359
    // The blocks of code in this else look very similar but are not actually
1360
    // duplicated because the order changes based on left or right.
1361
    switch ($position) {
1362
      case 'left':
1363
        if ($location == 'left') {
1364
          $item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']);
1365
          $output[] = ajax_command_prepend('#panels-dnd-main .' . $parent_class . '-left', $item_output);
1366
        }
1367
        else if ($location == 'right') {
1368
          // If we are adding to the right side of the left box, there is
1369
          // a splitter that we have to remove; then we add our box normally,
1370
          // and then add a new splitter for just our guy.
1371
          $output[] = ajax_command_remove('panels-flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $form_state['key']);
1372
          $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .  $item_output;
1373
          $item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], NULL);
1374
          $output[] = ajax_command_append('#panels-dnd-main .' . $parent_class . '-left', $item_output);
1375
        }
1376
        break;
1377
      case 'right':
1378
        if (!empty($form_state['sibling'])) {
1379
          $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .  $item_output;
1380
        }
1381
        $output[] = ajax_command_append('#panels-dnd-main .' . $parent_class . '-right', $item_output);
1382
        break;
1383
      case 'middle':
1384
        if ($location == 'left') {
1385
          if (!empty($form_state['sibling'])) {
1386
            $item_output .= panels_flexible_render_splitter($renderer, $form_state['key'], $form_state['sibling']);
1387
          }
1388
          $output[] = ajax_command_prepend('#panels-dnd-main .' . $parent_class . '-middle', $item_output);
1389
        }
1390
        else {
1391
          if (!empty($form_state['sibling'])) {
1392
            $item_output = panels_flexible_render_splitter($renderer, $form_state['sibling'], $form_state['key']) .  $item_output;
1393
          }
1394
          $output[] = ajax_command_append('#panels-dnd-main .' . $parent_class . '-middle', $item_output);
1395
        }
1396
        break;
1397
1398
    }
1399
1400
    // Send our fix height command.
1401
    $output[] = array('command' => 'flexible_fix_height');
1402
1403
    if (!empty($form_state['sibling'])) {
1404
      $sibling_width = '#panels-dnd-main .' . $renderer->base[$item['type']] . '-' . $form_state['sibling'] . '-width';
1405
      $output[] = array(
1406
        'command' => 'flexible_set_width',
1407
        'selector' => $sibling_width,
1408
        'width' => $settings['items'][$form_state['sibling']]['width'],
1409
      );
1410
    }
1411
    foreach ($css as $selector => $data) {
1412
      $output[] = ajax_command_css($selector, $data);
1413
    }
1414
1415
    // Rerender our parent item links:
1416
    $output[] = ajax_command_replace('.flexible-links-' . $id,
1417
      panels_flexible_render_item_links($renderer, $id, $parent));
1418
1419
    $output[] = array(
1420
      'command' => 'flexible_fix_firstlast',
1421
      'selector' => '.' . $parent_class . '-inside',
1422
      'base' => 'panels-flexible-' . $item['type'],
1423
    );
1424
1425
    $output[] = ctools_modal_command_dismiss();
1426
  }
1427
1428
  $handler->commands = $output;
1429
}
1430
/**
1431
 * Form to add a row, column or region to a flexible layout.
1432
 * @param <type> $form_state
1433
 * @return <type>
1434
 */
1435
function panels_flexible_add_item_form($form, &$form_state) {
1436
  $display = &$form_state['display'];
1437
  $item = &$form_state['item'];
1438
  $parent = &$form_state['parent'];
1439
  $settings = &$form_state['settings'];
1440
  $location = &$form_state['location'];
1441
  $id = &$form_state['id'];
1442
1443
  if ($item['type'] == 'region') {
1444
    $form['title'] = array(
1445
      '#title' => t('Region title'),
1446
      '#type' => 'textfield',
1447
      '#default_value' => $item['title'],
1448
      '#required' => TRUE,
1449
    );
1450
  }
1451
1452
  $form['class'] = array(
1453
    '#title' => t('CSS Class'),
1454
    '#type' => 'textfield',
1455
    '#default_value' => isset($item['class']) ? $item['class'] : '',
1456
    '#description' => t('Enter a CSS class that will be used. This can be used to apply automatic styling from your theme, for example.'),
1457
  );
1458
1459
  if ($item['type'] != 'row') {
1460
    // If there is a 'fixed' type on the side we're adding to, then this
1461
    // must also be fixed. Otherwise it can be either and should default to
1462
    // fluid.
1463
    $restrict = FALSE;
1464
1465
    if (!empty($parent['children'])) {
1466
      if ($location == 'left') {
1467
        $sibling = reset($parent['children']);
1468
      }
1469
      else {
1470
        $sibling = end($parent['children']);
1471
      }
1472
      if ($settings['items'][$sibling]['width_type'] == 'px') {
1473
        $restrict = TRUE;
1474
        $item['width_type'] = 'px';
1475
      }
1476
    }
1477
1478
    $form['width_type'] = array(
1479
      '#type' => 'select',
1480
      '#title' => t('Width'),
1481
      '#default_value' => $item['width_type'],
1482
      '#options' => array(
1483
        '%' => t('Fluid'),
1484
        'px' => t('Fixed'),
1485
      ),
1486
      '#disabled' => $restrict,
1487
    );
1488
    if ($restrict) {
1489
      // This forces the value because disabled items don't always send
1490
      // their data back.
1491
      $form['width_type']['#value'] = $item['width_type'];
1492
      $form['width_type']['#description'] = t('Items cannot be set to fluid if there are fixed items already on that side.');
1493
    }
1494
  }
1495
  else {
1496
    $form['contains'] = array(
1497
      '#type' => 'select',
1498
      '#title' => t('Contains'),
1499
      '#default_value' => $item['contains'],
1500
      '#options' => array(
1501
        'region' => t('Regions'),
1502
        'column' => t('Columns'),
1503
      ),
1504
    );
1505
  }
1506
1507 e4c061ad Assos Assos
  $form['hide_empty'] = array(
1508
    '#title' => t('Hide element if empty'),
1509
    '#type' => 'checkbox',
1510
    '#default_value' => 0,
1511
  );
1512
1513 85ad3d82 Assos Assos
  $form['save'] = array(
1514
    '#type' => 'submit',
1515
    '#value' => t('Save'),
1516
  );
1517
1518
  return $form;
1519
}
1520
1521
/**
1522
 * Submit handler for editing a flexible item.
1523
 */
1524
function panels_flexible_add_item_form_submit(&$form, &$form_state) {
1525
  $item = &$form_state['item'];
1526
  $parent = &$form_state['parent'];
1527
  $location = &$form_state['location'];
1528
  $settings = &$form_state['settings'];
1529
1530
  $item['class'] = $form_state['values']['class'];
1531
1532
  if ($item['type'] == 'region') {
1533
    $item['title'] = $form_state['values']['title'];
1534
  }
1535
1536
  if ($item['type'] != 'row') {
1537
    $item['width_type'] = $form_state['values']['width_type'];
1538
  }
1539
  else {
1540
    $item['contains'] = $form_state['values']['contains'];
1541
  }
1542
1543 e4c061ad Assos Assos
  $item['hide_empty'] = $form_state['values']['hide_empty'];
1544
1545 85ad3d82 Assos Assos
  if ($item['type'] == 'region') {
1546
    // derive the region key from the title
1547
    $key = preg_replace("/[^a-z0-9]/", '_', drupal_strtolower($item['title']));
1548
    while (isset($settings['items'][$key])) {
1549
      $key .= '_';
1550
    }
1551
    $form_state['key'] = $key;
1552
  }
1553
  else {
1554
    $form_state['key'] = $key = max(array_keys($settings['items'])) + 1;
1555
  }
1556
1557
  $form_state['sibling'] = NULL;
1558
  if ($item['type'] != 'row' && !empty($parent['children'])) {
1559
    // Figure out what the width should be and adjust our sibling if
1560
    // necessary.
1561
    if ($location == 'left') {
1562
      $form_state['sibling'] = reset($parent['children']);
1563
    }
1564
    else {
1565
      $form_state['sibling'] = end($parent['children']);
1566
1567
    }
1568
1569
    // If there is no sibling, or the sibling is of a different type,
1570
    // the default 100 will work for either fixed or fluid.
1571
    if ($form_state['sibling'] && $settings['items'][$form_state['sibling']]['width_type'] == $item['width_type']) {
1572
      // steal half of the sibling's space.
1573
      $width = $settings['items'][$form_state['sibling']]['width'] / 2;
1574
      $settings['items'][$form_state['sibling']]['width'] = $width;
1575
      $item['width'] = $width;
1576
    }
1577
  }
1578
1579
  // Place the item.
1580
  $settings['items'][$key] = $item;
1581
  if ($location == 'left') {
1582
    array_unshift($parent['children'], $key);
1583
  }
1584
  else {
1585
    $parent['children'][] = $key;
1586
  }
1587
}
1588
1589
/**
1590
 * AJAX responder to remove an existing row, column or region from a flexible
1591
 * layout.
1592
 */
1593
function panels_ajax_flexible_edit_remove($handler, $id) {
1594
  $settings = &$handler->display->layout_settings;
1595
  panels_flexible_convert_settings($settings, $handler->plugins['layout']);
1596
1597
  if (empty($settings['items'][$id])) {
1598
    ajax_render_error(t('Invalid item id.'));
1599
  }
1600
1601
  $item = &$settings['items'][$id];
1602
  $css_id = isset($handler->display->css_id) ? $handler->display->css_id : '';
1603
  // Create a renderer object so we can render our new stuff.
1604
  $renderer = panels_flexible_create_renderer(TRUE, $css_id, array(), $settings, $handler->display, $handler->plugins['layout'], $handler);
1605
1606
1607
  $siblings = &$settings['items'][$item['parent']]['children'];
1608
  $parent_class = '.'  . $renderer->base[$settings['items'][$item['parent']]['type']] . '-' . $item['parent'];
1609
1610
  // Find the offset of our array. This will also be the key because
1611
  // this is a simple array.
1612
  $offset = array_search($id, $siblings);
1613
1614
  // Only bother with this stuff if our item is fluid, since fixed is
1615
  // as fixed does.
1616
  if ($item['type'] != 'row') {
1617
    if (isset($siblings[$offset + 1])) {
1618
      $next = $siblings[$offset + 1];
1619
    }
1620
    if (isset($siblings[$offset - 1])) {
1621
      $prev = $siblings[$offset - 1];
1622
    }
1623
1624
    if ($item['width_type'] == '%') {
1625
      // First, try next.
1626
      if (isset($next) && $settings['items'][$next]['width_type'] == '%') {
1627
        $settings['items'][$next]['width'] += $item['width'];
1628
      }
1629
      // If that failed, try the previous one.
1630
      else if (isset($prev) && $settings['items'][$prev]['width_type'] == '%') {
1631
        $settings['items'][$prev]['width'] += $item['width'];
1632
      }
1633
    }
1634
    // Not sure what happens if they both failed. Maybe nothing.
1635
  }
1636
1637
  // Remove the item.
1638
  array_splice($siblings, $offset, 1);
1639
1640
  unset($settings['items'][$id]);
1641
1642
  // Save our new state.
1643
  panels_edit_cache_set($handler->cache);
1644
  $class = $renderer->base[$item['type']] . '-' . $id;
1645
  $output = array();
1646
1647
  $output[] = ajax_command_remove('#panels-dnd-main .' . $class);
1648
1649
  // Regenerate the CSS for siblings.
1650
  if (!empty($siblings)) {
1651
    // Get all the CSS necessary for the entire row (as width adjustments may
1652
    // have cascaded).
1653
    $css = array();
1654
    panels_flexible_get_css_group($css, $renderer, $siblings, $parent_class, $item['type'], $item['parent']);
1655
    foreach ($css as $selector => $data) {
1656
      $output[] = ajax_command_css($selector, $data);
1657
    }
1658
  }
1659
1660
  // There are potentially two splitters linked to this item to be removed.
1661
  if (!empty($prev)) {
1662
    $output[] = ajax_command_remove('.flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $prev);
1663
  }
1664
1665
  // Try to remove the 'next' one even if there isn't a $next.
1666
  $output[] = ajax_command_remove('.flexible-splitter-for-' . $renderer->base[$item['type']] . '-' . $id);
1667
1668
  if (!empty($prev) && !empty($next)) {
1669
    // Add a new splitter that links $prev and $next:
1670
    $splitter = panels_flexible_render_splitter($renderer, $prev, $next);
1671
    $prev_class = '#panels-dnd-main .' . $renderer->base[$item['type']] . '-' . $prev;
1672
    $output[] = ajax_command_after($prev_class, $splitter);
1673
    // Send our fix height command.
1674
    $output[] = array('command' => 'flexible_fix_height');
1675
  }
1676
  // Rerender our parent item links:
1677
  $output[] = ajax_command_replace('.flexible-links-' . $item['parent'],
1678
    panels_flexible_render_item_links($renderer, $item['parent'], $settings['items'][$item['parent']]));
1679
1680
  $output[] = array(
1681
    'command' => 'flexible_fix_firstlast',
1682
    'selector' => $parent_class . '-inside',
1683
    'base' => 'panels-flexible-' . $item['type'],
1684
  );
1685
1686
  $handler->commands = $output;
1687
}
1688
1689
/**
1690
 * AJAX responder to store resize information when the user adjusts the
1691
 * splitter.
1692
 */
1693
function panels_ajax_flexible_edit_resize($handler) {
1694
  ctools_include('ajax');
1695
  $settings = &$handler->display->layout_settings;
1696
  panels_flexible_convert_settings($settings, $handler->plugins['layout']);
1697
1698
  $settings['items'][$_POST['left']]['width'] = $_POST['left_width'];
1699
  if (!empty($_POST['right']) && $_POST['right'] != $_POST['left']) {
1700
    $settings['items'][$_POST['right']]['width'] = $_POST['right_width'];
1701
  }
1702
1703
  // Save our new state.
1704
  panels_edit_cache_set($handler->cache);
1705
1706
  $handler->commands = array('ok');
1707
}
1708
1709
/**
1710
 * AJAX form to bring up the "reuse" modal.
1711
 */
1712
function panels_ajax_flexible_edit_reuse($handler) {
1713
  $settings = &$handler->display->layout_settings;
1714
  panels_flexible_convert_settings($settings, $handler->plugins['layout']);
1715
1716
  $form_state = array(
1717
    'display' => &$handler->display,
1718
    'settings' => &$settings,
1719
    'ajax' => TRUE,
1720
    'title' => t('Save this layout for reuse'),
1721
  );
1722
1723
  $output = ctools_modal_form_wrapper('panels_flexible_reuse_form', $form_state);
1724
  if (!empty($form_state['executed'])) {
1725
    // Create the new layout.
1726
    ctools_include('export');
1727
    $layout = ctools_export_crud_new('panels_layout');
1728
    $layout->plugin = 'flexible';
1729
    $layout->name = $form_state['values']['name'];
1730
    $layout->admin_title = $form_state['values']['admin_title'];
1731
    $layout->admin_description = $form_state['values']['admin_description'];
1732
    $layout->category = $form_state['values']['category'];
1733
    $layout->settings = $handler->display->layout_settings;
1734
1735
    // Save it.
1736
    ctools_export_crud_save('panels_layout', $layout);
1737
1738
    if (empty($form_state['values']['keep'])) {
1739
      // Set the actual layout_settings to now use the newly minted layout:
1740
      $handler->display->layout = 'flexible:' . $layout->name;
1741
      $handler->display->layout_settings = array();
1742
1743
      // Save our new state.
1744
      panels_edit_cache_set($handler->cache);
1745
    }
1746
1747
    // Dismiss the modal.
1748
    $output[] = ctools_modal_command_dismiss();
1749
  }
1750
1751
  $handler->commands = $output;
1752
}
1753
1754
function panels_flexible_reuse_form($form, &$form_state) {
1755
  $form['markup'] = array(
1756
    '#prefix' => '<div class="description">',
1757
    '#suffix' => '</div>',
1758
    '#value' => t('If you save this layout for reuse it will appear in the list of reusable layouts at admin/structure/panels/layouts, and you will need to go there to edit it. This layout will then become an option for all future panels you make.'),
1759
  );
1760
1761
  $form['admin_title'] = array(
1762
    '#type' => 'textfield',
1763
    '#title' => t('Administrative title'),
1764
    '#description' => t('This will appear in the administrative interface to easily identify it.'),
1765
  );
1766
1767
  $form['name'] = array(
1768
    '#type' => 'machine_name',
1769
    '#title' => t('Machine name'),
1770
    '#description' => t('The machine readable name of this layout. It must be unique, and it must contain only alphanumeric characters and underscores. Once created, you will not be able to change this value!'),
1771
    '#machine_name' => array(
1772
      'exists' => 'panels_flexible_edit_name_exists',
1773
      'source' => array('admin_title'),
1774
    ),
1775
  );
1776
1777
  $form['category'] = array(
1778
    '#type' => 'textfield',
1779
    '#title' => t('Category'),
1780
    '#description' => t('What category this layout should appear in. If left blank the category will be "Miscellaneous".'),
1781
  );
1782
1783
  $form['admin_description'] = array(
1784
    '#type' => 'textarea',
1785
    '#title' => t('Administrative description'),
1786
    '#description' => t('A description of what this layout is, does or is for, for administrative use.'),
1787
  );
1788
1789
  $form['keep'] = array(
1790
    '#type' => 'checkbox',
1791
    '#title' => t('Keep current panel layout flexible'),
1792
    '#description' => t('If checked, this panel will continue to use a generic flexible layout and will not use the saved layout. Use this option if you wish to clone this layout.'),
1793
  );
1794
1795
  $form['submit'] = array(
1796
    '#type' => 'submit',
1797
    '#value' => t('Save'),
1798
  );
1799
1800
  return $form;
1801
}
1802
1803
function panels_flexible_reuse_form_validate(&$form, &$form_state) {
1804
  if (empty($form_state['values']['name'])) {
1805
    form_error($form['name'], t('You must choose a machine name.'));
1806
  }
1807
1808
  ctools_include('export');
1809
  $test = ctools_export_crud_load('panels_layout', $form_state['values']['name']);
1810
  if ($test) {
1811
    form_error($form['name'], t('That name is used by another layout: @layout', array('@layout' => $test->admin_title)));
1812
  }
1813
1814
  // Ensure name fits the rules:
1815
  if (preg_match('/[^a-zA-Z0-9_]/', $form_state['values']['name'])) {
1816
    form_error($form['name'], t('Name must be alphanumeric or underscores only.'));
1817
  }
1818
}
1819
1820
/**
1821
 * Test for #machine_name type to see if an export exists.
1822
 */
1823
function panels_flexible_edit_name_exists($name, $element, &$form_state) {
1824
  ctools_include('export');
1825
  $plugin = $form_state['plugin'];
1826
1827
  return (empty($form_state['item']->export_ui_allow_overwrite) && ctools_export_crud_load($plugin['schema'], $name));
1828
}