You are here

mobile_tools.module in Mobile Tools 7.2

Functionality to ease the creation of mixed device environments.

Create "device groups" and allow different settings for each including different themes, front page, views displays and more.

For more documentation and examples please go to http://drupal.org/node/459686 @todo update the documentation on that page for the new Drupal 7 version @todo update the documentation on that page to be complete for Drupal 6

File

mobile_tools.module
View source
<?php

/**
 * @file
 * Functionality to ease the creation of mixed device environments.
 *
 * Create "device groups" and allow different settings for each including
 * different themes, front page, views displays and more.
 *
 * For more documentation and examples please go to http://drupal.org/node/459686
 * @todo update the documentation on that page for the new Drupal 7 version
 * @todo update the documentation on that page to be complete for Drupal 6
 */
define('MOBILE_TOOLS_SWITCH_LINK_TEXT', variable_get('mobile_tools_switch_link_text', 'View @device site'));
define('MOBILE_TOOLS_SITE_TYPE_MOBILE', 'mobile');
define('MOBILE_TOOLS_SITE_TYPE_DESKTOP', 'desktop');

/**
 * Prefix added to all device group definitions
 */
define('MOBILE_TOOLS_DEVICE_GROUP_PREFIX', 'mobile_tools_');

// Core API hook implementations

/**
 * Implements hook_help().
 */
function mobile_tools_help($path, $arg) {
  switch ($path) {
    case 'admin/help#mobile_tools':
      return '<p>' . t('Visit the !documentation for more info', array(
        '!documentation' => l('documentation page', 'http://drupal.org/node/459686'),
      )) . '<p>';
  }
}

/**
 * Implements hook_permission().
 */
