浏览代码

Allow better detection of mobile devices. Also added tests.

euromark 12 年之前
父节点
当前提交
c893aa22eb

+ 143 - 59
Controller/Component/MobileComponent.php

@@ -4,12 +4,24 @@ App::uses('Component', 'Controller');
 App::uses('Router', 'Routing');
 
 /**
- * Uses Session: User.mobile and User.nomobile
+ * A component to easily serve mobile views to users.
+ * It allows good default values while not being restrictive as you can always
+ * overwrite the auto-detection manually to force desktop or mobile version.
+ *
+ * Uses Session to remember lookups: User.mobile and User.nomobile
  * - mobile is the auto-detection (true/false)
  * - nomobile can be set by the user and overrides the default behavior/detection
  *   (1=true/0=false or -1=null which will remove the override)
+ * Uses object attributes as well as Configure to store the results for later use.
+ * It also pushes switch urls to the view.
+ *
+ * New:
+ * - Support for named params has been dropped in favor of query strings.
+ * - Support for different engines (vendor should be used as this is up to date).
+ * - Allows Configure setup and auto-start for easy default cases.
+ * - Accept closures to easily use any custom detection engine.
+ * - Cleanup and tests
  *
- * TODO: differentaite between "isMobile" and "has/wants mobile"
  * @author Mark Scherer
  * @license MIT
  */
