You are here

layout.module in Layout 7

Same filename and directory in other branches
  1. 8.2 layout.module

Responsive layout builder tool for Panels.

File

layout.module
View source
<?php

/**
 * @file
 * Responsive layout builder tool for Panels.
 */

/**
 * Implementation of hook_module_implements_alter().
 */
function layout_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'menu_alter') {

    // Move layout_menu_alter() to the end of the list. module_implements()
    // iterates through $implementations with a foreach loop which PHP iterates
    // in the order that the items were added, so to move an item to the end of
    // the array, we remove it and then add it.
    $group = $implementations['layout'];
    unset($implementations['layout']);
    $implementations['layout'] = $group;
  }
}

/**
 * Implementation of hook_menu_alter().
 */
function layout_menu_alter(&$items) {

  // Convert the automatically created ctools menu items to local tasks.
  if (isset($items['admin/structure/panels/layouts/breakpoints'])) {
    $items['admin/structure/panels/layouts/breakpoints']['type'] = MENU_LOCAL_TASK;
  }
  if (isset($items['admin/structure/panels/layouts/regions'])) {
    $items['admin/structure/panels/layouts/regions']['type'] = MENU_LOCAL_TASK;
  }

  // Hide this local action so its not visible anymore in Panels.
  if (isset($items['admin/structure/panels/layouts/add-flexible'])) {
    $items['admin/structure/panels/layouts/add-flexible']['type'] = MENU_CALLBACK;
    $items['admin/structure/panels/layouts/add-responsive']['title'] = 'Add layout';

    // Modify the title on the 'List' tab, although this makes it pretty
    // non-standard, by putting more things on the same level (breakpoints,
    // grids and regions by side of layouts), it becomes confusing as long
    // as the tab is labeled 'List'.
    $items['admin/structure/panels/layouts/list']['title'] = 'Layouts';
  }
}

/**
 * Implements hook_permission().
 */
function layout_permission() {
  return array(
    'administer layouts' => array(
      'title' => t('Administer responsive layouts'),
      'description' => t('Administer backend settings for responsive layouts.'),
    ),
  );
}

/**
 * Implements hook_ctools_plugin_directory().
 */
function layout_ctools_plugin_directory($owner, $plugin_type) {
  if ($owner == 'panels' && $plugin_type == 'layouts' || $owner == 'ctools' && $plugin_type == 'export_ui') {
    return "plugins/{$plugin_type}";
  }
}

/**
 * Implementation of hook_ctools_plugin_api().
 *
 * Tell CTools that we support the default_layout_breakpoint and
 * default_panels_layout APIs.
 */
function layout_ctools_plugin_api($owner, $api) {
  if ($owner == 'layout' && $api == 'default_layout_breakpoint' || $owner == 'layout' && $api == 'default_layout_region' || $owner == 'panels' && $api == 'layouts') {
    return array(
      'version' => 1,
    );
  }
}

// == Layouts =================================================================

/**
 * Implementation of hook_default_panels_layout().
 *
 * Provide a couple default layouts.
 */
function layout_default_panels_layout() {
  $export = array();
  $layout = new stdClass();
  $layout->disabled = FALSE;
  $layout->api_version = 1;
  $layout->name = 'default';
  $layout->admin_title = 'Default responsive layout';
  $layout->admin_description = '';
  $layout->category = '';
  $layout->plugin = 'responsive';
  $layout->settings = array(
    'regions' => array(
      'header_a' => 'header_a',
      'header_b' => 'header_b',
      'header_c' => 'header_c',
      'subheader_a' => 'subheader_a',
      'subheader_b' => 'subheader_b',
      'subheader_c' => 'subheader_c',
      'navigation' => 'navigation',
      'title' => 'title',
      'body' => 'body',
      'sidebar_a' => 'sidebar_a',
      'sidebar_b' => 'sidebar_b',
      'sidebar_c' => 'sidebar_c',
      'footer_a' => 'footer_a',
      'footer_b' => 'footer_b',
      'footer_c' => 'footer_c',
    ),
    'overrides' => array(),
  );
  $export['default'] = $layout;
  return $export;
}

