[ Index ]

PHP Cross Reference of Drupal 6 (yi-drupal)

title

Body

[close]

/sites/all/modules/panels/plugins/layouts/flexible/ -> flexible.inc (source)

   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] : "&nbsp;";
 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  }


Generated: Mon Jul 9 18:01:44 2012 Cross-referenced by PHPXref 0.7