function mobile_tools_permission() {
  return array(
    'configure mobile tools' => array(
      'title' => t('Configure mobile tools'),
      'description' => t('Configure the Mobile Tools module'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function mobile_tools_menu() {
  $items['admin/config/system/mobile-tools'] = array(
    'title' => 'Mobile Tools',
    'description' => t('Configure how Drupal must behave for mobile devices.'),
    'access arguments' => array(
      'configure mobile tools',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'mobile_tools_configuration_form',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'mobile_tools.admin.inc',
  );
  $items['admin/config/system/mobile-tools/device-detection'] = array(
    'title' => 'Notification / redirection',
    'description' => 'Configure the detection of the mobile client and the appropriate actions (notifications or redirection)',
    'access arguments' => array(
      'configure mobile tools',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -4,
    'file' => 'mobile_tools.admin.inc',
  );
  $items['admin/config/system/mobile-tools/themes'] = array(
    'title' => 'Theme Switching',
    'page arguments' => array(
      'mobile_tools_themes_configuration_form',
    ),
    'page callback' => 'drupal_get_form',
    'access arguments' => array(
      'configure mobile tools',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 0,
    'file' => 'mobile_tools.admin.inc',
  );
  $items['admin/config/system/mobile-tools/ext'] = array(
    'title' => 'External modules',
    'page arguments' => array(
      'mobile_tools_external_modules_configuration_form',
    ),
    'page callback' => 'drupal_get_form',
    'access arguments' => array(
      'configure mobile tools',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 10,
    'file' => 'mobile_tools.admin.inc',
  );

  // @todo add a switch link menu item and document how it can be altered
  // to move into any other menu tree
  // @todo ensure all switch link functions are documented so module
  // authors can add multipl switch links if required.
  return $items;
}

/**
 * Implements hook_init().
 */
function mobile_tools_init() {

  // Redirect if required
  if (variable_get('mobile_tools_redirect', FALSE)) {

    // @todo fix, this doesn't work
    // need to use something other than hook_init or get spaces
    // to activate sooner
    mobile_tools_device_redirect();
  }
}

/**
 * Implements hook_block_info().
 */
function mobile_tools_block_info() {
  $blocks = array();
  $blocks['mobile_tools'] = array(
    'info' => t('Mobile Tools message block'),
    'cache' => DRUPAL_NO_CACHE,
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function mobile_tools_block_view($delta) {
  $block = array();
  if ($delta == "mobile_tools") {
    $block['content'] = mobile_tools_block_message();
  }
  return $block;
}

/**
 * Implementation of hook_alter()
 */
function mobile_tools_page_alter(&$page) {
  $site = mobile_tools_get_active_device_group();
  if (variable_get('mobile_tools_hide_address_bar', TRUE) && $site == MOBILE_TOOLS_SITE_TYPE_MOBILE) {

    // @todo find a way to make this script overridable for modules like jQuery Mobile
    // where the load event isn't sufficient
    drupal_add_js(drupal_get_path('module', 'mobile_tools') . '/assets/js/mobile_tools_hide_address_bar.js', array(
      'type' => 'file',
      'scope' => 'footer',
    ));
  }
}

/**
 * Implements hook_html_head_alter().
 */
function mobile_tools_html_head_alter(&$head_elements) {
  $site = mobile_tools_get_active_device_group();
  if (variable_get('mobile_tools_add_header', 1) && $site == 'mobile') {

    // Add the viewport settings
    $head_elements['viewport'] = array(
      '#type' => 'html_tag',
      '#tag' => 'meta',
      '#attributes' => array(
        'name' => 'viewport',
        'content' => 'user-scalable=no, width=device-width, maximum-scale=1.0',
      ),
    );

    // Add the iOS web app flag
    $head_elements['apple_mobile_web_app_capable'] = array(
      '#type' => 'html_tag',
      '#tag' => 'meta',
      '#attributes' => array(
        'name' => 'apple-mobile-web-app-capable',
        'content' => 'yes',
      ),
    );

    // Add the handheld flag
    $head_elements['HandheldFriendly'] = array(
      '#type' => 'html_tag',
      '#tag' => 'meta',
      '#attributes' => array(
        'name' => 'HandheldFriendly',
        'content' => 'true',
      ),
    );
  }
}

/**
 * Implementation of hook_entity_info_alter()
 *
 * We provide new build modes for all entities
 */
function mobile_tools_entity_info_alter(&$entity_info) {
  $enable_build_mode = variable_get('mobile_tools_enable_build_mode', FALSE);
  foreach ($entity_info as $entity_type => $entity) {
    $entity_info[$entity_type]['view modes']['mobile'] = array(
      'label' => 'Mobile',
      'custom settings' => FALSE,
    );
    $device_groups = array(
      'mobile' => 'Mobile',
    );
    foreach ($device_groups as $device => $device_name) {
      $entity_info[$entity_type]['view modes'][$device] = array(
        'label' => $device_name,
        'custom settings' => FALSE,
      );
    }
  }
}

/**
 * Alteration to global setting form
 */
function mobile_tools_form_alter(&$form, $form_state, $form_id) {
  switch ($form_id) {
    case 'node_configure':
      $form['default_nodes_main_mobile'] = array(
        '#type' => 'select',
        '#title' => t('Number of posts on main page for the mobile version'),
        '#default_value' => variable_get('default_nodes_main_mobile', variable_get('default_nodes_main')),
        '#options' => array(
          1 => 1,
          2 => 2,
          3 => 3,
          4 => 4,
          5 => 5,
          6 => 6,
          7 => 7,
          8 => 8,
          9 => 9,
          10 => 10,
          15 => 15,
          20 => 20,
          25 => 25,
          30 => 30,
        ),
        '#description' => t('The default maximum number of posts to display per page on overview pages such as the main page (on Mobile).'),
      );
      break;
    case 'system_site_information_settings':
      $form['site_frontpage_mobile'] = array(
        '#type' => 'textfield',
        '#title' => t('Choose another frontpage for mobile visitors.'),
        '#default_value' => variable_get('site_frontpage_mobile', variable_get('site_frontpage', 'node')),
        '#description' => t('If you want a different page as the frontpage of your site for mobile users, specify it here.'),
        '#weight' => 1,
      );
      break;
  }
}

/**
 * Implements hook_flush_caches().
 */
function mobile_tools_flush_caches() {

  //return array('cache_mobile_tools_device_group');
}

/**
 * Implements hook_modules_enabled().
 */
function mobile_tools_modules_enabled($modules) {

  // Rebuild the list of device groups
  mobile_tools_device_groups_rebuild();
}

/**
 * Implements hook_modules_disabled().
 */
function mobile_tools_modules_disabled($modules) {

  // Rebuild the list of device groups
  mobile_tools_device_groups_rebuild();
}

// Contrib API hook implementations

/**
 * Implements hook_ctools_plugin_api().
 */
function mobile_tools_ctools_plugin_api($module, $api) {
  if ($module = 'spaces' && ($api = 'plugins')) {
    return array(
      'version' => 3,
    );
  }
  elseif ($module == 'spaces' && $api == 'spaces') {
    return array(
      'version' => 3,
    );
  }
}

/**
 * Implements hook_purl_provider().
 */
function mobile_tools_purl_provider() {
  $items = array();
  $device_groups = mobile_tools_device_groups();

  // Create a purl provider entry for each device group
  // @todo see if we actually need an entry for each. Might be sufficient
  // to have one entry for each in the modifiers instead
  foreach ($device_groups as $id => $name) {
    $items['spaces_mobile_tools_' . $id] = array(
      'name' => t('Device Group: @group', array(
        '@group' => t($name),
      )),
      'description' => t('Device group space for @group', array(
        '@group' => t($name),
      )),
      'callback' => 'spaces_init_space',
      'callback arguments' => array(
        MOBILE_TOOLS_DEVICE_GROUP_PREFIX . $id,
      ),
      'example' => $id,
    );
  }

  // Default test groups
  $items['spaces_mobile_tools'] = array(
    'name' => t('Device Group: @group', array(
      '@group' => t('Mobile'),
    )),
    'description' => t('Trigger the mobile site using the given criteria.'),
    'callback' => 'spaces_init_space',
    'callback arguments' => array(
      'mobile_tools',
    ),
    'example' => 'mobile-tools',
  );
  return $items;
}

/**
 * Implements hook_purl_modifiers().
 */
function mobile_tools_purl_modifiers() {

  // Load the device groups
  $device_groups = mobile_tools_device_groups();

  // Create the modifiers container
  $modifiers = array();

  // Each device group gets a modifier associated to it
  foreach ($device_groups as $id => $name) {
    $modifiers['spaces_mobile_tools_' . $id] = array(
      // @todo find a way to allow a value other than the id
      array(
        'value' => $id,
        'id' => $id,
      ),
    );
  }

  // Test modifier
  $modifiers['spaces_mobile_tools'] = array(
    array(
      'value' => 'mobile',
      'id' => 'mobile',
    ),
  );
  return $modifiers;
}

/**
 * Implements hook_spaces_plugins().
 */
function mobile_tools_spaces_plugins() {
  $plugins = array();

  // Register the space plugin
  $plugins['space_mobile_tools'] = array(
    'handler' => array(
      'path' => drupal_get_path('module', 'mobile_tools') . '/plugins',
      'file' => 'mobile_tools_spaces.inc',
      'class' => 'space_mobile_tools',
      'parent' => 'space_type_purl',
    ),
  );
  return $plugins;
}

/**
 * Implements hook_spaces_registry().
 */
function mobile_tools_spaces_registry() {
  $type = array();
  $device_groups = mobile_tools_device_groups();

  // Register a space for each device group
  // @todo change to register only one device space type
  // instead use implementations of each space for settings
  foreach ($device_groups as $id => $name) {
    $types['mobile_tools_' . $id] = array(
      'title' => t('Device Group Space: @group', array(
        '@group' => t($name),
      )),
      'plugin' => 'space_mobile_tools',
    );
  }
  return array(
    'types' => $types,
  );
}

/**
 * Implements hook_mobile_tools_device_groups().
 */
function mobile_tools_mobile_tools_device_groups() {
  return array(
    'iphone' => 'iPhone',
    'ipod' => 'iPod',
    'ipad' => 'iPad',
    'android' => 'Android',
    'operamini' => 'Opera Mini',
    'blackberry' => 'BlackBerry',
  );
}

/**
 *  Implements hook_mobile_tools_detect_device().
 */
function mobile_tools_mobile_tools_detect_device() {
  $mobile_browser = array(
    'type' => 'desktop',
    'group' => '',
  );

  // set mobile browser as FALSE till we can prove otherwise
  if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) {
    $user_agent = $_SERVER['HTTP_USER_AGENT'];
  }
  else {
    return FALSE;
  }

  // get the content accept value
  // FIXME: this should be cleaned to ensure no nefarious input gets executed
  if (array_key_exists('HTTP_ACCEPT', $_SERVER)) {
    $accept = $_SERVER['HTTP_ACCEPT'];
  }
  else {
    $accept = '';
  }
  switch (TRUE) {
    case FALSE !== stripos($user_agent, 'ipad'):
      $mobile_browser = array(
        'type' => 'desktop',
        'group' => 'ipad',
      );
      break;
    case FALSE !== stripos($user_agent, 'ipod'):

      // we find the words iphone or ipod in the user agent
      $mobile_browser = array(
        'type' => 'mobile',
        'group' => 'ipod',
      );

      // mobile browser is either TRUE or FALSE depending on the setting of iphone when calling the function
      break;

    // break out and skip the rest if we've had a match on the iphone or ipod
    case FALSE !== stripos($user_agent, 'iphone'):

      // we find the words iphone or ipod in the user agent
      $mobile_browser = array(
        'type' => 'mobile',
        'group' => 'iphone',
      );
      break;
    case FALSE !== stripos($user_agent, 'android'):

      // we find android in the user agent
      $mobile_browser = array(
        'type' => 'mobile',
        'group' => 'android',
      );

      // mobile browser is either TRUE or FALSE depending on the setting of android when calling the function
      break;

    // break out and skip the rest if we've had a match on android
    case FALSE !== stripos($user_agent, 'opera mini'):

      // we find opera mini in the user agent
      $mobile_browser = array(
        'type' => 'mobile',
        'group' => 'opera_mini',
      );

      // mobile browser is either TRUE or FALSE depending on the setting of opera when calling the function
      break;

    // break out and skip the rest if we've had a match on opera
    case FALSE !== stripos($user_agent, 'blackberry'):

      // we find blackberry in the user agent
      $mobile_browser = array(
        'type' => 'mobile',
        'group' => 'blackberry',
      );

      // mobile browser is either TRUE or FALSE depending on the setting of blackberry when calling the function
      break;

    // break out and skip the rest if we've had a match on blackberry
    case preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|vodafone|o2|pocket|kindle|mobile|pda|psp|treo)/i', $user_agent):

      // check if any of the values listed create a match on the user agent - these are some of the most common terms used in agents to identify them as being mobile devices - the i at the end makes it case insensitive
      $mobile_browser = array(
        'type' => 'mobile',
        'group' => '',
      );

      // set mobile browser to TRUE
      break;

    // break out and skip the rest if we've preg_match on the user agent returned TRUE
    case strpos($accept, 'text/vnd.wap.wml') > 0 || strpos($accept, 'application/vnd.wap.xhtml+xml') > 0:

      // is the device showing signs of support for text/vnd.wap.wml or application/vnd.wap.xhtml+xml
      $mobile_browser = array(
        'type' => 'mobile',
        'group' => '',
      );

      // set mobile browser to TRUE
      break;

    // break out and skip the rest if we've had a match on the content accept headers
    case isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE']):

      // is the device giving us a HTTP_X_WAP_PROFILE or HTTP_PROFILE header - only mobile devices would do this
      $mobile_browser = array(
        'type' => 'mobile',
        'group' => '',
      );

      // set mobile browser to TRUE
      break;

    // break out and skip the final step if we've had a return TRUE on the mobile specfic headers
    case in_array(strtolower(substr($user_agent, 0, 4)), array(
      '1207' => '1207',
      '3gso' => '3gso',
      '4thp' => '4thp',
      '501i' => '501i',
      '502i' => '502i',
      '503i' => '503i',
      '504i' => '504i',
      '505i' => '505i',
      '506i' => '506i',
      '6310' => '6310',
      '6590' => '6590',
      '770s' => '770s',
      '802s' => '802s',
      'a wa' => 'a wa',
      'acer' => 'acer',
      'acs-' => 'acs-',
      'airn' => 'airn',
      'alav' => 'alav',
      'asus' => 'asus',
      'attw' => 'attw',
      'au-m' => 'au-m',
      'aur ' => 'aur ',
      'aus ' => 'aus ',
      'abac' => 'abac',
      'acoo' => 'acoo',
      'aiko' => 'aiko',
      'alco' => 'alco',
      'alca' => 'alca',
      'amoi' => 'amoi',
      'anex' => 'anex',
      'anny' => 'anny',
      'anyw' => 'anyw',
      'aptu' => 'aptu',
      'arch' => 'arch',
      'argo' => 'argo',
      'bell' => 'bell',
      'bird' => 'bird',
      'bw-n' => 'bw-n',
      'bw-u' => 'bw-u',
      'beck' => 'beck',
      'benq' => 'benq',
      'bilb' => 'bilb',
      'blac' => 'blac',
      'c55/' => 'c55/',
      'cdm-' => 'cdm-',
      'chtm' => 'chtm',
      'capi' => 'capi',
      'comp' => 'comp',
      'cond' => 'cond',
      'craw' => 'craw',
      'dall' => 'dall',
      'dbte' => 'dbte',
      'dc-s' => 'dc-s',
      'dica' => 'dica',
      'ds-d' => 'ds-d',
      'ds12' => 'ds12',
      'dait' => 'dait',
      'devi' => 'devi',
      'dmob' => 'dmob',
      'doco' => 'doco',
      'dopo' => 'dopo',
      'el49' => 'el49',
      'erk0' => 'erk0',
      'esl8' => 'esl8',
      'ez40' => 'ez40',
      'ez60' => 'ez60',
      'ez70' => 'ez70',
      'ezos' => 'ezos',
      'ezze' => 'ezze',
      'elai' => 'elai',
      'emul' => 'emul',
      'eric' => 'eric',
      'ezwa' => 'ezwa',
      'fake' => 'fake',
      'fly-' => 'fly-',
      'fly_' => 'fly_',
      'g-mo' => 'g-mo',
      'g1 u' => 'g1 u',
      'g560' => 'g560',
      'gf-5' => 'gf-5',
      'grun' => 'grun',
      'gene' => 'gene',
      'go.w' => 'go.w',
      'good' => 'good',
      'grad' => 'grad',
      'hcit' => 'hcit',
      'hd-m' => 'hd-m',
      'hd-p' => 'hd-p',
      'hd-t' => 'hd-t',
      'hei-' => 'hei-',
      'hp i' => 'hp i',
      'hpip' => 'hpip',
      'hs-c' => 'hs-c',
      'htc ' => 'htc ',
      'htc-' => 'htc-',
      'htca' => 'htca',
      'htcg' => 'htcg',
      'htcp' => 'htcp',
      'htcs' => 'htcs',
      'htct' => 'htct',
      'htc_' => 'htc_',
      'haie' => 'haie',
      'hita' => 'hita',
      'huaw' => 'huaw',
      'hutc' => 'hutc',
      'i-20' => 'i-20',
      'i-go' => 'i-go',
      'i-ma' => 'i-ma',
      'i230' => 'i230',
      'iac' => 'iac',
      'iac-' => 'iac-',
      'iac/' => 'iac/',
      'ig01' => 'ig01',
      'im1k' => 'im1k',
      'inno' => 'inno',
      'iris' => 'iris',
      'jata' => 'jata',
      'java' => 'java',
      'kddi' => 'kddi',
      'kgt' => 'kgt',
      'kgt/' => 'kgt/',
      'kpt ' => 'kpt ',
      'kwc-' => 'kwc-',
      'klon' => 'klon',
      'lexi' => 'lexi',
      'lg g' => 'lg g',
      'lg-a' => 'lg-a',
      'lg-b' => 'lg-b',
      'lg-c' => 'lg-c',
      'lg-d' => 'lg-d',
      'lg-f' => 'lg-f',
      'lg-g' => 'lg-g',
      'lg-k' => 'lg-k',
      'lg-l' => 'lg-l',
      'lg-m' => 'lg-m',
      'lg-o' => 'lg-o',
      'lg-p' => 'lg-p',
      'lg-s' => 'lg-s',
      'lg-t' => 'lg-t',
      'lg-u' => 'lg-u',
      'lg-w' => 'lg-w',
      'lg/k' => 'lg/k',
      'lg/l' => 'lg/l',
      'lg/u' => 'lg/u',
      'lg50' => 'lg50',
      'lg54' => 'lg54',
      'lge-' => 'lge-',
      'lge/' => 'lge/',
      'lynx' => 'lynx',
      'leno' => 'leno',
      'm1-w' => 'm1-w',
      'm3ga' => 'm3ga',
      'm50/' => 'm50/',
      'maui' => 'maui',
      'mc01' => 'mc01',
      'mc21' => 'mc21',
      'mcca' => 'mcca',
      'medi' => 'medi',
      'meri' => 'meri',
      'mio8' => 'mio8',
      'mioa' => 'mioa',
      'mo01' => 'mo01',
      'mo02' => 'mo02',
      'mode' => 'mode',
      'modo' => 'modo',
      'mot ' => 'mot ',
      'mot-' => 'mot-',
      'mt50' => 'mt50',
      'mtp1' => 'mtp1',
      'mtv ' => 'mtv ',
      'mate' => 'mate',
      'maxo' => 'maxo',
      'merc' => 'merc',
      'mits' => 'mits',
      'mobi' => 'mobi',
      'motv' => 'motv',
      'mozz' => 'mozz',
      'n100' => 'n100',
      'n101' => 'n101',
      'n102' => 'n102',
      'n202' => 'n202',
      'n203' => 'n203',
      'n300' => 'n300',
      'n302' => 'n302',
      'n500' => 'n500',
      'n502' => 'n502',
      'n505' => 'n505',
      'n700' => 'n700',
      'n701' => 'n701',
      'n710' => 'n710',
      'nec-' => 'nec-',
      'nem-' => 'nem-',
      'newg' => 'newg',
      'neon' => 'neon',
      'netf' => 'netf',
      'noki' => 'noki',
      'nzph' => 'nzph',
      'o2 x' => 'o2 x',
      'o2-x' => 'o2-x',
      'opwv' => 'opwv',
      'owg1' => 'owg1',
      'opti' => 'opti',
      'oran' => 'oran',
      'p800' => 'p800',
      'pand' => 'pand',
      'pg-1' => 'pg-1',
      'pg-2' => 'pg-2',
      'pg-3' => 'pg-3',
      'pg-6' => 'pg-6',
      'pg-8' => 'pg-8',
      'pg-c' => 'pg-c',
      'pg13' => 'pg13',
      'phil' => 'phil',
      'pn-2' => 'pn-2',
      'ppc;' => 'ppc;',
      'pt-g' => 'pt-g',
      'palm' => 'palm',
      'pana' => 'pana',
      'pire' => 'pire',
      'pock' => 'pock',
      'pose' => 'pose',
      'psio' => 'psio',
      'qa-a' => 'qa-a',
      'qc-2' => 'qc-2',
      'qc-3' => 'qc-3',
      'qc-5' => 'qc-5',
      'qc-7' => 'qc-7',
      'qc07' => 'qc07',
      'qc12' => 'qc12',
      'qc21' => 'qc21',
      'qc32' => 'qc32',
      'qc60' => 'qc60',
      'qci-' => 'qci-',
      'qwap' => 'qwap',
      'qtek' => 'qtek',
      'r380' => 'r380',
      'r600' => 'r600',
      'raks' => 'raks',
      'rim9' => 'rim9',
      'rove' => 'rove',
      's55/' => 's55/',
      'sage' => 'sage',
      'sams' => 'sams',
      'sc01' => 'sc01',
      'sch-' => 'sch-',
      'scp-' => 'scp-',
      'sdk/' => 'sdk/',
      'se47' => 'se47',
      'sec-' => 'sec-',
      'sec0' => 'sec0',
      'sec1' => 'sec1',
      'semc' => 'semc',
      'sgh-' => 'sgh-',
      'shar' => 'shar',
      'sie-' => 'sie-',
      'sk-0' => 'sk-0',
      'sl45' => 'sl45',
      'slid' => 'slid',
      'smb3' => 'smb3',
      'smt5' => 'smt5',
      'sp01' => 'sp01',
      'sph-' => 'sph-',
      'spv ' => 'spv ',
      'spv-' => 'spv-',
      'sy01' => 'sy01',
      'samm' => 'samm',
      'sany' => 'sany',
      'sava' => 'sava',
      'scoo' => 'scoo',
      'send' => 'send',
      'siem' => 'siem',
      'smar' => 'smar',
      'smit' => 'smit',
      'soft' => 'soft',
      'sony' => 'sony',
      't-mo' => 't-mo',
      't218' => 't218',
      't250' => 't250',
      't600' => 't600',
      't610' => 't610',
      't618' => 't618',
      'tcl-' => 'tcl-',
      'tdg-' => 'tdg-',
      'telm' => 'telm',
      'tim-' => 'tim-',
      'ts70' => 'ts70',
      'tsm-' => 'tsm-',
      'tsm3' => 'tsm3',
      'tsm5' => 'tsm5',
      'tx-9' => 'tx-9',
      'tagt' => 'tagt',
      'talk' => 'talk',
      'teli' => 'teli',
      'topl' => 'topl',
      'tosh' => 'tosh',
      'up.b' => 'up.b',
      'upg1' => 'upg1',
      'utst' => 'utst',
      'v400' => 'v400',
      'v750' => 'v750',
      'veri' => 'veri',
      'vk-v' => 'vk-v',
      'vk40' => 'vk40',
      'vk50' => 'vk50',
      'vk52' => 'vk52',
      'vk53' => 'vk53',
      'vm40' => 'vm40',
      'vx98' => 'vx98',
      'virg' => 'virg',
      'vite' => 'vite',
      'voda' => 'voda',
      'vulc' => 'vulc',
      'wapj' => 'wapj',
      'wapp' => 'wapp',
      'wapu' => 'wapu',
      'wapm' => 'wapm',
      'wig ' => 'wig ',
      'wapi' => 'wapi',
      'wapr' => 'wapr',
      'wapv' => 'wapv',
      'wapy' => 'wapy',
      'wapa' => 'wapa',
      'waps' => 'waps',
      'wapt' => 'wapt',
      'winc' => 'winc',
      'winw' => 'winw',
      'wonu' => 'wonu',
      'x700' => 'x700',
      'xda2' => 'xda2',
      'xdag' => 'xdag',
      'yas-' => 'yas-',
      'your' => 'your',
      'zte-' => 'zte-',
      'zeto' => 'zeto',
      'acs-' => 'acs-',
      'alav' => 'alav',
      'alca' => 'alca',
      'amoi' => 'amoi',
      'aste' => 'aste',
      'audi' => 'audi',
      'avan' => 'avan',
      'benq' => 'benq',
      'bird' => 'bird',
      'blac' => 'blac',
      'blaz' => 'blaz',
      'brew' => 'brew',
      'brvw' => 'brvw',
      'bumb' => 'bumb',
      'ccwa' => 'ccwa',
      'cell' => 'cell',
      'cldc' => 'cldc',
      'cmd-' => 'cmd-',
      'dang' => 'dang',
      'doco' => 'doco',
      'eml2' => 'eml2',
      'eric' => 'eric',
      'fetc' => 'fetc',
      'hipt' => 'hipt',
      'http' => 'http',
      'ibro' => 'ibro',
      'idea' => 'idea',
      'ikom' => 'ikom',
      'inno' => 'inno',
      'ipaq' => 'ipaq',
      'jbro' => 'jbro',
      'jemu' => 'jemu',
      'java' => 'java',
      'jigs' => 'jigs',
      'kddi' => 'kddi',
      'keji' => 'keji',
      'kyoc' => 'kyoc',
      'kyok' => 'kyok',
      'leno' => 'leno',
      'lg-c' => 'lg-c',
      'lg-d' => 'lg-d',
      'lg-g' => 'lg-g',
      'lge-' => 'lge-',
      'libw' => 'libw',
      'm-cr' => 'm-cr',
      'maui' => 'maui',
      'maxo' => 'maxo',
      'midp' => 'midp',
      'mits' => 'mits',
      'mmef' => 'mmef',
      'mobi' => 'mobi',
      'mot-' => 'mot-',
      'moto' => 'moto',
      'mwbp' => 'mwbp',
      'mywa' => 'mywa',
      'nec-' => 'nec-',
      'newt' => 'newt',
      'nok6' => 'nok6',
      'noki' => 'noki',
      'o2im' => 'o2im',
      'opwv' => 'opwv',
      'palm' => 'palm',
      'pana' => 'pana',
      'pant' => 'pant',
      'pdxg' => 'pdxg',
      'phil' => 'phil',
      'play' => 'play',
      'pluc' => 'pluc',
      'port' => 'port',
      'prox' => 'prox',
      'qtek' => 'qtek',
      'qwap' => 'qwap',
      'rozo' => 'rozo',
      'sage' => 'sage',
      'sama' => 'sama',
      'sams' => 'sams',
      'sany' => 'sany',
      'sch-' => 'sch-',
      'sec-' => 'sec-',
      'send' => 'send',
      'seri' => 'seri',
      'sgh-' => 'sgh-',
      'shar' => 'shar',
      'sie-' => 'sie-',
      'siem' => 'siem',
      'smal' => 'smal',
      'smar' => 'smar',
      'sony' => 'sony',
      'sph-' => 'sph-',
      'symb' => 'symb',
      't-mo' => 't-mo',
      'teli' => 'teli',
      'tim-' => 'tim-',
      'tosh' => 'tosh',
      'treo' => 'treo',
      'tsm-' => 'tsm-',
      'upg1' => 'upg1',
      'upsi' => 'upsi',
      'vk-v' => 'vk-v',
      'voda' => 'voda',
      'vx52' => 'vx52',
      'vx53' => 'vx53',
      'vx60' => 'vx60',
      'vx61' => 'vx61',
      'vx70' => 'vx70',
      'vx80' => 'vx80',
      'vx81' => 'vx81',
      'vx83' => 'vx83',
      'vx85' => 'vx85',
      'wap-' => 'wap-',
      'wapa' => 'wapa',
      'wapi' => 'wapi',
      'wapp' => 'wapp',
      'wapr' => 'wapr',
      'webc' => 'webc',
      'whit' => 'whit',
      'winw' => 'winw',
      'wmlb' => 'wmlb',
      'xda-' => 'xda-',
    )):

      // check against a list of trimmed user agents to see if we find a match
      if (!ereg("w3c_css_validator", strtolower($user_agent))) {
        $mobile_browser = array(
          'type' => 'mobile',
          'group' => '',
        );

        // set mobile browser to TRUE
      }
      break;
  }

  // ends the switch
  $result = $mobile_browser;
  return $result;
}

// Public functions

/**
 * Helper function returning the configurable message for the notification
 *
 * @return string
 *  Returns an item list of links or a notification message.
 */
function mobile_tools_block_message() {

  // If automatic redirection is enabled, disable the switch links
  if (variable_get('mobile_tools_redirect', FALSE)) {
    return t('Automatic redirection is active. Manual switching is disabled.');
  }

  // Get the active device group
  $active_groups = mobile_tools_get_active_device_group(TRUE);

  // Get the full list of device groups
  $device_groups = mobile_tools_device_groups();
  foreach ($active_groups as $key => $group) {

    // Eliminate the active group from the list of links
    unset($device_groups[$group['id']]);
  }

  // Prepare the formatted array for theme_item_list().
  $items = array();

  // Get the current path
  $path = purl_get_normal_path(themekey_get_q());
  foreach ($device_groups as $id => $name) {

    // Get the link text
    $text = mobile_tools_switch_link_text($id);

    // Load the options required for PURL to rewrite the generated link.
    $options = mobile_tools_switch_options($id);

    // Generate the link
    $items[] = l(t($text, array(
      '@device' => $name,
    )), $path, $options);
  }
  return theme('item_list', array(
    'items' => $items,
  ));
}

/**
 * Get the text to display on the switch links
 *
 * Pass the device group you want to link to.
 * Ex: Go from desktop -> mobile
 *     $device_group = MOBILE_TOOLS_SITE_TYPE_MOBILE;
 * @param string $device_group
 *  Device group
 * @return string
 *  Returns the link text
 */
function mobile_tools_switch_link_text($device_group) {
  return variable_get('mobile_tools_switch_link_text', MOBILE_TOOLS_SWITCH_LINK_TEXT);
}

/**
 * Generate the options required for PURL to rewrite the URL
 *
 * @param string $device_group
 *  The device group you want to switch to.
 * @return string
 *  Returns options array to pass into l() or url().
 */
function mobile_tools_switch_options($device_group, $options = array()) {

  // Set the options required for each device group
  $device_groups = mobile_tools_device_groups();
  if (isset($device_groups[$device_group])) {

    // Set the options required to get the link for the mobile site
    $options['purl']['provider'] = 'spaces_mobile_tools_' . $device_group;
    $options['purl']['id'] = $device_group;
    return $options;
  }

  // Disable purl if unknown group
  $options['purl']['disabled'] = TRUE;
  return $options;
}

/**
 * Redirect the user based on the device/site type combination
 */
function mobile_tools_device_redirect() {

  // @todo add redirect session detection (i.e. see if the user has already been redirected)
  // @todo add detection to get the current device
  // Get the device group
  $active_groups = mobile_tools_get_active_device_group();

  // Since we can only redirect to one site, we take the first returned device
  // group
  if (!empty($active_groups)) {
    $device_group = $active_groups[0];
  }
  else {

    // No active device group, so no redirect can be triggered
    return FALSE;
  }

  // code takes into account path exceptions in the configuration.
  $pages = variable_get('mobile_tools_redirect_exceptions', '');
  $page_match = FALSE;

  // Get the current 'q' path parameter
  // @todo replace with purl normal path function call
  $q = themekey_get_q();
  if ($q && $pages != '') {

    // Check for the excluded paths
    $path = drupal_get_path_alias($q);

    // Compare with the internal and path alias (if any).
    $page_match = drupal_match_path($path, $pages);
    if ($path != $q) {
      $page_match = $page_match || drupal_match_path($q, $pages);
    }
  }

  // Check if exceptions are pages on which to redirect, or not to redirect
  if (variable_get('mobile_tools_redirect_exceptions_type', FALSE) == 'only-redirect') {
    $page_match = !$page_match;
  }

  // Perform the redirect
  if (!$page_match) {

    // Load the switch options
    $options = mobile_tools_switch_options($device_group);

    // Send the user to that new URL.
    purl_goto($q, $options);
  }
}

/**
 * Get $device object.
 * Check if the 'device' argument is present or a cookie is set to overwrite the device:
 * - device=mobile =>  mobile view
 * - device=desktop => desktop view
 * - device=[group] => specific group view
 * - device=auto => reset overwrite
 * @return $device
 *  The $device object
 */
function mobile_tools_get_device() {

  // @todo replace with session api sid to track override
  global $cookie_domain, $mobile_tools_device;

  // currently the boot method saves the result in a global variable.
  if (isset($mobile_tools_device)) {
    return $mobile_tools_device;
  }

  // Checking the possible arguments
  $session_time = REQUEST_TIME + variable_get('mobile_tools_cookie_session', 3600 * 24 * 30);

  //first check if the device type is forced in the device argument
  if (isset($_GET['device'])) {
    switch ($_GET['device']) {
      case 'desktop':
      case 'mobile':
        setCookie('mt_device', $_GET['device'], variable_get('mobile_tools_cookie_lifetime', $session_time), '/', $cookie_domain);
        return array(
          'type' => $_GET['device'],
          'group' => '',
        );
        break;
      case 'auto':
        setCookie('mt_device', '', REQUEST_TIME - 3600 * 24 * 30, '/', $cookie_domain);
        break;
      default:
        $device_groups = mobile_tools_device_groups();
        if (isset($device_groups[$_GET['device']])) {
          setCookie('mt_device', $_GET['device'], variable_get('mobile_tools_cookie_lifetime', $session_time), '/', $cookie_domain);
          return array(
            'type' => 'mobile',
            'group' => $_GET['device'],
          );
        }
    }
  }
  elseif (isset($_COOKIE['mt_device'])) {
    switch ($_COOKIE['mt_device']) {
      case 'desktop':
      case 'mobile':
        return array(
          'type' => $_COOKIE['mt_device'],
          'group' => '',
        );
        break;
      case 'auto':
        setCookie('mt_device', '', REQUEST_TIME - 3600);
        break;
      default:
        $device_groups = mobile_tools_device_groups();
        if (isset($device_groups[$_COOKIE['mt_device']])) {
          setCookie('mt_device', $_COOKIE['mt_device'], variable_get('mobile_tools_cookie_lifetime', $session_time), '/');
          return array(
            'type' => 'mobile',
            'group' => $_COOKIE['mt_device'],
          );
        }
    }
  }

  // we default to the real detection
  return mobile_tools_detect_device();
}

/**
 * Detect the device
 * @return $device
 *  The $device object. Other modules can implement the hook_is_mobile_device()
 * @todo rename hook to match proper naming conventions
 */
function mobile_tools_detect_device() {
  $module = variable_get('mobile_tools_device_detection', 'mobile_tools');

  // Ensure the desired module is loaded
  drupal_load('module', $module);
  return module_invoke($module, 'mobile_tools_detect_device');
}

/**
 * Get the active device group
 *
 * @return array
 *  Returns the active device groups, FALSE otherwise.
 */
function mobile_tools_get_active_device_group() {

  // There are a few triggers which can point to the active device group
  // @todo add static cache
  // Next check for an active Space
  // @todo configure to handle multiple active spaces
  $space = spaces_get_space();
  if (isset($spaces->type)) {
    return array(
      'id' => $spaces->id,
      'type' => $spaces->type,
    );
  }
  else {

    // In case the device group is requested before the Space is initialized
    // we detect based on the active PURL modifiers
    $device_groups = array();
    $active = purl_active();
    $active = _mobile_tools_get_active_purl_modifiers($active
      ->get());
    if (!empty($active)) {
      foreach ($active as $key => $object) {
        $device_groups[] = array(
          'id' => $object->id,
          'type' => MOBILE_TOOLS_DEVICE_GROUP_PREFIX . $object->id,
        );
      }
      return $device_groups;
    }
  }
  return FALSE;
}

/**
 * Rebuild the list of implemented device groups
 */
function mobile_tools_device_groups_rebuild() {

  // Invoke hook_mobile_tools_device_group()
  $device_groups = module_invoke_all('mobile_tools_device_groups');

  // Validate the list of device groups
  _mobile_tools_validate_device_groups($device_groups);

  // Save the list
  variable_set('mobile_tools_device_groups', $device_groups);

  // Clear caches so PURL finds updated modifiers
  // @todo determine the exact caches which need clearing instead
  // of clearing all of them
  drupal_flush_all_caches();
}

/**
 * Retrieve the device groups
 */
function mobile_tools_device_groups() {

  // Load the list of device groups
  $device_groups = variable_get('mobile_tools_device_groups');

  // @todo allow device groups to be toggled on/off
  return $device_groups;
}

/**
 * Detects if a device has the given capability.
 *
 * @return boolean
 *  Returns TRUE if the device is capable, FALSE otherwise.
 */
function mobile_tools_devicecapability($capability) {
  $module = variable_get('mobile-tools-device-capability', 'wurfl');

  // Ensure the desired module is loaded
  drupal_load('module', $module);
  return module_invoke($module, 'mobile_tools_device_capability', $capability);
}

// Private functions

/**
 * Validates the defined device groups. Removes any invalid entries and logs
 * the error.
 */
function _mobile_tools_validate_device_groups(&$device_groups) {
  foreach ($device_groups as $id => $name) {
    if (!ctype_alpha($id)) {
      unset($device_groups[$id]);
      drupal_set_message(t('Invalid device group ID @group. Group IDs must be alphabetic.', array(
        '@group' => $id,
      )), 'error', FALSE);
      watchdog('mobile_tools', 'Invalid device group ID @group. Group IDs must be alphabetic.', array(
        '@group' => $id,
      ), WATCHDOG_ERROR);
    }

    // @todo add validation on the device group name
    //    else if (!ctype_alpha($name)) {
    //      unset($device_groups[$id]);
    //      drupal_set_message(t('Invalid device group name @group. Group names must be alphabetic.', array('@group' => $name)), 'error', FALSE);
    //      watchdog('mobile_tools', 'Invalid device group name @group. Group names must be alphabetic.', array('@group' => $name), WATCHDOG_ERROR);
    //    }
  }
}

/**
 * Scan the active modifiers for any active mobile tools ones.
 *
 * @param array $modifiers
 *  List of active PURL modifiers
 * @return array
 *  Returns an array of active mobile tools modifiers, FALSE otherwise.
 */
function _mobile_tools_get_active_purl_modifiers($modifiers) {

  // Get a list all the mobile tools purl modifiers
  $device_groups = mobile_tools_device_groups();

  // Store only active modifiers
  $active_modifiers = FALSE;

  // Store all defined modifiers
  $defined_modifiers = array();

  // @todo store this list in a static cache for faster access
  foreach ($device_groups as $group => $name) {
    $defined_modifiers[] = 'spaces_mobile_tools_' . $group;
  }

  // Scan the list of modifiers for matches
  foreach ($modifiers as $type => $list) {

    // Mark each active modifier
    foreach ($list as $key => $modifier) {
      if (in_array($modifier->provider, $defined_modifiers)) {
        $active_modifiers[] = $modifier;
      }
    }
  }

  // Return the list of active modifiers
  return $active_modifiers;
}

Functions

Namesort descending Description
mobile_tools_block_info Implements hook_block_info().
mobile_tools_block_message Helper function returning the configurable message for the notification
mobile_tools_block_view Implements hook_block_view().
mobile_tools_ctools_plugin_api Implements hook_ctools_plugin_api().
mobile_tools_detect_device Detect the device
mobile_tools_devicecapability Detects if a device has the given capability.
mobile_tools_device_groups Retrieve the device groups
mobile_tools_device_groups_rebuild Rebuild the list of implemented device groups
mobile_tools_device_redirect Redirect the user based on the device/site type combination
mobile_tools_entity_info_alter Implementation of hook_entity_info_alter()
mobile_tools_flush_caches Implements hook_flush_caches().
mobile_tools_form_alter Alteration to global setting form
mobile_tools_get_active_device_group Get the active device group
mobile_tools_get_device Get $device object. Check if the 'device' argument is present or a cookie is set to overwrite the device:
mobile_tools_help Implements hook_help().
mobile_tools_html_head_alter Implements hook_html_head_alter().
mobile_tools_init Implements hook_init().
mobile_tools_menu Implements hook_menu().
mobile_tools_mobile_tools_detect_device Implements hook_mobile_tools_detect_device().
mobile_tools_mobile_tools_device_groups Implements hook_mobile_tools_device_groups().
mobile_tools_modules_disabled Implements hook_modules_disabled().
mobile_tools_modules_enabled Implements hook_modules_enabled().
mobile_tools_page_alter Implementation of hook_alter()
mobile_tools_permission Implements hook_permission().
mobile_tools_purl_modifiers Implements hook_purl_modifiers().
mobile_tools_purl_provider Implements hook_purl_provider().
mobile_tools_spaces_plugins Implements hook_spaces_plugins().
mobile_tools_spaces_registry Implements hook_spaces_registry().
mobile_tools_switch_link_text Get the text to display on the switch links
mobile_tools_switch_options Generate the options required for PURL to rewrite the URL
_mobile_tools_get_active_purl_modifiers Scan the active modifiers for any active mobile tools ones.
_mobile_tools_validate_device_groups Validates the defined device groups. Removes any invalid entries and logs the error.

Constants

Namesort descending Description
MOBILE_TOOLS_DEVICE_GROUP_PREFIX Prefix added to all device group definitions
MOBILE_TOOLS_SITE_TYPE_DESKTOP
MOBILE_TOOLS_SITE_TYPE_MOBILE
MOBILE_TOOLS_SWITCH_LINK_TEXT @file Functionality to ease the creation of mixed device environments.