Version: current

      1 <?php
      2
      3 class MobileExtension extends Extension {
      4
      5     /**
      6      * Режим автоматичеких редиректов мобильных браузеров, возможные значения:
      7      * - 'http': определение мобильного браузера по user agent, http redirect,
      8      * - 'js': определение мобильного браузера по ширине экрана, js редирект,
      9      * - 'auto': сейчас равносилен js
     10      * - 'off', '': без редиректа
     11      * @static
     12      */
     13     private static $auto_redirect = 'js';
     14
     15     /**
     16      * Настройки мобильных доменов, формат:
     17      * array(
     18      *  'mobile1' => array('canonical1'),
     19      *  'mobile2' => array('canonical2', 'domain3', 'domain4')
     20      * )
     21      * mobile1 - мобильный домен для домена canonical1 и всех не указанных доменов (потому что первый)
     22      * mobile2 - мобильный домен для доменов canonical2, domain3 и domain4
     23      * canonical1 - канонический домен для mobile1 (потому что первый)
     24      * canonical2 - канонический домен для mobile2 (потому что первый)
     25      *
     26      * если массив пустой - считаем "m.*" - мобильным доменом, другие домены - основными (префикс "www." из домена вырезаем)
     27      * каноническим для мобильного считаем домен без "m."
     28      * @static
     29      */
     30     private static $mobile_domains = array();
     31
     32     /**
     33      * Ширина для перехода в мобильную версию на js
     34      * @static
     35      */
     36     private static $mobile_width = 800;
     37
     38     /**
     39      * Тема для мобильных доменов
     40      * @static
     41      */
     42     private static $mobile_theme = 'mobile';
     43
     44     /**
     45      * Имя куки для отключения автоматического перехода на мобильную версию
     46      * @static
     47      */
     48     private static $cookie_name = 'mobignore';
     49
     50     /**
     51      * Время жизни куки в днях (0 - сессионная)
     52      * @static
     53      */
     54     private static $cookie_ttl = 0;
     55
     56     /**
     57      * устанавливаем способ авто редиректа
     58      *
     59      * @param string $value
     60      */
     61     static function set_auto_redirect($value) {
     62         if (in_array($value, array('http', 'js', 'auto', '', 'off')))
     63             self::$auto_redirect = $value;
     64     }
     65
     66     /*
     67      * Устанавливаем соответствия доменов
     68      *
     69      * @param array $value
     70      */
     71     static function setup_domains($value) {
     72         self::$mobile_domains = $value;
     73     }
     74
     75     /**
     76      * Установить ширину для перехода в мобильную версию на js
     77      *
     78      * @param int $value
     79      */
     80     static function set_mobile_width($value = 800) {
     81         self::$mobile_width = intval($value);
     82     }
     83
     84     /**
     85      * Устанавливаем тему для мобильного домена
     86      *
     87      * @param string $value
     88      */
     89     static function set_mobile_theme($value = 'mobile') {
     90         self::$mobile_theme = $value;
     91     }
     92
     93     static function get_mobile_theme() {
     94         return self::$mobile_theme;
     95     }
     96
     97     /**
     98      * Устанавливаем имя куки, использующейся для отключения автоперехода но мобильную версию
     99      *
    100      * @param string $value
    101      */
    102     static function set_cookie_name($value = 'mobignore') {
    103         self::$cookie_name = $value;
    104     }
    105
    106     /**
    107      * Устанавливаем время жизни куки, использующейся для отключения автоперехода но мобильную версию
    108      *
    109      * @param int $value
    110      */
    111     static function set_cookie_ttl($value = 0) {
    112         self::$cookie_ttl = intval($value);
    113     }
    114
    115     /**
    116      * Является ли текущий браузер мобильным (по user agent)
    117      *
    118      * @return
    119      */
    120     static function is_mobile_browser() {
    121         if (!isset($_SERVER['HTTP_USER_AGENT']) || !$_SERVER['HTTP_USER_AGENT'])
    122             return false;
    123
    124         return preg_match('/android|iphone|ipad|windows phone|silk|jdq39|rim tablet|bb10|symbian|mobile/i', $_SERVER['HTTP_USER_AGENT']);
    125     }
    126
    127     /**
    128      * Является ли текущий домен мобильным
    129      *
    130      * @return boolean
    131      */
    132     static function is_mobile_domain() {
    133         $domain = self::current_domain();
    134
    135         if (count(self::$mobile_domains) > 0) {
    136             if (isset(self::$mobile_domains[$domain]))
    137                 return true;
    138         }
    139         else {
    140             if (substr($domain, 0, 2) == 'm.') {
    141                 return true;
    142             }
    143         }
    144         return false;
    145     }
    146
    147     /**
    148      * Возвращает канонический домен для мобильного домена
    149      *
    150      * @return string;
    151      */
    152     static function canonical_domain() {
    153         if (!self::is_mobile_domain())
    154             return '';
    155
    156         $domain = self::current_domain();
    157         if (count(self::$mobile_domains) > 0) {
    158             if (isset(self::$mobile_domains[$domain])) {
    159                 return self::$mobile_domains[$domain][0];
    160             }
    161             self::$mobile_domains[0][0];
    162         } else if (substr($domain, 0, 2) == 'm.') {
    163             $domain = substr($domain, 2);
    164         }
    165         return $domain;
    166     }
    167
    168     /**
    169      * возвращает мобильный домен для текущего домена
    170      *
    171      * @return string
    172      */
    173     static function mobile_domain() {
    174         if (self::is_mobile_domain())
    175             return '';
    176
    177         $domain = self::current_domain();
    178         if (count(self::$mobile_domains) > 0) {
    179             foreach(self::$mobile_domains as $mobile_domain => $base_domains) {
    180                 if (array_search($domain, $base_domains) !== false)
    181                     return $mobile_domain;
    182             }
    183         }
    184         return 'm.' . $domain;
    185     }
    186
    187     /**
    188      * Возвращает текущий домен
    189      *
    190      * @return string
    191      */
    192     static function current_domain() {
    193         $domain = Director::protocolAndHost(); //на случай запуска из консоли
    194         $parts = explode('//', $domain);
    195         if (!isset($parts[1]))
    196             return false;
    197         $domain = $parts[1];
    198         if (substr($domain, 0, 4) == 'www.') {
    199             $domain = substr($domain, 4);
    200         }
    201         return $domain;
    202     }
    203
    204     /**
    205      * возвращает домен для куки (в том чилсе и для сессии
    206      *
    207      * @return string
    208      */
    209     static function cookie_domain() {
    210         $domain = self::current_domain();
    211         if (substr($domain, 0, 2) == 'm.')
    212             $domain = substr($domain, 2);
    213         return '.' . $domain;
    214     }
    215
    216     /**
    217      * Возвращает текущий протокол
    218      *
    219      * @return string
    220      */
    221     static function protocol() {
    222         if(isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https') return "https://";
    223         return (isset($_SERVER['SSL']) || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) ? 'https://' : 'http://';
    224     }
    225
    226     /**
    227      * Генерация Метатегов страницы
    228      * Перекрывает соответсвующий метод для SiteTree и потому должно его вызывать само
    229      *
    230      * @param boolean $includeTitle
    231      *
    232      * @return string
    233      */
    234     function MetaTags($includeTitle = true) {
    235         $tags = $this->owner->data()->MetaTags($includeTitle);
    236         $width = self::$mobile_width;
    237         $canonicalUrl = self::protocol() . self::canonical_domain() .  $_SERVER['REQUEST_URI'];
    238         $mobileUrl = self::protocol() . self::mobile_domain() .  $_SERVER['REQUEST_URI'];
    239
    240         if (self::is_mobile_browser()) {
    241             if (self::is_mobile_domain()) {
    242                 $tags .= "<link rel=\"canonical\" href=\"{$canonicalUrl}\">\n";
    243                 if ((self::$auto_redirect == 'js' || self::$auto_redirect == 'auto') && !$this->isForceMain()) {
    244                     $tags .= "
    245                         <script language=\"JavaScript\">
    246                             function _getwidth() {
    247                                 var m = 9000, a = [screen.width, window.outerWidth, window.innerWidth, document.documentElement.clientWidth];
    248                                 a.forEach(function(w) {
    249                                     if (w > 0 && w < m) m = w;
    250                                 });
    251                                 return m;
    252                             }
    253                             var w = _getwidth();
    254                             if (w && (w > {$width})) {
    255                                 location.href = '$canonicalUrl';
    256                             }
    257                         </script>
    258                     ";
    259                 }
    260             }
    261             else { // desktop domain
    262                 $tags .= "<link rel=\"alternate\" media=\"only screen and (max-width: {$width}px)\" href=\"{$mobileUrl}\">\n";
    263                 if ((self::$auto_redirect == 'js' || self::$auto_redirect == 'auto') && !$this->isForceMain()) {
    264                     $tags .= "
    265                         <script language=\"JavaScript\">
    266                             function _getwidth() {
    267                                 var m = 9000, a = [screen.width, window.outerWidth, window.innerWidth, document.documentElement.clientWidth];
    268                                 a.forEach(function(w) {
    269                                     if (w > 0 && w < m) m = w;
    270                                 });
    271                                 return m;
    272                             }
    273                             var w = _getwidth();
    274                             if (w && (w <= {$width})) {
    275                                 location.href = '$mobileUrl';
    276                             }
    277                         </script>
    278                     ";
    279                 }
    280             }
    281         }
    282         else { // desktop browser
    283             if (self::is_mobile_domain()) {
    284                 $tags .= "<link rel=\"canonical\" href=\"{$canonicalUrl}\">\n";
    285                 if ((self::$auto_redirect == 'js' || self::$auto_redirect == 'auto') && !$this->isforceMobile()) {
    286                     $tags .= "
    287                         <script language=\"JavaScript\">
    288                             location.href = '$canonicalUrl';
    289                         </script>
    290                     ";
    291                 }
    292             }
    293         }
    294         return $tags;
    295     }
    296
    297     /**
    298      * Расширение Controller::init()
    299      * - Выставляет/снимает куки при ручном переходе на основную/мобильную версию
    300      * - Редиректит мобильные браузеры на мобильную верисю сайта если нет куки и self::$auto_redirect == 'http' || 'auto'
    301      * - выставляет тему для мобильной версии
    302      */
    303     function onAfterInit() {
    304         if (isset($_GET['_fv'])) {
    305             $fv = $_GET['_fv'];
    306             if ($fv == 'main') {
    307                 Cookie::set(self::$cookie_name, '1', self::$cookie_ttl, null, self::cookie_domain());
    308                 if (!Director::redirected_to())
    309                     Director::redirect(Director::absoluteUrl('/' . HTTP::setGetVar('_fv', null), true));
    310                 return;
    311             }
    312             if ($fv == 'mobile') {
    313                 // для удаления выставляем куку пустой!
    314                 Cookie::set(self::$cookie_name, '', self::$cookie_ttl, null, self::cookie_domain());
    315                 if (!Director::redirected_to())
    316                     Director::redirect(self::protocol() . self::mobile_domain() . Director::baseUrl() . Director::makeRelative('/' . HTTP::setGetVar('_fv', null)));
    317                 return;
    318             }
    319         }
    320         elseif ((self::$auto_redirect == 'http') && !Director::redirected_to()) {
    321             if (self::is_mobile_browser() && !self::is_mobile_domain() && !$this->isForceMain()) {
    322                 Director::redirect(self::protocol() . self::mobile_domain() .  $_SERVER['REQUEST_URI']);
    323                 return;
    324             }
    325             elseif (!self::is_mobile_browser() && self::is_mobile_domain() && !$this->isForceMobile()) {
    326                 Director::redirect(self::protocol() . self::canonical_domain() .  $_SERVER['REQUEST_URI']);
    327                 return;
    328             }
    329         }
    330
    331         $this->owner->isMobileDomain = self::is_mobile_domain();
    332         $this->owner->MobileWidth = self::$mobile_width;
    333
    334         if (self::is_mobile_domain()) {
    335             $this->owner->CanonicalUrl = self::protocol() . self::canonical_domain() .  $_SERVER['REQUEST_URI'];
    336             $this->owner->SwitchToCanonicalUrl = self::protocol() . self::canonical_domain() .  Director::baseUrl() . Director::makeRelative('/' . HTTP::setGetVar('_fv', 'main'));
    337         }
    338         else {
    339             $this->owner->MobileUrl = self::protocol() . self::mobile_domain() .  $_SERVER['REQUEST_URI'];
    340             // делаем на текущем домене чтобы можно было снять куку
    341             $this->owner->SwitchToMobilelUrl =  self::protocol() . self::current_domain() .  Director::baseUrl() . Director::makeRelative('/' . HTTP::setGetVar('_fv', 'mobile', '/'));
    342         }
    343     }
    344
    345     // Флаг того, что пользователь сам выбрал основную версию вместо мобильной
    346     function isForceMain() {
    347         if (Cookie::get(self::$cookie_name))
    348             return true;
    349         return false;
    350     }
    351
    352     function isForceMobile() {
    353         return false;
    354     }
    355 }

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -