We are no longer offering accounts on this server. Consider https://gitlab.freedesktop.org/ as a place to host projects.

MobileProfilePlugin.php 15.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * XHTML Mobile Profile plugin that uses WAP 2.0 Plugin
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Sarven Capadisli <csarven@status.net>
 * @copyright 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

34
define('PAGE_TYPE_PREFS_MOBILEPROFILE',
35
       'application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html;q=0.9');
36 37 38 39 40 41 42 43 44 45 46 47 48 49

require_once INSTALLDIR.'/plugins/Mobile/WAP20Plugin.php';

/**
 * Superclass for plugin to output XHTML Mobile Profile
 *
 * @category Plugin
 * @package  StatusNet
 * @author   Sarven Capadisli <csarven@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class MobileProfilePlugin extends WAP20Plugin
{
50 51 52
    public $DTD            = null;
    public $serveMobile    = false;
    public $mobileFeatures = array();
53 54 55

    function __construct($DTD='http://www.wapforum.org/DTD/xhtml-mobile10.dtd')
    {
56
        $this->DTD = $DTD;
57 58 59 60 61 62

        parent::__construct();
    }

    function onStartShowHTML($action)
    {
63
        // XXX: This should probably graduate to WAP20Plugin
64

65 66
        // If they are on the mobile site, serve them MP
        if ((common_config('site', 'mobileserver').'/'.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
67
             common_config('site', 'path').'/' ==
68
            $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'])) {
69 70

            $this->serveMobile = true;
71 72 73
        } else if (isset($_COOKIE['MobileOverride'])) {
            // Cookie override is controlled by link at bottom.
            $this->serveMobile = (bool)$_COOKIE['MobileOverride'];
74
        } else {
75
            // If they like the WAP 2.0 mimetype, serve them MP
76 77 78 79 80
            // @fixme $type is undefined, making this if case useless and spewing errors.
            // What's the intent?
            //if (strstr('application/vnd.wap.xhtml+xml', $type) !== false) {
            //    $this->serveMobile = true;
            //} else {
Siebrand Mazeland's avatar
Siebrand Mazeland committed
81
                // If they are a mobile device that supports WAP 2.0,
82 83 84
                // serve them MP

                // XXX: Browser sniffing sucks
85

Siebrand Mazeland's avatar
Siebrand Mazeland committed
86
                // I really don't like going through this every page,
Sarven Capadisli's avatar
Sarven Capadisli committed
87
                // perhaps use $_SESSION or cookies
88

Siebrand Mazeland's avatar
Siebrand Mazeland committed
89
                // May be better to group the devices in terms of
90 91
                // low,mid,high-end

Siebrand Mazeland's avatar
Siebrand Mazeland committed
92
                // Or, detect the mobile devices based on their support for
93 94
                // MP 1.0, 1.1, or 1.2 may be ideal. Possible?

95
                $this->mobiledevices = array(
Sarven Capadisli's avatar
Sarven Capadisli committed
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
                    'alcatel',
                    'android',
                    'audiovox',
                    'au-mic,',
                    'avantgo',
                    'blackberry',
                    'blazer',
                    'cldc-',
                    'danger',
                    'epoc',
                    'ericsson',
                    'ericy',
                    'iphone',
                    'ipaq',
                    'ipod',
                    'j2me',
                    'lg',
                    'midp-',
                    'mobile',
                    'mot',
                    'netfront',
                    'nitro',
                    'nokia',
                    'opera mini',
                    'palm',
                    'palmsource',
                    'panasonic',
                    'philips',
                    'pocketpc',
                    'portalmmm',
                    'rover',
                    'samsung',
                    'sanyo',
                    'series60',
                    'sharp',
                    'sie-',
                    'smartphone',
                    'sony',
                    'symbian',
                    'up.browser',
                    'up.link',
                    'up.link',
                    'vodafone',
                    'wap1',
                    'wap2',
141
                    'webos',
Sarven Capadisli's avatar
Sarven Capadisli committed
142 143
                    'windows ce'
                );
144

145 146 147 148
                $blacklist = array(
                    'ipad', // Larger screen handles the full theme fairly well.
                );

149 150
                $httpuseragent = strtolower($_SERVER['HTTP_USER_AGENT']);

151 152 153 154 155 156 157
                foreach ($blacklist as $md) {
                    if (strstr($httpuseragent, $md) !== false) {
                        $this->serveMobile = false;
                        return true;
                    }
                }

158
                foreach ($this->mobiledevices as $md) {
159
                    if (strstr($httpuseragent, $md) !== false) {
Sarven Capadisli's avatar
Sarven Capadisli committed
160
                        $this->setMobileFeatures($httpuseragent);
161

162 163 164 165
                        $this->serveMobile = true;
                        break;
                    }
                }
166
            //}
167

Siebrand Mazeland's avatar
Siebrand Mazeland committed
168
            // If they are okay with MP, and the site has a mobile server,
169
            // redirect there
Siebrand Mazeland's avatar
Siebrand Mazeland committed
170
            if ($this->serveMobile &&
171
                common_config('site', 'mobileserver') !== false &&
Siebrand Mazeland's avatar
Siebrand Mazeland committed
172
                (common_config('site', 'mobileserver') !=
173
                    common_config('site', 'server'))) {
174

175
                // FIXME: Redirect to equivalent page on mobile site instead
Sarven Capadisli's avatar
Sarven Capadisli committed
176
                common_redirect($this->_common_path(''), 302);
177
            }
178 179
        }

180 181 182 183
        if (!$this->serveMobile) {
            return true;
        }

184 185 186
        // @fixme $type is undefined, making this if case useless and spewing errors.
        // What's the intent?
        //if (!$type) {
187 188 189 190
            $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ?
              $_SERVER['HTTP_ACCEPT'] : null;

            $cp = common_accept_to_prefs($httpaccept);
191
            $sp = common_accept_to_prefs(PAGE_TYPE_PREFS_MOBILEPROFILE);
192

193
            $type = common_negotiate_type($cp, $sp);
194

195
            if (!$type) {
Siebrand Mazeland's avatar
Siebrand Mazeland committed
196 197
                throw new ClientException(_m('This page is not available in a '.
                                            'media type you accept.'), 406);
198
            }
199
        //}
200 201 202

        header('Content-Type: '.$type);

203
        $action->extraHeaders();
204 205
        if (preg_match("/.*\/.*xml/", $type)) {
            // Required for XML documents
206
            $action->xw->startDocument('1.0', 'UTF-8');
207
        }
208
        $action->xw->writeDTD('html',
209 210 211 212 213 214 215 216 217 218 219
                        '-//WAPFORUM//DTD XHTML Mobile 1.0//EN',
                        $this->DTD);

        $language = $action->getLanguage();

        $action->elementStart('html', array('xmlns' => 'http://www.w3.org/1999/xhtml',
                                            'xml:lang' => $language));

        return false;
    }

220 221 222 223 224 225 226 227
    function setMobileFeatures($useragent)
    {
        $mobiledeviceInputFileType = array(
            'nokia'
        );

        $this->mobileFeatures['inputfiletype'] = false;

228
        foreach ($mobiledeviceInputFileType as $md) {
229 230 231 232 233 234 235
            if (strstr($useragent, $md) !== false) {
                $this->mobileFeatures['inputfiletype'] = true;
                break;
            }
        }
    }

236 237
    function onStartShowStatusNetStyles($action)
    {
Sarven Capadisli's avatar
Sarven Capadisli committed
238
        if (!$this->serveMobile) {
239 240 241
            return true;
        }

242
        $action->primaryCssLink();
243

244
        if (file_exists(Theme::file('css/mp-screen.css'))) {
245
            $action->cssLink('css/mp-screen.css', null, 'screen');
246
        } else {
247
            $action->cssLink($this->path('mp-screen.css'),null,'screen');
248 249
        }

250
        if (file_exists(Theme::file('css/mp-handheld.css'))) {
251
            $action->cssLink('css/mp-handheld.css', null, 'handheld');
252
        } else {
253
            $action->cssLink($this->path('mp-handheld.css'),null,'handheld');
254 255
        }

256 257 258 259
        // Allow other plugins to load their styles.
        Event::handle('EndShowStatusNetStyles', array($action));
        Event::handle('EndShowLaconicaStyles', array($action));

260 261 262
        return false;
    }

263 264 265 266 267 268 269 270
    function onStartShowUAStyles($action) {
        if (!$this->serveMobile) {
            return true;
        }

        return false;
    }

Sarven Capadisli's avatar
Sarven Capadisli committed
271 272 273 274 275 276 277
    function onStartShowHeader($action)
    {
        if (!$this->serveMobile) {
            return true;
        }

        $action->elementStart('div', array('id' => 'header'));
Sarven Capadisli's avatar
Sarven Capadisli committed
278
        $this->_showLogo($action);
Sarven Capadisli's avatar
Sarven Capadisli committed
279
        $this->_showPrimaryNav($action);
Sarven Capadisli's avatar
Sarven Capadisli committed
280 281 282 283
        if (common_logged_in()) {
            $action->showNoticeForm();
        }
        $action->elementEnd('div');
284 285

        return false;
Sarven Capadisli's avatar
Sarven Capadisli committed
286 287
    }

Sarven Capadisli's avatar
Sarven Capadisli committed
288 289 290 291 292
    function _showLogo($action)
    {
        $action->elementStart('address', 'vcard');
        $action->elementStart('a', array('class' => 'url home bookmark',
                                       'href' => common_local_url('public')));
Siebrand Mazeland's avatar
Siebrand Mazeland committed
293 294
        if (common_config('site', 'mobilelogo') ||
            file_exists(Theme::file('logo.png')) ||
Sarven Capadisli's avatar
Sarven Capadisli committed
295
            file_exists(Theme::file('mobilelogo.png'))) {
Sarven Capadisli's avatar
Sarven Capadisli committed
296 297

            $action->element('img', array('class' => 'photo',
Siebrand Mazeland's avatar
Siebrand Mazeland committed
298
                'src' => (common_config('site', 'mobilelogo')) ? common_config('site', 'mobilelogo') :
299
                            ((file_exists(Theme::file('mobilelogo.png'))) ? (Theme::path('mobilelogo.png')) : Theme::path('logo.png')),
Sarven Capadisli's avatar
Sarven Capadisli committed
300 301 302 303 304 305 306
                'alt' => common_config('site', 'name')));
        }
        $action->element('span', array('class' => 'fn org'), common_config('site', 'name'));
        $action->elementEnd('a');
        $action->elementEnd('address');
    }

307 308 309
    function _showPrimaryNav($action)
    {
        $user    = common_current_user();
Sarven Capadisli's avatar
Sarven Capadisli committed
310 311 312
        $action->elementStart('ul', array('id' => 'site_nav_global_primary'));
        if ($user) {
            $action->menuItem(common_local_url('all', array('nickname' => $user->nickname)),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
313
                            _m('Home'));
Sarven Capadisli's avatar
Sarven Capadisli committed
314
            $action->menuItem(common_local_url('profilesettings'),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
315
                            _m('Account'));
316
            $action->menuItem(common_local_url('oauthconnectionssettings'),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
317
                                _m('Connect'));
318 319
            if ($user->hasRight(Right::CONFIGURESITE)) {
                $action->menuItem(common_local_url('siteadminpanel'),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
320
                                _m('Admin'), _m('Change site configuration'), false, 'nav_admin');
321
            }
Sarven Capadisli's avatar
Sarven Capadisli committed
322 323
            if (common_config('invite', 'enabled')) {
                $action->menuItem(common_local_url('invite'),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
324
                                _m('Invite'));
Sarven Capadisli's avatar
Sarven Capadisli committed
325 326
            }
            $action->menuItem(common_local_url('logout'),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
327
                            _m('Logout'));
328
        } else {
Sarven Capadisli's avatar
Sarven Capadisli committed
329 330
            if (!common_config('site', 'closed')) {
                $action->menuItem(common_local_url('register'),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
331
                                _m('Register'));
Sarven Capadisli's avatar
Sarven Capadisli committed
332 333
            }
            $action->menuItem(common_local_url('login'),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
334
                            _m('Login'));
Sarven Capadisli's avatar
Sarven Capadisli committed
335 336 337
        }
        if ($user || !common_config('site', 'private')) {
            $action->menuItem(common_local_url('peoplesearch'),
Siebrand Mazeland's avatar
Siebrand Mazeland committed
338
                            _m('Search'));
Sarven Capadisli's avatar
Sarven Capadisli committed
339 340 341 342
        }
        $action->elementEnd('ul');
    }

Sarven Capadisli's avatar
Sarven Capadisli committed
343 344 345 346 347 348 349
    function onStartShowNoticeFormData($form)
    {
        if (!$this->serveMobile) {
            return true;
        }

        $form->out->element('textarea', array('id' => 'notice_data-text',
350
                                              'cols' => 15,
Sarven Capadisli's avatar
Sarven Capadisli committed
351 352 353 354 355 356 357
                                              'rows' => 4,
                                              'name' => 'status_textarea'),
                            ($form->content) ? $form->content : '');

        $contentLimit = Notice::maxContent();

        if ($contentLimit > 0) {
358
            $form->out->element('div', array('class' => 'count'),
Sarven Capadisli's avatar
Sarven Capadisli committed
359 360 361 362
                                $contentLimit);
        }

        if (common_config('attachments', 'uploads')) {
363
            if ($this->mobileFeatures['inputfiletype']) {
364
                $form->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
Siebrand Mazeland's avatar
Siebrand Mazeland committed
365
                $form->out->element('label', array('for' => 'notice_data-attach'), _m('Attach'));
366 367 368
                $form->out->element('input', array('id' => 'notice_data-attach',
                                                   'type' => 'file',
                                                   'name' => 'attach',
Siebrand Mazeland's avatar
Siebrand Mazeland committed
369
                                                   'title' => _m('Attach a file')));
370
            }
Sarven Capadisli's avatar
Sarven Capadisli committed
371 372 373 374 375 376 377 378 379
        }
        if ($form->action) {
            $form->out->hidden('notice_return-to', $form->action, 'returnto');
        }
        $form->out->hidden('notice_in-reply-to', $form->inreplyto, 'inreplyto');

        return false;
    }

380 381
    function onStartShowAside($action)
    {
Sarven Capadisli's avatar
Sarven Capadisli committed
382 383 384
        if ($this->serveMobile) {
            return false;
        }
385 386
    }

387
    function onEndShowScripts($action)
388
    {
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
        $action->inlineScript('
            $(function() {
                $("#mobile-toggle-disable").click(function() {
                    $.cookie("MobileOverride", "0", {path: "/"});
                    window.location.reload();
                    return false;
                });
                $("#mobile-toggle-enable").click(function() {
                    $.cookie("MobileOverride", "1", {path: "/"});
                    window.location.reload();
                    return false;
                });
            });'
        );
    }
404

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420

    function onEndShowInsideFooter($action)
    {
        if ($this->serveMobile) {
            // TRANS: Link to switch site layout from mobile to desktop mode. Appears at very bottom of page.
            $linkText = _m('Switch to desktop site layout.');
            $key = 'mobile-toggle-disable';
        } else {
            // TRANS: Link to switch site layout from desktop to mobile mode. Appears at very bottom of page.
            $linkText = _m('Switch to mobile site layout.');
            $key = 'mobile-toggle-enable';
        }
        $action->elementStart('p');
        $action->element('a', array('href' => '#', 'id' => $key), $linkText);
        $action->elementEnd('p');
        return true;
421 422
    }

423 424 425 426 427 428 429 430 431 432 433 434 435 436
    function _common_path($relative, $ssl=false)
    {
        $pathpart = (common_config('site', 'path')) ? common_config('site', 'path')."/" : '';

        if (($ssl && (common_config('site', 'ssl') === 'sometimes'))
            || common_config('site', 'ssl') === 'always') {
            $proto = 'https';
            if (is_string(common_config('site', 'sslserver')) &&
                mb_strlen(common_config('site', 'sslserver')) > 0) {
                $serverpart = common_config('site', 'sslserver');
            } else {
                $serverpart = common_config('site', 'mobileserver');
            }
        } else {
437
            $proto      = 'http';
438 439 440 441 442
            $serverpart = common_config('site', 'mobileserver');
        }

        return $proto.'://'.$serverpart.'/'.$pathpart.$relative;
    }
443

444 445 446 447 448 449 450 451 452 453 454
    function onPluginVersion(&$versions)
    {
        $versions[] = array('name' => 'MobileProfile',
                            'version' => STATUSNET_VERSION,
                            'author' => 'Sarven Capadisli',
                            'homepage' => 'http://status.net/wiki/Plugin:MobileProfile',
                            'rawdescription' =>
                            _m('XHTML MobileProfile output for supporting user agents.'));
        return true;
    }
}