]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/MobileProfile/MobileProfilePlugin.php
plugins/MobileProfile/MobileProfilePlugin.php: add 'maemo' to recognized devices
[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                     'maemo',
115                     'midp-',
116                     'mobile',
117                     'mot',
118                     'netfront',
119                     'nitro',
120                     'nokia',
121                     'opera mini',
122                     'palm',
123                     'palmsource',
124                     'panasonic',
125                     'philips',
126                     'pocketpc',
127                     'portalmmm',
128                     'rover',
129                     'samsung',
130                     'sanyo',
131                     'series60',
132                     'sharp',
133                     'sie-',
134                     'smartphone',
135                     'sony',
136                     'symbian',
137                     'up.browser',
138                     'up.link',
139                     'up.link',
140                     'vodafone',
141                     'wap1',
142                     'wap2',
143                     'webos',
144                     'windows ce'
145                 );
146
147                 $blacklist = array(
148                     'ipad', // Larger screen handles the full theme fairly well.
149                 );
150
151                 $httpuseragent = strtolower($_SERVER['HTTP_USER_AGENT']);
152
153                 foreach ($blacklist as $md) {
154                     if (strstr($httpuseragent, $md) !== false) {
155                         $this->serveMobile = false;
156                         return true;
157                     }
158                 }
159
160                 foreach ($this->mobiledevices as $md) {
161                     if (strstr($httpuseragent, $md) !== false) {
162                         $this->setMobileFeatures($httpuseragent);
163
164                         $this->serveMobile = true;
165                         $this->reallyMobile = true;
166                         break;
167                     }
168                 }
169             //}
170
171             // If they are okay with MP, and the site has a mobile server,
172             // redirect there
173             if ($this->serveMobile &&
174                 common_config('site', 'mobileserver') !== false &&
175                 (common_config('site', 'mobileserver') !=
176                     common_config('site', 'server'))) {
177
178                 // FIXME: Redirect to equivalent page on mobile site instead
179                 common_redirect($this->_common_path(''), 302);
180             }
181         }
182
183         if (!$this->serveMobile) {
184             return true;
185         }
186
187         // @fixme $type is undefined, making this if case useless and spewing errors.
188         // What's the intent?
189         //if (!$type) {
190             $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ?
191               $_SERVER['HTTP_ACCEPT'] : null;
192
193             $cp = common_accept_to_prefs($httpaccept);
194             $sp = common_accept_to_prefs(PAGE_TYPE_PREFS_MOBILEPROFILE);
195
196             $type = common_negotiate_type($cp, $sp);
197
198             if (!$type) {
199                 // TRANS: Client exception thrown when requesting a not supported media type.
200                 throw new ClientException(_m('This page is not available in a '.
201                                             'media type you accept.'), 406);
202             }
203         //}
204
205         header('Content-Type: '.$type);
206
207         if ($this->reallyMobile) {
208
209            $action->extraHeaders();
210            if (preg_match("/.*\/.*xml/", $type)) {
211                // Required for XML documents
212                $action->xw->startDocument('1.0', 'UTF-8');
213            }
214            $action->xw->writeDTD('html',
215                            '-//WAPFORUM//DTD XHTML Mobile 1.0//EN',
216                            $this->DTD);
217
218             $language = $action->getLanguage();
219
220             $action->elementStart('html', array('xmlns' => 'http://www.w3.org/1999/xhtml',
221                                             'xml:lang' => $language));
222
223             return false;
224
225         } else {
226         return true;
227         }
228
229     }
230
231     function setMobileFeatures($useragent)
232     {
233         $mobiledeviceInputFileType = array(
234             'nokia'
235         );
236
237         $this->mobileFeatures['inputfiletype'] = false;
238
239         foreach ($mobiledeviceInputFileType as $md) {
240             if (strstr($useragent, $md) !== false) {
241                 $this->mobileFeatures['inputfiletype'] = true;
242                 break;
243             }
244         }
245     }
246
247     function onStartShowStatusNetStyles($action)
248     {
249         if (!$this->serveMobile) {
250             return true;
251         }
252
253         $action->primaryCssLink();
254
255         $action->cssLink($this->path('mp-screen.css'),null,'screen');
256         if (file_exists(Theme::file('css/mp-screen.css'))) {
257             $action->cssLink('css/mp-screen.css', null, 'screen');
258         }
259
260         $action->cssLink($this->path('mp-handheld.css'),null,'handheld');
261         if (file_exists(Theme::file('css/mp-handheld.css'))) {
262             $action->cssLink('css/mp-handheld.css', null, 'handheld');
263         }
264
265         // Allow other plugins to load their styles.
266         Event::handle('EndShowStatusNetStyles', array($action));
267         Event::handle('EndShowLaconicaStyles', array($action));
268
269         return false;
270     }
271
272     function onStartShowUAStyles($action) {
273         if (!$this->serveMobile) {
274             return true;
275         }
276
277         return false;
278     }
279
280     function onStartShowHeader($action)
281     {
282         if (!$this->serveMobile) {
283             return true;
284         }
285
286         $action->elementStart('div', array('id' => 'header'));
287         $this->_showLogo($action);
288         $action->showPrimaryNav();
289         $action->elementEnd('div');
290
291         return false;
292     }
293
294     function _showLogo($action)
295     {
296         $action->elementStart('address', 'vcard');
297         if (common_config('singleuser', 'enabled')) {
298             $user = User::singleUser();
299             $url = common_local_url('showstream', array('nickname' => $user->nickname));
300         } else {
301             $url = common_local_url('public');
302         }
303
304         $action->elementStart('a', array('class' => 'url home bookmark',
305                                          'href' => $url));
306
307         if (common_config('site', 'mobilelogo') ||
308             file_exists(Theme::file('logo.png')) ||
309             file_exists(Theme::file('mobilelogo.png'))) {
310
311             $action->element('img', array('class' => 'photo',
312                 'src' => (common_config('site', 'mobilelogo')) ? common_config('site', 'mobilelogo') :
313                             ((file_exists(Theme::file('mobilelogo.png'))) ? (Theme::path('mobilelogo.png')) : Theme::path('logo.png')),
314                 'alt' => common_config('site', 'name')));
315         }
316         $action->element('span', array('class' => 'fn org'), common_config('site', 'name'));
317         $action->elementEnd('a');
318         $action->elementEnd('address');
319     }
320
321     function onStartShowAside($action)
322     {
323         if ($this->serveMobile) {
324             return false;
325         }
326     }
327
328     function onStartShowLocalNavBlock($action)
329     {
330         if ($this->serveMobile) {
331             // @todo FIXME: "Show Navigation" / "Hide Navigation" needs i18n
332             $action->element('a', array('href' => '#', 'id' => 'navtoggle'), 'Show Navigation');
333         return true;
334         }
335     }
336
337     function onEndShowScripts($action)
338     {
339         // @todo FIXME: "Show Navigation" / "Hide Navigation" needs i18n
340         $action->inlineScript('
341             $(function() {
342                 $("#mobile-toggle-disable").click(function() {
343                     $.cookie("MobileOverride", "0", {path: "/"});
344                     window.location.reload();
345                     return false;
346                 });
347                 $("#mobile-toggle-enable").click(function() {
348                     $.cookie("MobileOverride", "1", {path: "/"});
349                     window.location.reload();
350                     return false;
351                 });
352                 $("#navtoggle").click(function () {
353                           $("#site_nav_local_views").fadeToggle();
354                           var text = $("#navtoggle").text();
355                           $("#navtoggle").text(
356                           text == "Show Navigation" ? "Hide Navigation" : "Show Navigation");
357                 });
358             });'
359         );
360
361         if ($this->serveMobile) {
362             $action->inlineScript('
363                 $(function() {
364                         $(".checkbox-wrapper").unbind("click");
365                 });'
366             );
367         }
368
369
370     }
371
372
373     function onEndShowInsideFooter($action)
374     {
375         if ($this->serveMobile) {
376             // TRANS: Link to switch site layout from mobile to desktop mode. Appears at very bottom of page.
377             $linkText = _m('Switch to desktop site layout.');
378             $key = 'mobile-toggle-disable';
379         } else {
380             // TRANS: Link to switch site layout from desktop to mobile mode. Appears at very bottom of page.
381             $linkText = _m('Switch to mobile site layout.');
382             $key = 'mobile-toggle-enable';
383         }
384         $action->elementStart('p');
385         $action->element('a', array('href' => '#', 'id' => $key), $linkText);
386         $action->elementEnd('p');
387         return true;
388     }
389
390     function _common_path($relative, $ssl=false)
391     {
392         $pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';
393
394         if (($ssl && (common_config('site', 'ssl') === 'sometimes'))
395             || common_config('site', 'ssl') === 'always') {
396             $proto = 'https';
397             if (is_string(common_config('site', 'sslserver')) &&
398                 mb_strlen(common_config('site', 'sslserver')) > 0) {
399                 $serverpart = common_config('site', 'sslserver');
400             } else {
401                 $serverpart = common_config('site', 'mobileserver');
402             }
403         } else {
404             $proto      = 'http';
405             $serverpart = common_config('site', 'mobileserver');
406         }
407
408         return $proto.'://'.$serverpart.'/'.$pathpart.$relative;
409     }
410
411     function onPluginVersion(&$versions)
412     {
413         $versions[] = array('name' => 'MobileProfile',
414                             'version' => STATUSNET_VERSION,
415                             'author' => 'Sarven Capadisli',
416                             'homepage' => 'http://status.net/wiki/Plugin:MobileProfile',
417                             'rawdescription' =>
418                             // TRANS: Plugin description.
419                             _m('XHTML MobileProfile output for supporting user agents.'));
420         return true;
421     }
422 }