@@ -17,42 +29,106 @@ class MobileComponent extends Component {
 
 	public $components = array('Session');
 
+	public $Controller = null;
+
+	/**
+	 * Stores the result of the auto-detection.
+	 *
+	 * @var boolean
+	 */
 	public $isMobile = null;
 
+	/**
+	 * Stores the final detection result including user preference.
+	 *
+	 * @var boolean
+	 */
 	public $setMobile = null;
 
-	public $Controller = null;
-
+	/**
+	 * Default values. Can also be set using Configure.
+	 *
+	 * @param array
+	 */
 	protected $_defaults = array(
-		'engine' => 'cake',
+		'on' => 'startup', // initialize (prior to controller's beforeRender) or startup
+		'engine' => 'vendor', // cake internal (deprecated), tools (deprecated) or vendor
+		'themed' => false, // If false uses subfolders instead of themes: /View/.../mobile/
+		'mobile' => array('mobile', 'tablet'), // what is mobile? tablets as well? only for vendor
 		'auto' => false, // auto set mobile views
 	);
 
 	/**
-	 * If false uses subfolders: /View/.../mobile/
+	 * MobileComponent::__construct()
+	 *
+	 * @param ComponentCollection $collection
+	 * @param array $settings
 	 */
-	public $themed = true;
-
 	public function __construct(ComponentCollection $collection, $settings = array()) {
-		$settings = array_merge($this->_defaults, $settings);
+		$settings = array_merge($this->_defaults, (array)Configure::read('Mobile'), $settings);
 		parent::__construct($collection, $settings);
 	}
 
+	/**
+	 * MobileComponent::initialize()
+	 *
+	 * @param Controller $Controller
+	 * @return void
+	 */
 	public function initialize(Controller $Controller) {
 		parent::initialize($Controller);
 		$this->Controller = $Controller;
 
-		if (isset($this->Controller->request->params['named']['mobile'])) {
-			if ($this->Controller->request->params['named']['mobile'] === '-1') {
+		if ($this->settings['on'] !== 'initialize') {
+			return;
+		}
+		$this->_init();
+	}
+
+	/**
+	 * MobileComponent::startup()
+	 *
+	 * @param Controller $Controller
+	 * @return void
+	 */
+	public function startup(Controller $Controller) {
+		parent::startup($Controller);
+
+		if ($this->settings['on'] !== 'startup') {
+			return;
+		}
+		$this->_init();
+	}
+
+	/**
+	 * Main auto-detection logic including session based storage to avoid
+	 * multiple lookups.
+	 *
+	 * Uses "mobile" query string to overwrite the auto-detection.
+	 * -1 clears the fixation
+	 * 1 forces mobile
+	 * 0 forces no-mobile
+	 *
+	 * @return void
+	 */
+	protected function _init() {
+		$mobileOverwrite = $this->Controller->request->query('mobile');
+
+		if ($mobileOverwrite !== null) {
+			if ($mobileOverwrite === '-1') {
 				$noMobile = null;
 			} else {
-				$wantsMobile = (bool)$this->Controller->request->params['named']['mobile'];
+				$wantsMobile = (bool)$mobileOverwrite;
 				$noMobile = (int)(!$wantsMobile);
 			}
 			$this->Session->write('User.nomobile', $noMobile);
-
 		}
 		$this->isMobile();
+
+		if (!$this->settings['auto']) {
+			return;
+		}
+		$this->setMobile();
 	}
 
 	/**
@@ -91,7 +167,10 @@ class MobileComponent extends Component {
 	}
 
 	/**
-	 * Set mobile views as `Mobile` theme
+	 * Sets mobile views as `Mobile` theme.
+	 *
+	 * Only needs to be called if auto is set to false.
+	 * Then you probably want to call this from your AppController::beforeRender().
 	 *
 	 * @return void
 	 */
@@ -121,22 +200,24 @@ class MobileComponent extends Component {
 		}
 
 		if ($this->setMobile) {
-			$url = Router::url(array_merge($urlParams, array('mobile' => 0)));
+			$urlParams['?']['mobile'] = 0;
+			$url = Router::url($urlParams);
 			$this->Controller->set('desktopUrl', $url);
 
 		} else {
-			$url = Router::url(array_merge($urlParams, array('mobile' => 1)));
+			$urlParams['?']['mobile'] = 1;
+			$url = Router::url($urlParams);
 			$this->Controller->set('mobileUrl', $url);
 		}
 
-		Configure::write('User.mobile', $this->isMobile);
-		Configure::write('User.setMobile', $this->setMobile);
+		Configure::write('User.isMobile', (int)$this->isMobile);
+		Configure::write('User.setMobile', (int)$this->setMobile);
 
-		if (!$this->isMobile) {
+		if (!$this->setMobile) {
 			return;
 		}
 
-		if (!$this->themed) {
+		if (!$this->settings['themed']) {
 			$this->serveMobileIfAvailable();
 			return;
 		}
@@ -146,7 +227,8 @@ class MobileComponent extends Component {
 	}
 
 	/**
-	 * Determine if we need to so serve mobile views based on session preference and browser headers.
+	 * Determines if we need to so serve mobile views based on session preference
+	 * and browser headers.
 	 *
 	 * @return boolean Success
 	 */
@@ -155,69 +237,71 @@ class MobileComponent extends Component {
 			return $this->isMobile;
 		}
 
-		$wantsMobile = null;
-		if (isset($this->Controller->request->params['named']['mobile'])) {
-			if ($this->Controller->request->params['named']['mobile'] === '-1') {
-				$this->Session->delete('User.mobile');
-			} else {
-				$wantsMobile = (bool)$this->Controller->request->params['named']['mobile'];
-			}
-		}
-		if ($wantsMobile) {
-			$this->isMobile = $wantsMobile;
-			return $this->isMobile;
-		}
-
 		$this->isMobile = $this->Session->read('User.mobile');
 		if ($this->isMobile !== null) {
 			return $this->isMobile;
 		}
-		$this->isMobile = (int)$this->detect();
-		$this->Session->write('User.mobile', $this->isMobile);
+		$this->isMobile = (bool)$this->detect();
+		$this->Session->write('User.mobile', (int)$this->isMobile);
 		return $this->isMobile;
 	}
 
 	/**
-	 * Detect if the current request is from a mobile device
+	 * Detects if the current request is from a mobile device.
+	 *
+	 * Note that the cake internal way might soon be deprecated:
+	 * https://github.com/cakephp/cakephp/issues/2546
 	 *
 	 * @return boolean Success
 	 */
 	public function detect() {
-		if ($this->settings['engine'] !== 'cake') {
+		// Deprecated - the vendor libs are far more accurate and up to date
+		if ($this->settings['engine'] === 'cake') {
+			$this->Controller->request->addDetector('mobile', array('options' => array('OMNIA7')));
+			return $this->Controller->request->is('mobile');
+		}
+		if (is_callable($this->settings['engine'])) {
+			return call_user_func($this->settings['engine']);
+		}
+		if (!in_array($this->settings['engine'], array('tools', 'vendor'))) {
 			throw new CakeException(__('Engine %s not available', $this->settings['engine']));
-			//TODO
-			// $this->detectByTools()
-			// $this->detectByWurfl()
 		}
-		$this->Controller->request->addDetector('mobile', array('options' => array('OMNIA7')));
-		return $this->Controller->request->is('mobile');
+		return $this->detectByVendor($this->settings['engine']);
 	}
 
 	/**
+	 * Simple auto-detection based on Tools plugin or vendor classes.
+	 *
+	 * @param string $engine
 	 * @return boolean Success
 	 */
-	public function detectByTools() {
+	public function detectByVendor($engine) {
 		$isMobile = $this->Session->read('Session.mobile');
 		if ($isMobile !== null) {
 			return $isMobile;
 		}
-		App::uses('UserAgentLib', 'Tools.Lib');
-		$UserAgentLib = new UserAgentLib();
-		return (bool)$UserAgentLib->isMobile();
-	}
 
-	/**
-	 * @return boolean Success
-	 */
-	public function detectByWurfl() {
-		App::import('Vendor', 'WURFL', array('file' => 'WURFLManagerProvider.php'));
-		$wurflConfigFile = APP . 'Config' . DS . 'wurfl ' . DS . 'config.xml';
-		$wurflManager = WURFL_WURFLManagerProvider::getWURFLManager($wurflConfigFile);
+		// Deprecated - the vendor libs are far more accurate and up to date
+		if ($engine === 'tools') {
+			App::uses('UserAgentLib', 'Tools.Lib');
+			$UserAgentLib = new UserAgentLib();
+			return (bool)$UserAgentLib->isMobile();
+		}
+
+		App::import('Vendor', 'Tools.MobileDetect', array('file' => 'MobileDetect/Mobile_Detect.php'));
+		$MobileDetect = new Mobile_Detect();
 
-		$requestingDevice = $wurflManager->getDeviceForHttpRequest($_SERVER);
-		if ($requestingDevice->getCapability('is_wireless_device') === 'true') {
-			return true;
+		$result = empty($this->settings['mobile']) ? 0 : 1;
+		if (in_array('mobile', $this->settings['mobile'])) {
+			$result &= $MobileDetect->isMobile();
 		}
-		return false;
+		if (in_array('tablet', $this->settings['mobile'])) {
+			$result |= $MobileDetect->isTablet();
+		} else {
+			$result &= !$MobileDetect->isTablet();
+		}
+
+		return (bool)$result;
 	}
+
 }

+ 249 - 0
Test/Case/Controller/Component/MobileComponentTest.php

@@ -0,0 +1,249 @@
+<?php
+
+App::uses('MobileComponent', 'Tools.Controller/Component');
+App::uses('Component', 'Controller');
+App::uses('AppController', 'Controller');
+
+/**
+ * Test MobileComponent
+ */
+class MobileComponentTest extends CakeTestCase {
+
+	public $fixtures = array('core.cake_session');
+
+	/**
+	 * SetUp method
+	 *
+	 * @return void
+	 */
+	public function setUp() {
+		parent::setUp();
+
+		$this->Controller = new MobileComponentTestController(new CakeRequest(null, false), new CakeResponse());
+		$this->Controller->constructClasses();
+		$this->Controller->Mobile->Controller = $this->Controller;
+
+		CakeSession::delete('User');
+		Configure::delete('User');
+	}
+
+	/**
+	 * Tear-down method. Resets environment state.
+	 *
+	 * @return void
+	 */
+	public function tearDown() {
+		parent::tearDown();
+
+		unset($this->Controller->Mobile);
+		unset($this->Controller);
+	}
+
+	public function testDetect() {
+		$is = $this->Controller->Mobile->detect();
+		$this->assertFalse($is);
+	}
+
+	public function testMobileNotMobile() {
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$this->assertFalse($this->Controller->Mobile->isMobile);
+
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 0), $session);
+	}
+
+	public function testMobileForceActivated() {
+		$this->Controller->request->query['mobile'] = 1;
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('nomobile' => 0, 'mobile' => 0), $session);
+
+		$this->Controller->Mobile->setMobile();
+		$configure = Configure::read('User');
+		$this->assertSame(array('isMobile' => 0, 'setMobile' => 1), $configure);
+		$this->assertEquals(array('desktopUrl' => '/?mobile=0'), $this->Controller->viewVars);
+	}
+
+	public function testMobileForceDeactivated() {
+		$this->Controller->request->query['mobile'] = 0;
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('nomobile' => 1, 'mobile' => 0), $session);
+
+		$this->Controller->Mobile->setMobile();
+		$configure = Configure::read('User');
+		$this->assertSame(array('isMobile' => 0, 'setMobile' => 0), $configure);
+		$this->assertEquals(array('mobileUrl' => '/?mobile=1'), $this->Controller->viewVars);
+	}
+
+	public function testMobileFakeMobile() {
+		$_SERVER['HTTP_USER_AGENT'] = 'Some Android device';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 1), $session);
+
+		$this->assertTrue($this->Controller->Mobile->isMobile);
+
+		$this->Controller->Mobile->setMobile();
+		$configure = Configure::read('User');
+		$this->assertSame(array('isMobile' => 1, 'setMobile' => 1), $configure);
+	}
+
+	public function testMobileFakeMobileForceDeactivated() {
+		$this->Controller->request->query['mobile'] = 0;
+		$_SERVER['HTTP_USER_AGENT'] = 'Some Android device';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('nomobile' => 1, 'mobile' => 1), $session);
+
+		$this->assertTrue($this->Controller->Mobile->isMobile);
+
+		$this->Controller->Mobile->setMobile();
+		$configure = Configure::read('User');
+		$this->assertSame(array('isMobile' => 1, 'setMobile' => 0), $configure);
+	}
+
+	public function testMobileFakeMobileAuto() {
+		$this->Controller->Mobile->settings['auto'] = true;
+		$_SERVER['HTTP_USER_AGENT'] = 'Some Android device';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 1), $session);
+		$this->assertTrue($this->Controller->Mobile->isMobile);
+
+		$configure = Configure::read('User');
+		$this->assertSame(array('isMobile' => 1, 'setMobile' => 1), $configure);
+		$this->assertTrue($this->Controller->Mobile->setMobile);
+	}
+
+	public function testMobileVendorEngineCake() {
+		$this->Controller->Mobile->settings['engine'] = 'cake';
+		$_SERVER['HTTP_USER_AGENT'] = 'Some Android device';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 1), $session);
+		$this->assertTrue($this->Controller->Mobile->isMobile);
+	}
+
+	public function testMobileVendorEngineTools() {
+		$this->Controller->Mobile->settings['engine'] = 'tools';
+		$_SERVER['HTTP_USER_AGENT'] = 'Some Android device';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 1), $session);
+		$this->assertTrue($this->Controller->Mobile->isMobile);
+	}
+
+	public function testMobileCustomMobile() {
+		$this->Controller->Mobile->settings['mobile'] = array();
+		$_SERVER['HTTP_USER_AGENT'] = 'Some Ipad device';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 0), $session);
+	}
+
+	public function testMobileCustomMobileMobile() {
+		$this->Controller->Mobile->settings['mobile'] = array('mobile');
+		$_SERVER['HTTP_USER_AGENT'] = 'Some Android device';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 1), $session);
+	}
+
+	public function testMobileCustomMobileTablet() {
+		$this->Controller->Mobile->settings['mobile'] = array('tablet');
+		$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A403 Safari/8536.25';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 1), $session);
+	}
+
+	public function testMobileCustomMobileTablet2() {
+		$this->Controller->Mobile->settings['mobile'] = array('mobile');
+		$_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A403 Safari/8536.25';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 0), $session);
+	}
+
+	public function testMobileEngineClosure() {
+		$closure = function() {
+			return $_SERVER['HTTP_USER_AGENT'] === 'Foo';
+		};
+		$this->Controller->Mobile->settings['engine'] = $closure;
+		$_SERVER['HTTP_USER_AGENT'] = 'Foo';
+
+		$this->Controller->Mobile->initialize($this->Controller);
+		$this->Controller->Mobile->startup($this->Controller);
+		$session = CakeSession::read('User');
+		$this->assertSame(array('mobile' => 1), $session);
+	}
+
+}
+
+class MobileComponentTestController extends AppController {
+
+	/**
+	 * Components property
+	 *
+	 * @var array
+	 */
+	public $components = array('Tools.Mobile');
+
+	/**
+	 * Failed property
+	 *
+	 * @var boolean
+	 */
+	public $failed = false;
+
+	/**
+	 * Used for keeping track of headers in test
+	 *
+	 * @var array
+	 */
+	public $testHeaders = array();
+
+	/**
+	 * Fail method
+	 *
+	 * @return void
+	 */
+	public function fail() {
+		$this->failed = true;
+	}
+
+	/**
+	 * Redirect method
+	 *
+	 * @param mixed $option
+	 * @param mixed $code
+	 * @param mixed $exit
+	 * @return void
+	 */
+	public function redirect($url, $status = null, $exit = true) {
+		return $status;
+	}
+
+}

