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