// == Regions =================================================================

/**
 * Implementation of hook_default_layout_region().
 *
 * Provide a couple of default regions.
 */
function layout_default_layout_region() {
  $export = array();
  $base_regions = array(
    'header_a' => 'Header A',
    'header_b' => 'Header B',
    'header_c' => 'Header C',
    'subheader_a' => 'Subheader A',
    'subheader_b' => 'Subheader B',
    'subheader_c' => 'Subheader C',
    'navigation' => 'Navigation',
    'title' => 'Title',
    'body' => 'Body',
    'sidebar_a' => 'Sidebar A',
    'sidebar_b' => 'Sidebar B',
    'sidebar_c' => 'Sidebar C',
    'footer_a' => 'Footer A',
    'footer_b' => 'Footer B',
    'footer_c' => 'Footer C',
  );
  foreach ($base_regions as $name => $admin_title) {
    $region = new stdClass();
    $region->api_version = 1;
    $region->name = $name;
    $region->admin_title = $admin_title;
    $export[$name] = $region;
  }
  return $export;
}

/**
 * Add or update region in common set of regions.
 *
 * @param $region
 *   A fully populated region object.
 */
function layout_region_save($region) {
  ctools_include('export');
  $regions = ctools_export_crud_save('layout_region', $region);
  return $regions;
}

/**
 * Load all common layout regions.
 *
 * @return
 *   All common regions in an associative array keyed by machine name.
 */
function layout_region_load_all() {
  ctools_include('export');
  $regions = ctools_export_crud_load_all('layout_region');
  return $regions;
}

/**
 * Load one layout based on its machine name.
 */
function layout_region_load($name) {
  ctools_include('export');
  return ctools_export_crud_load('layout_region', $name);
}

/**
 * Get a list of layout names using the given region machine name.
 */
function layout_get_layouts_using_region($name) {
  $layouts = layout_get_responsive_layouts();
  $layouts_using_region = array();
  foreach ($layouts as $data) {
    if (isset($data['layout']->settings['regions']) && is_array($data['layout']->settings['regions']) && isset($data['layout']->settings['regions'][$name])) {
      $layouts_using_region[$data['layout']->name] = $data['title'];
    }
  }
  return $layouts_using_region;
}

// == Breakpoints =============================================================

/**
 * Implementation of hook_default_layout_breakpoint().
 *
 * Provide a couple of default breakpoints.
 */
function layout_default_layout_breakpoint() {
  $export = array();
  $breakpoint = new stdClass();
  $breakpoint->api_version = 1;
  $breakpoint->name = 'smartphone';
  $breakpoint->admin_title = 'Smartphone';
  $breakpoint->width = '0px';
  $breakpoint->grid_name = 'three_column_fluid';
  $export['smatphone'] = $breakpoint;
  $breakpoint = new stdClass();
  $breakpoint->api_version = 1;
  $breakpoint->name = 'tablet';
  $breakpoint->admin_title = 'Tablet';
  $breakpoint->width = '320px';
  $breakpoint->grid_name = 'six_column_fluid';
  $export['tablet'] = $breakpoint;
  $breakpoint = new stdClass();
  $breakpoint->api_version = 1;
  $breakpoint->name = 'standard';
  $breakpoint->admin_title = 'Standard';
  $breakpoint->width = '760px';
  $breakpoint->grid_name = 'twelve_column_fluid';
  $export['standard'] = $breakpoint;
  return $export;
}

/**
 * API function to get all responsive breakpoint on the site.
 */
function layout_breakpoint_load_all() {
  ctools_include('export');
  $breakpoints = ctools_export_crud_load_all('layout_breakpoint');
  uasort($breakpoints, 'layout_breakpoint_sort_by_width');
  return $breakpoints;
}

/**
 * Look up one breakpoint setup based on machine name.
 */
function layout_breakpoint_load($name) {
  ctools_include('export');
  return ctools_export_crud_load('layout_breakpoint', $name);
}

/**
 * Build CSS for the breakpoints with media queries.
 *
 * @param boolean $include_media_queries
 *   Whether generate one flat CSS without media queries (useful for
 *   administration), or wrap breakpoints with media queries (for frontend).
 *
 * @todo
 *   Figure out a good way to avoid equal max/min-weights in subsequent
 *   breakpoints if that is a problem.
 */
