]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/MobileProfile/MobileProfilePlugin.php
Merge branch 'master' into testing
[quix0rs-gnu-social.git] / plugins / MobileProfile / MobileProfilePlugin.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * XHTML Mobile Profile plugin that uses WAP 2.0 Plugin
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Plugin
23  * @package   StatusNet
24  * @author    Sarven Capadisli <csarven@status.net>
25  * @copyright 2009 StatusNet, Inc.
26  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27  * @link      http://status.net/
28  */
29
30 if (!defined('STATUSNET') && !defined('LACONICA')) {
31     exit(1);
32 }
33
34 define('PAGE_TYPE_PREFS_MOBILEPROFILE',
35        'application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html;q=0.9');
36
37 require_once INSTALLDIR.'/plugins/Mobile/WAP20Plugin.php';
38
39 /**
40  * Superclass for plugin to output XHTML Mobile Profile
41  *
42  * @category Plugin
43  * @package  StatusNet
44  * @author   Sarven Capadisli <csarven@status.net>
45  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
46  * @link     http://status.net/
47  */
48 class MobileProfilePlugin extends WAP20Plugin
49 {
50     public $DTD            = null;
51     public $serveMobile    = false;
52     public $reallyMobile   = false;
53     public $mobileFeatures = array();
54
55     function __construct($DTD='http://www.wapforum.org/DTD/xhtml-mobile10.dtd')
56     {
57         $this->DTD = $DTD;
58
59         parent::__construct();
60     }
61
62     function onStartShowHTML($action)
63     {
64         // XXX: This should probably graduate to WAP20Plugin
65
66         // If they are on the mobile site, serve them MP
67         if ((common_config('site', 'mobileserver').'/'.
68              common_config('site', 'path').'/' ==
69             $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'])) {
70
71             $this->serveMobile = true;
72         } else if (isset($_COOKIE['MobileOverride'])) {
73             // Cookie override is controlled by link at bottom.
74             $this->serveMobile = (bool)$_COOKIE['MobileOverride'];
75         } else {
76             // If they like the WAP 2.0 mimetype, serve them MP
77             // @fixme $type is undefined, making this if case useless and spewing errors.
78             // What's the intent?
79             //if (strstr('application/vnd.wap.xhtml+xml', $type) !== false) {
80             //    $this->serveMobile = true;
81             //} else {
82                 // If they are a mobile device that supports WAP 2.0,
83                 // serve them MP
84
85                 // XXX: Browser sniffing sucks
86
87                 // I really don't like going through this every page,
88                 // perhaps use $_SESSION or cookies
89
90                 // May be better to group the devices in terms of
91                 // low,mid,high-end
92
93                 // Or, detect the mobile devices based on their support for
94                 // MP 1.0, 1.1, or 1.2 may be ideal. Possible?
95
96                 $this->mobiledevices = array(
97                     'alcatel',
98                     'android',
99                     'audiovox',
100                     'au-mic,',
101                     'avantgo',
102                     'blackberry',
103                     'blazer',
104                     'cldc-',
105                     'danger',
106                     'epoc',
107                     'ericsson',
108                     'ericy',
109                     'iphone',
110                     'ipaq',
111                     'ipod',
112                     'j2me',
113                     'lg',
114                     'midp-',
115                     'mobile',
116                     'mot',
117                     'netfront',
118                     'nitro',
119                     'nokia',
120                     'opera mini',
121                     'palm',
122                     'palmsource',
123                     'panasonic',
124                     'philips',
125                     'pocketpc',
126                     'portalmmm',
127                     'rover',
128                     'samsung',
129                     'sanyo',
130                     'series60',
131                     'sharp',
132                     'sie-',
133                     'smartphone',
134                     'sony',
135                     'symbian',
136                     'up.browser',
137                     'up.link',
138                     'up.link',
139                     'vodafone',
140                     'wap1',
141                     'wap2',
142                     'webos',
143                     'windows ce'
144                 );
145
146                 $blacklist = array(
147                     'ipad', // Larger screen handles the full theme fairly well.
148                 );
149
150                 $httpuseragent = strtolower($_SERVER['HTTP_USER_AGENT']);
151
152                 foreach ($blacklist as $md) {
153                     if (strstr($httpuseragent, $md) !== false) {
154                         $this->serveMobile = false;
155                         return true;
156                     }
157                 }
158
159                 foreach ($this->mobiledevices as $md) {
160                     if (strstr($httpuseragent, $md) !== false) {
161                         $this->setMobileFeatures($httpuseragent);
162
163                         $this->serveMobile = true;
164                         $this->reallyMobile = true;
165                         break;
166                     }
167                 }
168             //}
169
170             // If they are okay with MP, and the site has a mobile server,
171             // redirect there
172             if ($this->serveMobile &&
173                 common_config('site', 'mobileserver') !== false &&
174                 (common_config('site', 'mobileserver') !=
175                     common_config('site', 'server'))) {
176
177                 // FIXME: Redirect to equivalent page on mobile site instead
178                 common_redirect($this->_common_path(''), 302);
179             }
180         }
181
182         if (!$this->serveMobile) {
183             return true;
184         }
185
186         // @fixme $type is undefined, making this if case useless and spewing errors.
187         // What's the intent?
188         //if (!$type) {
189             $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ?
190               $_SERVER['HTTP_ACCEPT'] : null;
191
192             $cp = common_accept_to_prefs($httpaccept);
193             $sp = common_accept_to_prefs(PAGE_TYPE_PREFS_MOBILEPROFILE);
194
195             $type = common_negotiate_type($cp, $sp);
196
197             if (!$type) {
198                 // TRANS: Client exception thrown when requesting a not supported media type.
199                 throw new ClientException(_m('This page is not available in a '.
200                                             'media type you accept.'), 406);
201             }
202         //}
203
204         header('Content-Type: '.$type);
205
206         if ($this->reallyMobile) {
207
208            $action->extraHeaders();
209            if (preg_match("/.*\/.*xml/", $type)) {
210                // Required for XML documents
211                $action->xw->startDocument('1.0', 'UTF-8');
212            }
213            $action->xw->writeDTD('html',
214                            '-//WAPFORUM//DTD XHTML Mobile 1.0//EN',
215                            $this->DTD);
216
217             $language = $action->getLanguage();
218
219             $action->elementStart('html', array('xmlns' => 'http://www.w3.org/1999/xhtml',
220                                             'xml:lang' => $language));
221
222             return false;
223
224         } else {
225         return true;
226         }
227
228     }
229
230     function setMobileFeatures($useragent)
231     {
232         $mobiledeviceInputFileType = array(
233             'nokia'
234         );
235
236         $this->mobileFeatures['inputfiletype'] = false;
237
238         foreach ($mobiledeviceInputFileType as $md) {
239             if (strstr($useragent, $md) !== false) {
240                 $this->mobileFeatures['inputfiletype'] = true;
241                 break;
242             }
243         }
244     }
245
246     function onStartShowStatusNetStyles($action)
247     {
248         if (!$this->serveMobile) {
249             return true;
250         }
251
252         $action->primaryCssLink();
253
254         if (file_exists(Theme::file('css/mp-screen.css'))) {
255             $action->cssLink('css/mp-screen.css', null, 'screen');
256         } else {
257             $action->cssLink($this->path('mp-screen.css'),null,'screen');
258         }
259
260         if (file_exists(Theme::file('css/mp-handheld.css'))) {
261             $action->cssLink('css/mp-handheld.css', null, 'handheld');
262         } else {
263             $action->cssLink($this->path('mp-handheld.css'),null,'handheld');
264         }
265
266         // Allow other plugins to load their styles.
267         Event::handle('EndShowStatusNetStyles', array($action));
268         Event::handle('EndShowLaconicaStyles', array($action));
269
270         return false;
271     }
272
273     function onStartShowUAStyles($action) {
274         if (!$this->serveMobile) {
275             return true;
276         }
277
278         return false;
279     }
280
281     function onStartShowHeader($action)
282     {
283         if (!$this->serveMobile) {
284             return true;
285         }
286
287         $action->elementStart('div', array('id' => 'header'));
288         $this->_showLogo($action);
289         $action->showPrimaryNav();
290         $action->elementEnd('div');
291
292         return false;
293     }
294
295     function _showLogo($action)
296     {
297         $action->elementStart('address', 'vcard');
298         $action->elementStart('a', array('class' => 'url home bookmark',
299                                        'href' => common_local_url('public')));
300         if (common_config('site', 'mobilelogo') ||
301             file_exists(Theme::file('logo.png')) ||
302             file_exists(Theme::file('mobilelogo.png'))) {
303
304             $action->element('img', array('class' => 'photo',
305                 'src' => (common_config('site', 'mobilelogo')) ? common_config('site', 'mobilelogo') :
306                             ((file_exists(Theme::file('mobilelogo.png'))) ? (Theme::path('mobilelogo.png')) : Theme::path('logo.png')),
307                 'alt' => common_config('site', 'name')));
308         }
309         $action->element('span', array('class' => 'fn org'), common_config('site', 'name'));
310         $action->elementEnd('a');
311         $action->elementEnd('address');
312     }
313
314     function onStartShowAside($action)
315     {
316         if ($this->serveMobile) {
317             return false;
318         }
319     }
320
321     function onStartShowLocalNavBlock($action)
322     {
323         if ($this->serveMobile) {
324             // @todo FIXME: "Show Navigation" / "Hide Navigation" needs i18n
325             $action->element('a', array('href' => '#', 'id' => 'navtoggle'), 'Show Navigation');
326         return true;
327         }
328     }
329
330     function onEndShowScripts($action)
331     {
332         // @todo FIXME: "Show Navigation" / "Hide Navigation" needs i18n
333         $action->inlineScript('
334             $(function() {
335                 $("#mobile-toggle-disable").click(function() {
336                     $.cookie("MobileOverride", "0", {path: "/"});
337                     window.location.reload();
338                     return false;
339                 });
340                 $("#mobile-toggle-enable").click(function() {
341                     $.cookie("MobileOverride", "1", {path: "/"});
342                     window.location.reload();
343                     return false;
344                 });
345                 $("#navtoggle").click(function () {
346                           $("#site_nav_local_views").fadeToggle();
347                           var text = $("#navtoggle").text();
348                           $("#navtoggle").text(
349                           text == "Show Navigation" ? "Hide Navigation" : "Show Navigation");
350                 });
351             });'
352         );
353
354         if ($this->serveMobile) {
355             $action->inlineScript('
356                 $(function() {
357                         $(".checkbox-wrapper").unbind("click");
358                 });'
359             );
360         }
361
362
363     }
364
365
366     function onEndShowInsideFooter($action)
367     {
368         if ($this->serveMobile) {
369             // TRANS: Link to switch site layout from mobile to desktop mode. Appears at very bottom of page.
370             $linkText = _m('Switch to desktop site layout.');
371             $key = 'mobile-toggle-disable';
372         } else {
373             // TRANS: Link to switch site layout from desktop to mobile mode. Appears at very bottom of page.
374             $linkText = _m('Switch to mobile site layout.');
375             $key = 'mobile-toggle-enable';
376         }
377         $action->elementStart('p');
378         $action->element('a', array('href' => '#', 'id' => $key), $linkText);
379         $action->elementEnd('p');
380         return true;
381     }
382
383     function _common_path($relative, $ssl=false)
384     {
385         $pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
386
387         if (($ssl && (common_config('site', 'ssl') === 'sometimes'))
388             || common_config('site', 'ssl') === 'always') {
389             $proto = 'https';
390             if (is_string(common_config('site', 'sslserver')) &&
391                 mb_strlen(common_config('site', 'sslserver')) > 0) {
392                 $serverpart = common_config('site', 'sslserver');
393             } else {
394                 $serverpart = common_config('site', 'mobileserver');
395             }
396         } else {
397             $proto      = 'http';
398             $serverpart = common_config('site', 'mobileserver');
399         }
400
401         return $proto.'://'.$serverpart.'/'.$pathpart.$relative;
402     }
403
404     function onPluginVersion(&$versions)
405     {
406         $versions[] = array('name' => 'MobileProfile',
407                             'version' => STATUSNET_VERSION,
408                             'author' => 'Sarven Capadisli',
409                             'homepage' => 'http://status.net/wiki/Plugin:MobileProfile',
410                             'rawdescription' =>
411                             // TRANS: Plugin description.
412                             _m('XHTML MobileProfile output for supporting user agents.'));
413         return true;
414     }
415 }