文件差异内容过多而无法显示
+ 1183 - 0
Vendor/MobileDetect/Mobile_Detect.php


+ 172 - 0
Vendor/MobileDetect/README.md

@@ -0,0 +1,172 @@
+<img src="http://demo.mobiledetect.net/logo-github.png">
+
+> Motto: "Every business should have a mobile detection script to detect mobile readers."
+
+<i>Mobile_Detect is a lightweight PHP class for detecting mobile devices (including tablets).
+It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.</i>
+
+Nowadays web site/app <b>content strategy</b> matters, this is why you need all the tools to deliver the best and the fastest experience for the small screens. Mobile_Detect class is a [server-side detection](http://www.w3.org/TR/mwabp/#bp-devcap-detection) tool, it is not a replacement for Responsive Web Design (media queries) or other forms of client-side feature detection.
+
+We're commited to make Mobile_Detect the best open-source mobile detection resource and this is why before each release we're running [unit tests](./tests), we also research and update the detection rules on <b>daily</b> and <b>weekly</b> basis.
+
+##### Download and demo
+
+<a href="https://github.com/serbanghita/Mobile-Detect/tags">Latest releases</a>, <a href="https://github.com/serbanghita/Mobile-Detect/blob/devel/Mobile_Detect.php">Latest dev branch</a>, <a href="https://packagist.org/packages/mobiledetect/mobiledetectlib">composer package</a>
+
+See also: <a href="https://github.com/serbanghita/Mobile-Detect/wiki/Become-a-contributor">:bulb: Become a contributor</a> / <a href="https://github.com/serbanghita/Mobile-Detect/wiki/History">:coffee: History</a> / <a href="https://github.com/serbanghita/Mobile-Detect/wiki/Code-examples">:point_right: Code examples</a> /
+<a href="http://is.gd/mobiletest">:iphone: Live demo!</a> (point your device browser to this URL)
+
+##### Help
+
+<a href="http://pledgie.com/campaigns/21856"><img alt='Click here to lend your support to: Research and development of Mobile Detect library!' src='http://www.pledgie.com/campaigns/21856.png?skin_name=chrome' border='0' /></a>
+<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=mobiledetectlib%40gmail%2ecom&lc=US&item_name=Mobile%20Detect&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" border="0"></a>
+
+I'm currently paying for hosting and spend a lot of my family time :family: to maintain the project and planning the future releases.
+I would highly appreciate any money donations that will keep the research going.
+
+Special thanks to the community :+1: for donations, [BrowserStack](http://browserstack.com) - for providing access to their great platform, [Zend](http://zend.com) - for donating licenses, [Dragos Gavrila](https://twitter.com/grafician) who contributed with the logo.
+
+##### 3rd party modules / [Submit new](https://github.com/serbanghita/Mobile-Detect/issues/new?title=New%203rd%20party%20module&body=Name, Link and Description of the module.)
+
+:point_right: The recommendation is that you keep Mobile_Detect.php class in a separate module and not include it in your script core because of the high frequency of updates.
+
+<table>
+
+<tr>
+  <td>Wordpress</td>
+  <td>
+    <p><a href="http://wordpress.org/extend/plugins/wp-mobile-detect/">Wordpress Mobile Detect</a> - Gives you the ability to wrap that infographic in a [notdevice][/notdevice] shortcode so at the server level <code>WordPress</code> will decide to show that content only if the user is NOT on a phone or tablet. Made by <a href="http://profiles.wordpress.org/professor44/">Jesse Friedman</a>.</p>
+
+    <p><a href="http://wordpress.org/plugins/mobble/">mobble</a> - provides mobile related conditional functions for your site. e.g. is_iphone(), is_mobile() and is_tablet(). Made by Scott Evans.</p>
+
+    <p><a href="https://github.com/iamspacehead/responsage">Wordpress Responsage</a> - A small <code>WordPress</code> theme plugin that allows you to make your images responsive. Made by <a href="https://github.com/iamspacehead">Adrian Ciaschetti</a>.</p>
+
+    <p><a href="http://wordpress.org/plugins/social-popup/">Social PopUP</a> - This plugin will display a popup or splash screen when a new user visit your site showing a Google+, Twitter and Facebook follow links. It uses Mobile_Detect to detect mobile devices.</p>
+  </td>
+</tr>
+
+<tr>
+  <td>Drupal</td>
+  <td>
+    <p><a href="http://drupal.org/project/mobile_switch">Drupal Mobile Switch</a> - The Mobile Switch <code>Drupal</code> module provides a automatic theme switch functionality for mobile devices,
+    detected by Browscap or Mobile Detect. Made by <a href="http://drupal.org/user/45267">Siegfried Neumann</a>.</p>
+
+      <p><a href="http://drupal.org/project/context_mobile_detect">Drupal Context Mobile Detect</a> - This is a <code>Drupal context</code> module which integrates Context and PHP Mobile Detect library.
+      Created by <a href="http://drupal.org/user/432492">Artem Shymko</a>.</p>
+
+      <p><a href="http://drupal.org/project/mobile_detect">Drupal Mobile Detect</a> - Lightweight mobile detect module for <code>Drupal</code> created by <a href="http://drupal.org/user/325244">Matthew Donadio</a></p>
+  </td>
+</tr>
+
+  <tr>
+    <td>Joomla</td>
+    <td><p><a href="http://www.yagendoo.com/en/blog/free-mobile-detection-plugin-for-joomla.html">yagendoo Joomla! Mobile Detection Plugin</a> - Lightweight PHP plugin for Joomla! that detects a mobile browser using the Mobile Detect class. Made by <a href="http://www.yagendoo.com/">yagendoo media</a>.</p></td>
+ </tr>
+
+ <tr>
+     <td>Magento</td>
+     <td><p><a href="http://www.magentocommerce.com/magento-connect/catalog/product/view/id/16835/">Magento</a> - This <code>Magento helper</code> from Optimise Web enables the use of all functions provided by MobileDetect.net.
+     Made by <a href="http://www.kathirvel.com">Kathir Vel</a>.</p></td>
+ </tr>
+
+ <tr>
+  <td>PrestaShop</td>
+  <td><p>Free, secure and open source shopping cart platform. Included in the distribution since 1.5.x.</p></td>
+ </tr>
+
+ <tr>
+  <td>Zend Framework</td>
+  <td><p><a href="https://github.com/neilime/zf2-mobile-detect.git">ZF2 Mobile-Detect</a> - Zend Framework 2 module that provides Mobile-Detect features (Mobile_Detect class as a service, helper for views and plugin controllers). Made by <a href="https://github.com/neilime">neilime</a></p></td>
+ </tr>
+
+  <tr>
+    <td>Symfony</td>
+    <td><p><a href="https://github.com/suncat2000/MobileDetectBundle">Symfony2 Mobile Detect Bundle</a> - The bundle for detecting mobile devices, manage mobile view and redirect to the mobile and tablet version.
+        Made by <a href="https://github.com/suncat2000">Nikolay Ivlev</a>.</p>
+        <p><a href="https://github.com/jbinfo/MobileDetectServiceProvider">Silex Mobile Detect Service Provider</a> - <code>Silex</code> service provider to interact with Mobile detect class methods. Made by <a href="https://github.com/jbinfo">Lhassan Baazzi</a>.</p>
+     </td>
+  </tr>
+
+  <tr>
+    <td>ExpressionEngine</td>
+    <td><p><a href="https://github.com/garethtdavies/detect-mobile">EE2 Detect Mobile</a> - Lightweight PHP plugin for <code>EE2</code> that detects a mobile browser using the Mobile Detect class. Made by <a href="https://github.com/garethtdavies">Gareth Davies</a>.</p></td>
+ </tr>
+
+ <tr>
+  <td>Yii Framework</td>
+  <td><p><a href="https://github.com/iamsalnikov/MobileDetect">Yii Extension</a> - Mobile detect plugin for Yii framework. Made by <a href="https://github.com/iamsalnikov">Alexey Salnikov</a>.</p></td>
+ </tr>
+
+<tr>
+    <td>CakePHP</td>
+    <td><p><a href="https://github.com/chronon/CakePHP-MobileDetectComponent-Plugin">CakePHP MobileDetect</a> - <code>plugin</code> component for <code>CakePHP</code> 2.x. Made by <a href="https://github.com/chronon">Gregory Gaskill</a></p></td>
+</tr>
+
+<tr>
+    <td>FuelPHP</td>
+    <td><a href="https://github.com/rob-bar/special_agent">Special Agent</a> is a FuelPHP package which uses php-mobile-detect to determine whether a device is mobile or not.
+It overrides the Fuelphp Agent class its methods. Made by <a href="https://github.com/rob-bar">Robbie Bardjin</a>.</td>
+</tr>
+
+<tr>
+  <td>Typo3</td>
+  <td><a href="http://docs.typo3.org/typo3cms/extensions/px_mobiledetect/1.0.2/">px_mobiledetect</a> is an extension that helps to detect visitor's mobile device class (if that’s tablet or mobile device like smartphone). Made by Alexander Tretyak.</td>
+</tr>
+
+<tr>
+  <td>Statamic</td>
+  <td><p><a href="https://github.com/sergeifilippov/statamic-mobile-detect">Statamic CMS Mobile Detect</a> - <code>plugin</code>. Made by <a href="https://github.com/sergeifilippov">Sergei Filippov</a>.</p></td>
+</tr>
+
+<tr>
+  <td>mobile-detect.js</td>
+  <td><p>A <a href="https://github.com/hgoebl/mobile-detect.js">JavaScript port</a> of Mobile-Detect class. Made by <a href="https://github.com/hgoebl">Heinrich Goebl</a></p></td>
+  </tr>
+
+ <tr>
+      <td>python</td>
+      <td><p><a href="http://pypi.python.org/pypi/pymobiledetect">pymobiledetect</a> - Mobile detect <code>python package</code>. Made by Bas van Oostveen.</p></td>
+ </tr>
+
+ <tr>
+    <td>MemHT</td>
+    <td><p><code>MemHT</code> is a Free PHP CMS and Blog that permit the creation and the management online of websites with few and easy steps. Has the class included in the core.</p></td>
+ </tr>
+
+ <tr>
+  <td>concrete5</td>
+  <td><p><code>concrete5</code> is a CMS that is free and open source. The library is included in the core.</p></td>
+ </tr>
+
+ <tr>
+  <td>engine7</td>
+  <td><p><a href="https://github.com/gchiappe/exengine7">ExEngine 7</a> PHP Open Source Framework. The Mobile_Detect class is included in the engine.</p></td>
+ </tr>
+
+ <tr>
+  <td>Zikula</td>
+  <td><p><a href="http://zikula.org/">Zikula</a> is a free and open-source Content Management Framework, which allows you to run impressive websites and build powerful online applications. The core uses Mobile-Detect to switch to a special Mobile theme, using jQueryMobile</p></td>
+ </tr>
+
+<tr>
+    <td>UserAgentInfo</td>
+    <td><p><a href="https://github.com/quentin389/UserAgentInfo">UserAgentInfo</a> is a PHP class for parsing user agent strings (HTTP_USER_AGENT). Includes mobile checks, bot checks, browser types/versions and more.
+Based on browscap, Mobile_Detect and ua-parser. Created for high traffic websites and fast batch processing. Made by <a href="https://github.com/quentin389">quentin389</a></p></td>
+</tr>
+
+<tr>
+<td>RexBrowscap</td>
+<td><p><a href="https://github.com/jdlx/_rex_browscap">RexBrowscap</a> - Addon for Redaxo CMS - bundles phpbrowscap, mobile-detect and some client side funcs for detecting UA capabilities in one package. Made by <a href="https://github.com/jdlx">jdlx</a></p></td>
+</tr>
+
+<tr>
+  <td>Craft CMS</td>
+  <td><p><a href="https://github.com/lewisjenkins/craft-lj-mobiledetect">LJ Mobile Detect</a> is a simple implementation of Mobile Detect for Craft CMS. Made by <a href="https://github.com/lewisjenkins">Lewis Jenkins</a></p></td>
+</tr>
+
+<tr>
+  <td>Laravel</td>
+  <td><p><a href="https://github.com/jenssegers/Laravel-Agent">Laravel-Agent</a> a user agent class for Laravel, based on Mobile Detect with some additional functionality. Made by <a href="https://github.com/jenssegers">Jens Segers</a></p></td>
+</tr>
+
+</table>