MobileComponent.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <?php
  2. namespace Tools\Controller\Component;
  3. use Cake\Controller\Controller;
  4. use Shim\Controller\Component\Component;
  5. use Cake\Core\Configure;
  6. use Cake\Event\Event;
  7. use Cake\Routing\Router;
  8. use Tools\Utility\Utility;
  9. /**
  10. * A component to easily store mobile in session and serve mobile views to users.
  11. * It allows good default values while not being restrictive as you can always
  12. * overwrite the auto-detection manually to force desktop or mobile version.
  13. *
  14. * Uses object attributes as well as Configure to store the results for later use.
  15. *
  16. * Don't foget to set up your mobile detectors in your bootstrap.
  17. *
  18. * Uses Configure to cache lookups in request: User.isMobile and User.setMobile
  19. * - isMobile is the auto-detection (true/false)
  20. * - setMobile can be set by the user and overrides the default behavior/detection
  21. * (1=true/0=false or -1=null which will remove the override)
  22. *
  23. * The overwrite of a user is stored in the session: User.mobile.
  24. * It overwrites the Configure value.
  25. *
  26. * It also pushes switch urls to the view.
  27. *
  28. * @author Mark Scherer
  29. * @license http://opensource.org/licenses/mit-license.php MIT
  30. */
  31. class MobileComponent extends Component {
  32. public $Controller = null;
  33. /**
  34. * Stores the result of the auto-detection.
  35. *
  36. * @var bool
  37. */
  38. public $isMobile = null;
  39. /**
  40. * Stores the final detection result including user preference.
  41. *
  42. * @var bool
  43. */
  44. public $setMobile = null;
  45. /**
  46. * Default values. Can also be set using Configure.
  47. *
  48. * @param array
  49. */
  50. protected $_defaultConfig = [
  51. 'on' => 'beforeFilter', // initialize (prior to controller's beforeRender) or startup
  52. 'engine' => null, // CakePHP internal if null
  53. 'themed' => false, // If false uses subfolders instead of themes: /View/.../mobile/
  54. 'auto' => false, // auto set mobile views
  55. ];
  56. /**
  57. * MobileComponent::initialize()
  58. *
  59. * @param Controller $Controller
  60. * @return void
  61. */
  62. public function initialize(array $config) {
  63. parent::initialize($config);
  64. if ($this->_config['on'] !== 'initialize') {
  65. return;
  66. }
  67. $this->_init();
  68. }
  69. /**
  70. * MobileComponent::startup()
  71. *
  72. * @param Controller $Controller
  73. * @return void
  74. */
  75. public function beforeFilter(Event $event) {
  76. if ($this->_config['on'] !== 'beforeFilter') {
  77. return;
  78. }
  79. $this->_init();
  80. }
  81. /**
  82. * Main auto-detection logic including session based storage to avoid
  83. * multiple lookups.
  84. *
  85. * Uses "mobile" query string to overwrite the auto-detection.
  86. * -1 clears the fixation
  87. * 1 forces mobile
  88. * 0 forces no-mobile
  89. *
  90. * @return void
  91. */
  92. protected function _init() {
  93. $mobileOverwrite = $this->Controller->request->query('mobile');
  94. if ($mobileOverwrite !== null) {
  95. if ($mobileOverwrite === '-1') {
  96. $noMobile = null;
  97. } else {
  98. $wantsMobile = (bool)$mobileOverwrite;
  99. }
  100. $this->request->session()->write('User.mobile', (int)$wantsMobile);
  101. }
  102. $this->isMobile();
  103. if (!$this->_config['auto']) {
  104. return;
  105. }
  106. $this->setMobile();
  107. }
  108. /**
  109. * Sets mobile views as `Mobile` theme.
  110. *
  111. * Only needs to be called if auto is set to false.
  112. * Then you probably want to call this from your AppController::beforeRender().
  113. *
  114. * @return void
  115. */
  116. public function setMobile() {
  117. if ($this->isMobile === null) {
  118. $this->isMobile();
  119. }
  120. $forceMobile = $this->request->session()->read('User.mobile');
  121. if ($forceMobile !== null && !$forceMobile) {
  122. $this->setMobile = false;
  123. } elseif ($forceMobile !== null && $forceMobile || $this->isMobile()) {
  124. $this->setMobile = true;
  125. } else {
  126. $this->setMobile = false;
  127. }
  128. //$urlParams = Router::getParams(true);
  129. $urlParams = [];
  130. if (!isset($urlParams['pass'])) {
  131. $urlParams['pass'] = [];
  132. }
  133. $urlParams = array_merge($urlParams, $urlParams['pass']);
  134. unset($urlParams['pass']);
  135. if (isset($urlParams['prefix'])) {
  136. unset($urlParams['prefix']);
  137. }
  138. if ($this->setMobile) {
  139. $urlParams['?']['mobile'] = 0;
  140. $url = Router::url($urlParams);
  141. $this->Controller->set('desktopUrl', $url);
  142. } else {
  143. $urlParams['?']['mobile'] = 1;
  144. $url = Router::url($urlParams);
  145. $this->Controller->set('mobileUrl', $url);
  146. }
  147. Configure::write('User.setMobile', (int)$this->setMobile);
  148. if (!$this->setMobile) {
  149. return;
  150. }
  151. $this->Controller->viewBuilder()->className('Theme');
  152. $this->Controller->viewBuilder()->theme('Mobile');
  153. }
  154. /**
  155. * Determines if we need to so serve mobile views based on session preference
  156. * and browser headers.
  157. *
  158. * @return bool Success
  159. */
  160. public function isMobile() {
  161. if ($this->isMobile !== null) {
  162. return $this->isMobile;
  163. }
  164. $this->isMobile = Configure::read('User.isMobile');
  165. if ($this->isMobile !== null) {
  166. return $this->isMobile;
  167. }
  168. $this->isMobile = (bool)$this->detect();
  169. Configure::write('User.isMobile', (int)$this->isMobile);
  170. return $this->isMobile;
  171. }
  172. /**
  173. * Detects if the current request is from a mobile device.
  174. *
  175. * Note that the cake internal way might soon be deprecated:
  176. * https://github.com/cakephp/cakephp/issues/2546
  177. *
  178. * @return bool Success
  179. */
  180. public function detect() {
  181. // Deprecated - the vendor libs are far more accurate and up to date
  182. if (!$this->_config['engine']) {
  183. if (isset($this->Controller->RequestHandler)) {
  184. return $this->Controller->RequestHandler->isMobile();
  185. }
  186. return $this->Controller->request->is('mobile');
  187. }
  188. if (is_callable($this->_config['engine'])) {
  189. return call_user_func($this->_config['engine']);
  190. }
  191. throw new CakeException(sprintf('Engine %s not available', $this->_config['engine']));
  192. }
  193. }