function layout_breakpoint_get_css($include_media_queries = TRUE) {
  $breakpoints = layout_breakpoint_load_all();
  $breakpoint_css = array();
  $min_width = 0;
  $breakpoint_count = count($breakpoints);
  $breakpoint_index = 0;
  foreach ($breakpoints as $name => $breakpoint) {
    if ($include_media_queries) {

      // Build the media query for this breakpoint. The first item should have a
      // min-width of 0, and the last item should have no max-width
      // (open-ended to infinity). Mid-items should both have a min-width and a
      // max-width. A breakpoint specifies the max-width for the previous breakpoint
      // (if any).
      $breakpoint_css[$breakpoint_index]['media-query'] = '@media screen and (min-width:' . $breakpoint->width . ')';
      if ($breakpoint_index > 0) {
        $breakpoint_css[$breakpoint_index - 1]['media-query'] .= ' and (max-width: ' . $breakpoint->width . ')';
      }

      // Get grid CSS from gridbuilder and apply some extra indentation.
      $breakpoint_css[$breakpoint_index]['css'] = '  ' . str_replace("\n", "\n  ", gridbuilder_get_css($breakpoint->grid_name, '.panel-responsive', '.rld-span-' . $name . '_'));
      $breakpoint_index++;
    }
    else {
      $breakpoint_css[$breakpoint_index]['css'] = gridbuilder_get_css($breakpoint->grid_name, NULL, NULL, TRUE);
    }
  }

  // Build CSS based on media query information.
  $built_css = '';
  foreach ($breakpoint_css as $data) {
    if (isset($data['media-query'])) {
      $built_css .= $data['media-query'] . " {\n" . $data['css'] . "\n}";
    }
    else {
      $built_css .= $data['css'] . "\n";
    }
  }
  return $built_css;
}

/**
 * Sort the breakpoints in ascending order by their width.
 */
function layout_breakpoint_sort_by_width($a, $b) {

  // Cast the width to int. Whether it provided as px or em, the cast should
  // result in a relevant number. It will not sort mixed em/px numbers properly
  // but that sounds like a broken setup. Not desigining for that.
  if ((int) $a->width == (int) $b->width) {
    return 0;
  }
  return (int) $a->width < (int) $b->width ? -1 : 1;
}

// == Layouts =================================================================

/**
 * Get all responsive layout info arrays from panels.
 */
function layout_get_responsive_layouts() {
  ctools_include('plugins', 'panels');
  $all_layouts = panels_get_layouts();
  $responsive_layouts = array();
  foreach ($all_layouts as $name => $data) {
    if (empty($data['builder']) && isset($data['layout']->plugin) && $data['layout']->plugin == 'responsive') {
      $responsive_layouts[$name] = $data;
    }
  }
  return $responsive_layouts;
}

Functions

Namesort descending Description
layout_breakpoint_get_css Build CSS for the breakpoints with media queries.
layout_breakpoint_load Look up one breakpoint setup based on machine name.
layout_breakpoint_load_all API function to get all responsive breakpoint on the site.
layout_breakpoint_sort_by_width Sort the breakpoints in ascending order by their width.
layout_ctools_plugin_api Implementation of hook_ctools_plugin_api().
layout_ctools_plugin_directory Implements hook_ctools_plugin_directory().
layout_default_layout_breakpoint Implementation of hook_default_layout_breakpoint().
layout_default_layout_region Implementation of hook_default_layout_region().
layout_default_panels_layout Implementation of hook_default_panels_layout().
layout_get_layouts_using_region Get a list of layout names using the given region machine name.
layout_get_responsive_layouts Get all responsive layout info arrays from panels.
layout_menu_alter Implementation of hook_menu_alter().
layout_module_implements_alter Implementation of hook_module_implements_alter().
layout_permission Implements hook_permission().
layout_region_load Load one layout based on its machine name.
layout_region_load_all Load all common layout regions.
layout_region_save Add or update region in common set of regions.