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