router.php 47.6 KB
Newer Older
1 2
<?php
/**
3
 * StatusNet, the distributed open-source microblogging tool
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * URL routing utilities
 *
 * 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  URL
23
 * @package   StatusNet
24
 * @author    Evan Prodromou <evan@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('GNUSOCIAL')) { exit(1); }
31 32 33 34 35 36 37

/**
 * URL Router
 *
 * Cheap wrapper around Net_URL_Mapper
 *
 * @category URL
38
 * @package  StatusNet
39
 * @author   Evan Prodromou <evan@status.net>
40
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
41
 * @link     http://status.net/
42 43 44
 */
class Router
{
45
    var $m = null;
Evan Prodromou's avatar
Evan Prodromou committed
46 47
    static $inst = null;

48 49
    const REGEX_TAG = '[^\/]+'; // [\pL\pN_\-\.]{1,64} better if we can do unicode regexes

Evan Prodromou's avatar
Evan Prodromou committed
50 51 52 53 54 55 56
    static function get()
    {
        if (!Router::$inst) {
            Router::$inst = new Router();
        }
        return Router::$inst;
    }
57

58 59 60 61 62 63 64 65 66
    /**
     * Clear the global singleton instance for this class.
     * Needed to ensure reset when switching site configurations.
     */
    static function clear()
    {
        Router::$inst = null;
    }

67 68
    function __construct()
    {
69
        if (empty($this->m)) {
70
            $this->m = $this->initialize();
71 72 73
        }
    }

74 75
    /**
     * Create a unique hashkey for the router.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
76
     *
77 78 79 80
     * The router's url map can change based on the version of the software
     * you're running and the plugins that are enabled. To avoid having bad routes
     * get stuck in the cache, the key includes a list of plugins and the software
     * version.
81 82
     * 
    * There can still be problems with a) differences in versions of the plugins and
83 84
     * b) people running code between official versions, but these tend to be more
     * sophisticated users who can grok what's going on and clear their caches.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
85
     *
86 87
     * @return string cache key string that should uniquely identify a router
     */
Siebrand Mazeland's avatar
Siebrand Mazeland committed
88

89 90
    static function cacheKey()
    {
91 92 93 94 95 96 97 98 99 100
        $parts = array('router');

        // Many router paths depend on this setting.
        if (common_config('singleuser', 'enabled')) {
            $parts[] = '1user';
        } else {
            $parts[] = 'multi';
        }

        return Cache::codeKey(implode(':', $parts));
101
    }
Siebrand Mazeland's avatar
Siebrand Mazeland committed
102

103 104
    function initialize()
    {
105
        $m = new URLMapper();
106

107
        if (Event::handle('StartInitializeRouter', array(&$m))) {
108

109 110 111 112 113
            // top of the menu hierarchy, sometimes "Home"
            $m->connect('', array('action' => 'top'));

            // public endpoints

114 115
            $m->connect('robots.txt', array('action' => 'robotstxt'));

116 117 118 119
            $m->connect('opensearch/people', array('action' => 'opensearch',
                                                   'type' => 'people'));
            $m->connect('opensearch/notice', array('action' => 'opensearch',
                                                   'type' => 'notice'));
120

121
            // docs
122

123
            $m->connect('doc/:title', array('action' => 'doc'));
124

125 126 127 128
            $m->connect('main/otp/:user_id/:token',
                        array('action' => 'otp'),
                        array('user_id' => '[0-9]+',
                              'token' => '.+'));
129

130 131 132 133 134 135 136 137 138 139 140 141 142 143
            // these take a code; before the main part

            foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) {
                $m->connect('main/'.$c.'/:code', array('action' => $c));
            }

            // Also need a block variant accepting ID on URL for mail links
            $m->connect('main/block/:profileid',
                        array('action' => 'block'),
                        array('profileid' => '[0-9]+'));

            $m->connect('main/sup/:seconds', array('action' => 'sup'),
                        array('seconds' => '[0-9]+'));

144
            // main stuff is repetitive
145

146
            $main = array('login', 'logout', 'register', 'subscribe',
147
                          'unsubscribe', 'cancelsubscription', 'approvesub',
148
                          'confirmaddress', 'recoverpassword',
149
                          'invite', 'sup',
150
                          'block', 'unblock', 'subedit',
151 152 153
                          'groupblock', 'groupunblock',
                          'sandbox', 'unsandbox',
                          'silence', 'unsilence',
154
                          'grantrole', 'revokerole',
155
                          'deleteuser',
156 157
                          'geocode',
                          'version',
158
                          'backupaccount',
159
                          'deleteaccount',
160
                          'restoreaccount',
161
                          'top',
162
                          'public',
163
            );
164

165 166 167
            foreach ($main as $a) {
                $m->connect('main/'.$a, array('action' => $a));
            }
Evan Prodromou's avatar
Evan Prodromou committed
168

169 170
            $m->connect('main/all', array('action' => 'networkpublic'));

Shashi Gowda's avatar
Shashi Gowda committed
171 172
            $m->connect('main/tagprofile/:id', array('action' => 'tagprofile'),
                                               array('id' => '[0-9]+'));
173

174 175
            $m->connect('main/tagprofile', array('action' => 'tagprofile'));

176 177 178
            $m->connect('main/xrds',
                        array('action' => 'publicxrds'));

179 180
            // settings

181
            foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections',
182
                           'oauthapps', 'email', 'sms', 'url') as $s) {
183 184
                $m->connect('settings/'.$s, array('action' => $s.'settings'));
            }
185

186 187 188 189
            if (common_config('oldschool', 'enabled')) {
                $m->connect('settings/oldschool', array('action' => 'oldschoolsettings'));
            }

Evan Prodromou's avatar
Evan Prodromou committed
190
            $m->connect('settings/oauthapps/show/:id',
191 192
                        array('action' => 'showapplication'),
                        array('id' => '[0-9]+')
Evan Prodromou's avatar
Evan Prodromou committed
193 194
            );
            $m->connect('settings/oauthapps/new',
195
                        array('action' => 'newapplication')
Evan Prodromou's avatar
Evan Prodromou committed
196 197
            );
            $m->connect('settings/oauthapps/edit/:id',
198 199
                        array('action' => 'editapplication'),
                        array('id' => '[0-9]+')
Evan Prodromou's avatar
Evan Prodromou committed
200
            );
201
            $m->connect('settings/oauthapps/delete/:id',
202 203
                        array('action' => 'deleteapplication'),
                        array('id' => '[0-9]+')
204
            );
Evan Prodromou's avatar
Evan Prodromou committed
205

206
            // search
207

208 209 210 211
            foreach (array('group', 'people', 'notice') as $s) {
                $m->connect('search/'.$s.'?q=:q',
                            array('action' => $s.'search'),
                            array('q' => '.+'));
212
                $m->connect('search/'.$s, array('action' => $s.'search'));
213 214 215 216 217 218
            }

            // The second of these is needed to make the link work correctly
            // when inserted into the page. The first is needed to match the
            // route on the way in. Seems to be another Net_URL_Mapper bug to me.
            $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
219
                        array('q' => '.+'));
220
            $m->connect('search/notice/rss', array('action' => 'noticesearchrss'));
221

222 223 224 225 226 227 228 229 230 231
            $m->connect('attachment/:attachment',
                        array('action' => 'attachment'),
                        array('attachment' => '[0-9]+'));

            $m->connect('attachment/:attachment/thumbnail',
                        array('action' => 'attachment_thumbnail'),
                        array('attachment' => '[0-9]+'));

            $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
                        array('action' => 'newnotice'),
232
                        array('replyto' => Nickname::DISPLAY_FMT),
233 234
                        array('inreplyto' => '[0-9]+'));

235 236 237 238 239 240
            $m->connect('notice/new?replyto=:replyto',
                        array('action' => 'newnotice'),
                        array('replyto' => Nickname::DISPLAY_FMT));

            $m->connect('notice/new', array('action' => 'newnotice'));

241 242 243
            $m->connect('notice/:notice',
                        array('action' => 'shownotice'),
                        array('notice' => '[0-9]+'));
244

245
            $m->connect('notice/:notice/delete',
246 247 248 249 250 251 252 253 254 255 256 257 258
                        array('action' => 'deletenotice'),
                        array('notice' => '[0-9]+'));

            // conversation

            $m->connect('conversation/:id',
                        array('action' => 'conversation'),
                        array('id' => '[0-9]+'));

            $m->connect('user/:id',
                        array('action' => 'userbyid'),
                        array('id' => '[0-9]+'));

259 260 261 262 263 264
            if (!common_config('performance', 'high')) {
                $m->connect('tags/', array('action' => 'publictagcloud'));
                $m->connect('tag/', array('action' => 'publictagcloud'));
                $m->connect('tags', array('action' => 'publictagcloud'));
                $m->connect('tag', array('action' => 'publictagcloud'));
            }
265 266
            $m->connect('tag/:tag/rss',
                        array('action' => 'tagrss'),
267
                        array('tag' => self::REGEX_TAG));
268 269
            $m->connect('tag/:tag',
                        array('action' => 'tag'),
270
                        array('tag' => self::REGEX_TAG));
271 272 273 274 275

            // groups

            $m->connect('group/new', array('action' => 'newgroup'));

276
            foreach (array('edit', 'join', 'leave', 'delete', 'cancel', 'approve') as $v) {
277 278
                $m->connect('group/:nickname/'.$v,
                            array('action' => $v.'group'),
279
                            array('nickname' => Nickname::DISPLAY_FMT));
280 281 282
                $m->connect('group/:id/id/'.$v,
                            array('action' => $v.'group'),
                            array('id' => '[0-9]+'));
283 284
            }

285
            foreach (array('members', 'logo', 'rss') as $n) {
286 287
                $m->connect('group/:nickname/'.$n,
                            array('action' => 'group'.$n),
288
                            array('nickname' => Nickname::DISPLAY_FMT));
289 290 291 292
            }

            $m->connect('group/:nickname/foaf',
                        array('action' => 'foafgroup'),
293
                        array('nickname' => Nickname::DISPLAY_FMT));
294

295 296
            $m->connect('group/:nickname/blocked',
                        array('action' => 'blockedfromgroup'),
297
                        array('nickname' => Nickname::DISPLAY_FMT));
298

299 300
            $m->connect('group/:nickname/makeadmin',
                        array('action' => 'makeadmin'),
301
                        array('nickname' => Nickname::DISPLAY_FMT));
302

303 304 305
            $m->connect('group/:nickname/members/pending',
                        array('action' => 'groupqueue'),
                        array('nickname' => Nickname::DISPLAY_FMT));
306

307 308 309
            $m->connect('group/:id/id',
                        array('action' => 'groupbyid'),
                        array('id' => '[0-9]+'));
310

311 312
            $m->connect('group/:nickname',
                        array('action' => 'showgroup'),
313
                        array('nickname' => Nickname::DISPLAY_FMT));
314

315 316 317 318
            $m->connect('group/:nickname/',
                        array('action' => 'showgroup'),
                        array('nickname' => Nickname::DISPLAY_FMT));

319 320 321 322 323 324 325 326 327
            $m->connect('group/', array('action' => 'groups'));
            $m->connect('group', array('action' => 'groups'));
            $m->connect('groups/', array('action' => 'groups'));
            $m->connect('groups', array('action' => 'groups'));

            // Twitter-compatible API

            // statuses API

328 329 330 331 332
            $m->connect('api',
                        array('action' => 'Redirect',
                              'nextAction' => 'doc',
                              'args' => array('title' => 'api')));

333 334
            $m->connect('api/statuses/public_timeline.:format',
                        array('action' => 'ApiTimelinePublic',
335
                              'format' => '(xml|json|rss|atom|as)'));
336

337 338 339 340 341
            // this is not part of the Twitter API. Also may require authentication depending on server config!
            $m->connect('api/statuses/networkpublic_timeline.:format',
                        array('action' => 'ApiTimelineNetworkPublic',
                              'format' => '(xml|json|rss|atom|as)'));

342 343
            $m->connect('api/statuses/friends_timeline/:id.:format',
                        array('action' => 'ApiTimelineFriends',
344
                              'id' => Nickname::INPUT_FMT,
345
                              'format' => '(xml|json|rss|atom|as)'));
346

347 348
            $m->connect('api/statuses/friends_timeline.:format',
                        array('action' => 'ApiTimelineFriends',
349
                              'format' => '(xml|json|rss|atom|as)'));
350 351

            $m->connect('api/statuses/home_timeline/:id.:format',
352
                        array('action' => 'ApiTimelineHome',
353
                              'id' => Nickname::INPUT_FMT,
354
                              'format' => '(xml|json|rss|atom|as)'));
355

356 357
            $m->connect('api/statuses/home_timeline.:format',
                        array('action' => 'ApiTimelineHome',
358
                              'format' => '(xml|json|rss|atom|as)'));
359

360 361
            $m->connect('api/statuses/user_timeline/:id.:format',
                        array('action' => 'ApiTimelineUser',
362
                              'id' => Nickname::INPUT_FMT,
363
                              'format' => '(xml|json|rss|atom|as)'));
364

365 366
            $m->connect('api/statuses/user_timeline.:format',
                        array('action' => 'ApiTimelineUser',
367
                              'format' => '(xml|json|rss|atom|as)'));
368 369 370

            $m->connect('api/statuses/mentions/:id.:format',
                        array('action' => 'ApiTimelineMentions',
371
                              'id' => Nickname::INPUT_FMT,
372
                              'format' => '(xml|json|rss|atom|as)'));
373

374
            $m->connect('api/statuses/mentions.:format',
375
                        array('action' => 'ApiTimelineMentions',
376
                              'format' => '(xml|json|rss|atom|as)'));
377 378 379

            $m->connect('api/statuses/replies/:id.:format',
                        array('action' => 'ApiTimelineMentions',
380
                              'id' => Nickname::INPUT_FMT,
381
                              'format' => '(xml|json|rss|atom|as)'));
382

383 384 385
            $m->connect('api/statuses/replies.:format',
                        array('action' => 'ApiTimelineMentions',
                              'format' => '(xml|json|rss|atom|as)'));
386 387 388 389 390 391 392 393 394
 
            $m->connect('api/statuses/mentions_timeline/:id.:format',
                        array('action' => 'ApiTimelineMentions',
                              'id' => Nickname::INPUT_FMT,
                              'format' => '(xml|json|rss|atom|as)'));

            $m->connect('api/statuses/mentions_timeline.:format',
                        array('action' => 'ApiTimelineMentions',
                              'format' => '(xml|json|rss|atom|as)'));
395

396 397
            $m->connect('api/statuses/friends/:id.:format',
                        array('action' => 'ApiUserFriends',
398
                              'id' => Nickname::INPUT_FMT,
399
                              'format' => '(xml|json)'));
400

401 402
            $m->connect('api/statuses/friends.:format',
                        array('action' => 'ApiUserFriends',
403
                              'format' => '(xml|json)'));
404

405 406
            $m->connect('api/statuses/followers/:id.:format',
                        array('action' => 'ApiUserFollowers',
407
                              'id' => Nickname::INPUT_FMT,
408
                              'format' => '(xml|json)'));
409

410 411 412
            $m->connect('api/statuses/followers.:format',
                        array('action' => 'ApiUserFollowers',
                              'format' => '(xml|json)'));
413

414 415 416
            $m->connect('api/statuses/show/:id.:format',
                        array('action' => 'ApiStatusesShow',
                              'id' => '[0-9]+',
417
                              'format' => '(xml|json|atom)'));
418

419 420 421 422
            $m->connect('api/statuses/show.:format',
                        array('action' => 'ApiStatusesShow',
                              'format' => '(xml|json|atom)'));

423 424 425
            $m->connect('api/statuses/update.:format',
                        array('action' => 'ApiStatusesUpdate',
                              'format' => '(xml|json)'));
426

427
            $m->connect('api/statuses/destroy/:id.:format',
428
                        array('action' => 'ApiStatusesDestroy',
429
                              'id' => '[0-9]+',
430
                              'format' => '(xml|json)'));
431

432
            $m->connect('api/statuses/destroy.:format',
433
                        array('action' => 'ApiStatusesDestroy',
434 435
                              'format' => '(xml|json)'));

436 437
            // START qvitter API additions
            
438 439 440 441
            $m->connect('api/attachment/:id.:format',
                        array('action' => 'ApiAttachment',
                              'id' => '[0-9]+',
                              'format' => '(xml|json)'));
442
            
443 444 445
            $m->connect('api/checkhub.:format',
                        array('action' => 'ApiCheckHub',
                              'format' => '(xml|json)'));
446
            
447 448 449
            $m->connect('api/externalprofile/show.:format',
                        array('action' => 'ApiExternalProfileShow',
                              'format' => '(xml|json)'));
450 451

            $m->connect('api/statusnet/groups/admins/:id.:format',
452 453 454
                        array('action' => 'ApiGroupAdmins',
                              'id' => Nickname::INPUT_FMT,
                              'format' => '(xml|json)'));
455
            
456 457 458
            $m->connect('api/account/update_link_color.:format',
                        array('action' => 'ApiAccountUpdateLinkColor',
                              'format' => '(xml|json)'));
459
                
460 461 462
            $m->connect('api/account/update_background_color.:format',
                        array('action' => 'ApiAccountUpdateBackgroundColor',
                              'format' => '(xml|json)'));
463

464 465 466
            $m->connect('api/account/register.:format',
                        array('action' => 'ApiAccountRegister',
                              'format' => '(xml|json)'));
467
            
468 469 470
            $m->connect('api/check_nickname.:format',
                        array('action' => 'ApiCheckNickname',
                              'format' => '(xml|json)'));
471 472 473

            // END qvitter API additions

474
            // users
475

476
            $m->connect('api/users/show/:id.:format',
477
                        array('action' => 'ApiUserShow',
478
                              'id' => Nickname::INPUT_FMT,
479 480
                              'format' => '(xml|json)'));

481
            $m->connect('api/users/show.:format',
482 483
                        array('action' => 'ApiUserShow',
                              'format' => '(xml|json)'));
484

485 486 487 488 489
            $m->connect('api/users/profile_image/:screen_name.:format',
                        array('action' => 'ApiUserProfileImage',
                              'screen_name' => Nickname::DISPLAY_FMT,
                              'format' => '(xml|json)'));

490
            // friendships
491

492 493 494
            $m->connect('api/friendships/show.:format',
                        array('action' => 'ApiFriendshipsShow',
                              'format' => '(xml|json)'));
495

496 497 498
            $m->connect('api/friendships/exists.:format',
                        array('action' => 'ApiFriendshipsExists',
                              'format' => '(xml|json)'));
499

500
            $m->connect('api/friendships/create/:id.:format',
501
                        array('action' => 'ApiFriendshipsCreate',
502
                              'id' => Nickname::INPUT_FMT,
503
                              'format' => '(xml|json)'));
504

505
            $m->connect('api/friendships/create.:format',
506 507
                        array('action' => 'ApiFriendshipsCreate',
                              'format' => '(xml|json)'));
508

509 510
            $m->connect('api/friendships/destroy/:id.:format',
                        array('action' => 'ApiFriendshipsDestroy',
511
                              'id' => Nickname::INPUT_FMT,
512
                              'format' => '(xml|json)'));
513

514 515 516 517
            $m->connect('api/friendships/destroy.:format',
                        array('action' => 'ApiFriendshipsDestroy',
                              'format' => '(xml|json)'));

518
            // Social graph
519

520
            $m->connect('api/friends/ids/:id.:format',
521
                        array('action' => 'ApiUserFriends',
522
                              'ids_only' => true));
523

524
            $m->connect('api/followers/ids/:id.:format',
525
                        array('action' => 'ApiUserFollowers',
526
                              'ids_only' => true));
527

528
            $m->connect('api/friends/ids.:format',
529
                        array('action' => 'ApiUserFriends',
530
                              'ids_only' => true));
531

532
            $m->connect('api/followers/ids.:format',
533
                        array('action' => 'ApiUserFollowers',
534
                              'ids_only' => true));
535

536
            // account
537

538 539
            $m->connect('api/account/verify_credentials.:format',
                        array('action' => 'ApiAccountVerifyCredentials'));
540

541 542 543
            $m->connect('api/account/update_profile.:format',
                        array('action' => 'ApiAccountUpdateProfile'));

544 545 546
            $m->connect('api/account/update_profile_image.:format',
                        array('action' => 'ApiAccountUpdateProfileImage'));

547 548 549
            $m->connect('api/account/update_delivery_device.:format',
                        array('action' => 'ApiAccountUpdateDeliveryDevice'));

550
            // special case where verify_credentials is called w/out a format
551

552 553
            $m->connect('api/account/verify_credentials',
                        array('action' => 'ApiAccountVerifyCredentials'));
554

555 556
            $m->connect('api/account/rate_limit_status.:format',
                        array('action' => 'ApiAccountRateLimitStatus'));
557

558
            // blocks
559

560 561
            $m->connect('api/blocks/create/:id.:format',
                        array('action' => 'ApiBlockCreate',
562
                              'id' => Nickname::INPUT_FMT,
563
                              'format' => '(xml|json)'));
564

565 566
            $m->connect('api/blocks/create.:format',
                        array('action' => 'ApiBlockCreate',
567 568
                              'format' => '(xml|json)'));

569 570
            $m->connect('api/blocks/destroy/:id.:format',
                        array('action' => 'ApiBlockDestroy',
571
                              'id' => Nickname::INPUT_FMT,
572
                              'format' => '(xml|json)'));
573 574 575 576 577

            $m->connect('api/blocks/destroy.:format',
                        array('action' => 'ApiBlockDestroy',
                              'format' => '(xml|json)'));

578
            // help
579

580 581 582
            $m->connect('api/help/test.:format',
                        array('action' => 'ApiHelpTest',
                              'format' => '(xml|json)'));
583

584
            // statusnet
585

586
            $m->connect('api/statusnet/version.:format',
587
                        array('action' => 'ApiGNUsocialVersion',
588
                              'format' => '(xml|json)'));
589

590
            $m->connect('api/statusnet/config.:format',
591
                        array('action' => 'ApiGNUsocialConfig',
592
                              'format' => '(xml|json)'));
593

594
            // For our current software name, we provide "gnusocial" base action
595

596 597
            $m->connect('api/gnusocial/version.:format',
                        array('action' => 'ApiGNUsocialVersion',
598 599
                              'format' => '(xml|json)'));

600 601
            $m->connect('api/gnusocial/config.:format',
                        array('action' => 'ApiGNUsocialConfig',
602
                              'format' => '(xml|json)'));
603

604 605 606 607 608 609 610 611
            // Groups and tags are newer than 0.8.1 so no backward-compatibility
            // necessary

            // Groups
            //'list' has to be handled differently, as php will not allow a method to be named 'list'

            $m->connect('api/statusnet/groups/timeline/:id.:format',
                        array('action' => 'ApiTimelineGroup',
612
                              'id' => Nickname::INPUT_FMT,
613
                              'format' => '(xml|json|rss|atom|as)'));
614 615 616

            $m->connect('api/statusnet/groups/show/:id.:format',
                        array('action' => 'ApiGroupShow',
617
                              'id' => Nickname::INPUT_FMT,
618 619
                              'format' => '(xml|json)'));

620 621
            $m->connect('api/statusnet/groups/show.:format',
                        array('action' => 'ApiGroupShow',
622 623 624 625
                              'format' => '(xml|json)'));

            $m->connect('api/statusnet/groups/join/:id.:format',
                        array('action' => 'ApiGroupJoin',
mmn's avatar
mmn committed
626
                              'id' => Nickname::INPUT_FMT,
627 628
                              'format' => '(xml|json)'));

629 630
            $m->connect('api/statusnet/groups/join.:format',
                        array('action' => 'ApiGroupJoin',
631 632 633 634 635 636
                              'format' => '(xml|json)'));

            $m->connect('api/statusnet/groups/leave/:id.:format',
                        array('action' => 'ApiGroupLeave',
                              'format' => '(xml|json)'));

637 638 639 640 641
            $m->connect('api/statusnet/groups/leave.:format',
                        array('action' => 'ApiGroupLeave',
                              'id' => Nickname::INPUT_FMT,
                              'format' => '(xml|json)'));

642 643 644 645
            $m->connect('api/statusnet/groups/is_member.:format',
                        array('action' => 'ApiGroupIsMember',
                              'format' => '(xml|json)'));

646
            $m->connect('api/statusnet/groups/list/:id.:format',
647
                        array('action' => 'ApiGroupList',
648
                              'id' => Nickname::INPUT_FMT,
649 650
                              'format' => '(xml|json|rss|atom)'));

651
            $m->connect('api/statusnet/groups/list.:format',
652 653 654 655 656 657 658
                        array('action' => 'ApiGroupList',
                              'format' => '(xml|json|rss|atom)'));

            $m->connect('api/statusnet/groups/list_all.:format',
                        array('action' => 'ApiGroupListAll',
                              'format' => '(xml|json|rss|atom)'));

659
            $m->connect('api/statusnet/groups/membership/:id.:format',
660
                        array('action' => 'ApiGroupMembership',
661
                              'id' => Nickname::INPUT_FMT,
662 663
                              'format' => '(xml|json)'));

664
            $m->connect('api/statusnet/groups/membership.:format',
665 666 667 668 669 670
                        array('action' => 'ApiGroupMembership',
                              'format' => '(xml|json)'));

            $m->connect('api/statusnet/groups/create.:format',
                        array('action' => 'ApiGroupCreate',
                              'format' => '(xml|json)'));
671 672 673 674 675

            $m->connect('api/statusnet/groups/update/:id.:format',
                        array('action' => 'ApiGroupProfileUpdate',
                              'id' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json)'));
Evan Prodromou's avatar
Evan Prodromou committed
676 677 678 679 680
                              
            $m->connect('api/statusnet/conversation/:id.:format',
                        array('action' => 'apiconversation',
                              'id' => '[0-9]+',
                              'format' => '(xml|json|rss|atom|as)'));
681

682
            // Lists (people tags)
683 684 685
            $m->connect('api/lists/list.:format',
                        array('action' => 'ApiListSubscriptions',
                              'format' => '(xml|json)'));
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704

            $m->connect('api/lists/memberships.:format',
                        array('action' => 'ApiListMemberships',
                              'format' => '(xml|json)'));

            $m->connect('api/:user/lists/memberships.:format',
                        array('action' => 'ApiListMemberships',
                              'user' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json)'));

            $m->connect('api/lists/subscriptions.:format',
                        array('action' => 'ApiListSubscriptions',
                              'format' => '(xml|json)'));

            $m->connect('api/:user/lists/subscriptions.:format',
                        array('action' => 'ApiListSubscriptions',
                              'user' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json)'));

705
            $m->connect('api/lists.:format',
706 707 708 709 710 711 712 713 714
                        array('action' => 'ApiLists',
                              'format' => '(xml|json)'));

            $m->connect('api/:user/lists/:id.:format',
                        array('action' => 'ApiList',
                              'user' => '[a-zA-Z0-9]+',
                              'id' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json)'));

715 716 717 718 719
            $m->connect('api/:user/lists.:format',
                        array('action' => 'ApiLists',
                              'user' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json)'));

720 721 722 723 724 725
            $m->connect('api/:user/lists/:id/statuses.:format',
                        array('action' => 'ApiTimelineList',
                              'user' => '[a-zA-Z0-9]+',
                              'id' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json|rss|atom)'));

726 727
            $m->connect('api/:user/:list_id/members/:id.:format',
                        array('action' => 'ApiListMember',
728 729
                              'user' => '[a-zA-Z0-9]+',
                              'list_id' => '[a-zA-Z0-9]+',
730
                              'id' => '[a-zA-Z0-9]+',
731 732
                              'format' => '(xml|json)'));

733 734
            $m->connect('api/:user/:list_id/members.:format',
                        array('action' => 'ApiListMembers',
735 736 737 738
                              'user' => '[a-zA-Z0-9]+',
                              'list_id' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json)'));

739 740
            $m->connect('api/:user/:list_id/subscribers/:id.:format',
                        array('action' => 'ApiListSubscriber',
741 742 743 744 745
                              'user' => '[a-zA-Z0-9]+',
                              'list_id' => '[a-zA-Z0-9]+',
                              'id' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json)'));

746 747
            $m->connect('api/:user/:list_id/subscribers.:format',
                        array('action' => 'ApiListSubscribers',
748 749 750 751
                              'user' => '[a-zA-Z0-9]+',
                              'list_id' => '[a-zA-Z0-9]+',
                              'format' => '(xml|json)'));

752 753 754
            // Tags
            $m->connect('api/statusnet/tags/timeline/:tag.:format',
                        array('action' => 'ApiTimelineTag',
755
                              'tag'    => self::REGEX_TAG,
756
                              'format' => '(xml|json|rss|atom|as)'));
757

758 759 760 761 762
            // media related
            $m->connect(
                'api/statusnet/media/upload',
                array('action' => 'ApiMediaUpload')
            );
763 764 765 766
            $m->connect(
                'api/statuses/update_with_media.json',
                array('action' => 'ApiMediaUpload')
            );
767 768 769 770 771 772 773
            // Twitter Media upload API v1.1
            $m->connect(
                'api/media/upload.:format',
                array('action' => 'ApiMediaUpload',
                      'format' => '(xml|json)',
                      )
            );
774

775
            // search
776 777 778
            $m->connect('api/search.atom', array('action' => 'ApiSearchAtom'));
            $m->connect('api/search.json', array('action' => 'ApiSearchJSON'));
            $m->connect('api/trends.json', array('action' => 'ApiTrends'));
779

Evan Prodromou's avatar
Evan Prodromou committed
780
            $m->connect('api/oauth/request_token',
781
                        array('action' => 'ApiOAuthRequestToken'));
Evan Prodromou's avatar
Evan Prodromou committed
782 783

            $m->connect('api/oauth/access_token',
784
                        array('action' => 'ApiOAuthAccessToken'));
Evan Prodromou's avatar
Evan Prodromou committed
785 786

            $m->connect('api/oauth/authorize',
787
                        array('action' => 'ApiOAuthAuthorize'));
Evan Prodromou's avatar
Evan Prodromou committed
788 789 790

            // Admin

791 792
            $m->connect('panel/site', array('action' => 'siteadminpanel'));
            $m->connect('panel/user', array('action' => 'useradminpanel'));
793
            $m->connect('panel/access', array('action' => 'accessadminpanel'));
794 795 796 797 798 799 800
            $m->connect('panel/paths', array('action' => 'pathsadminpanel'));
            $m->connect('panel/sessions', array('action' => 'sessionsadminpanel'));
            $m->connect('panel/sitenotice', array('action' => 'sitenoticeadminpanel'));
            $m->connect('panel/license', array('action' => 'licenseadminpanel'));

            $m->connect('panel/plugins', array('action' => 'pluginsadminpanel'));
            $m->connect('panel/plugins/enable/:plugin',
801 802
                        array('action' => 'pluginenable'),
                        array('plugin' => '[A-Za-z0-9_]+'));
803
            $m->connect('panel/plugins/disable/:plugin',
804 805
                        array('action' => 'plugindisable'),
                        array('plugin' => '[A-Za-z0-9_]+'));
806

807 808 809 810
            $m->connect('getfile/:filename',
                        array('action' => 'getfile'),
                        array('filename' => '[A-Za-z0-9._-]+'));

811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
            // Common people-tag stuff

            $m->connect('peopletag/:tag', array('action' => 'peopletag',
                                                'tag'    => self::REGEX_TAG));

            $m->connect('selftag/:tag', array('action' => 'selftag',
                                              'tag'    => self::REGEX_TAG));

            $m->connect('main/addpeopletag', array('action' => 'addpeopletag'));

            $m->connect('main/removepeopletag', array('action' => 'removepeopletag'));

            $m->connect('main/profilecompletion', array('action' => 'profilecompletion'));

            $m->connect('main/peopletagautocomplete', array('action' => 'peopletagautocomplete'));

Evan Prodromou's avatar
Evan Prodromou committed
827
            // In the "root"
828

Evan Prodromou's avatar
Evan Prodromou committed
829 830
            if (common_config('singleuser', 'enabled')) {

831
                $nickname = User::singleUserNickname();
Evan Prodromou's avatar
Evan Prodromou committed
832 833

                foreach (array('subscriptions', 'subscribers',
834
                               'all', 'foaf', 'replies',
835
                               'microsummary') as $a) {
Evan Prodromou's avatar
Evan Prodromou committed
836 837 838 839 840 841 842 843 844
                    $m->connect($a,
                                array('action' => $a,
                                      'nickname' => $nickname));
                }

                foreach (array('subscriptions', 'subscribers') as $a) {
                    $m->connect($a.'/:tag',
                                array('action' => $a,
                                      'nickname' => $nickname),
845
                                array('tag' => self::REGEX_TAG));
Evan Prodromou's avatar
Evan Prodromou committed
846 847
                }

848 849 850 851
                $m->connect('subscribers/pending',
                            array('action' => 'subqueue',
                                  'nickname' => $nickname));

Evan Prodromou's avatar
Evan Prodromou committed
852 853 854 855 856 857
                foreach (array('rss', 'groups') as $a) {
                    $m->connect($a,
                                array('action' => 'user'.$a,
                                      'nickname' => $nickname));
                }

858
                foreach (array('all', 'replies') as $a) {
Evan Prodromou's avatar
Evan Prodromou committed
859 860 861 862 863
                    $m->connect($a.'/rss',
                                array('action' => $a.'rss',
                                      'nickname' => $nickname));
                }

864 865 866
                $m->connect('avatar',
                            array('action' => 'avatarbynickname',
                                  'nickname' => $nickname));
Evan Prodromou's avatar
Evan Prodromou committed
867 868 869
                $m->connect('avatar/:size',
                            array('action' => 'avatarbynickname',
                                  'nickname' => $nickname),
870
                            array('size' => '(|original|\d+)'));
Evan Prodromou's avatar
Evan Prodromou committed
871 872 873 874

                $m->connect('tag/:tag/rss',
                            array('action' => 'userrss',
                                  'nickname' => $nickname),
875
                            array('tag' => self::REGEX_TAG));
Evan Prodromou's avatar
Evan Prodromou committed
876 877 878 879

                $m->connect('tag/:tag',
                            array('action' => 'showstream',
                                  'nickname' => $nickname),
880
                            array('tag' => self::REGEX_TAG));
Evan Prodromou's avatar
Evan Prodromou committed
881

882 883 884 885
                $m->connect('rsd.xml',
                            array('action' => 'rsd',
                                  'nickname' => $nickname));

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
                // peopletags

                $m->connect('peopletags',
                            array('action' => 'peopletagsbyuser'));

                $m->connect('peopletags/private',
                            array('action' => 'peopletagsbyuser',
                                  'private' => 1));

                $m->connect('peopletags/public',
                            array('action' => 'peopletagsbyuser',
                                  'public' => 1));

                $m->connect('othertags',
                            array('action' => 'peopletagsforuser'));

                $m->connect('peopletagsubscriptions',
                            array('action' => 'peopletagsubscriptions'));

                $m->connect('all/:tag/subscribers',
                            array('action' => 'peopletagsubscribers',
                                  'tag' => self::REGEX_TAG));

                $m->connect('all/:tag/tagged',
                                array('action' => 'peopletagged',
                                      'tag' => self::REGEX_TAG));

                $m->connect('all/:tag/edit',
                                array('action' => 'editpeopletag',
                                      'tag' => self::REGEX_TAG));

                foreach(array('subscribe', 'unsubscribe') as $v) {
                    $m->connect('peopletag/:id/'.$v,
                                    array('action' => $v.'peopletag',
                                          'id' => '[0-9]{1,64}'));
                }
                $m->connect('user/:tagger_id/profiletag/:id/id',
                                array('action' => 'profiletagbyid',
                                      'tagger_id' => '[0-9]+',
                                      'id' => '[0-9]+'));

                $m->connect('all/:tag',
                                array('action' => 'showprofiletag',
929
                                      'nickname' => $nickname,
930 931 932 933 934 935 936
                                      'tag' => self::REGEX_TAG));

                foreach (array('subscriptions', 'subscribers') as $a) {
                    $m->connect($a.'/:tag',
                                array('action' => $a),
                                array('tag' => self::REGEX_TAG));
                }
937
            }
Evan Prodromou's avatar
Evan Prodromou committed
938

939 940 941 942 943 944 945 946 947 948 949
            $m->connect('rss', array('action' => 'publicrss'));
            $m->connect('featuredrss', array('action' => 'featuredrss'));
            $m->connect('featured/', array('action' => 'featured'));
            $m->connect('featured', array('action' => 'featured'));
            $m->connect('rsd.xml', array('action' => 'rsd'));

            foreach (array('subscriptions', 'subscribers',
                           'nudge', 'all', 'foaf', 'replies',
                           'inbox', 'outbox', 'microsummary') as $a) {
                $m->connect(':nickname/'.$a,
                            array('action' => $a),
950
                            array('nickname' => Nickname::DISPLAY_FMT));
951 952 953 954
            }
            $m->connect(':nickname/subscribers/pending',
                        array('action' => 'subqueue'),
                        array('nickname' => Nickname::DISPLAY_FMT));
Evan Prodromou's avatar
Evan Prodromou committed
955

956 957 958 959 960 961 962
            // some targeted RSS 1.0 actions (extends TargetedRss10Action)
            foreach (array('all', 'replies') as $a) {
                $m->connect(':nickname/'.$a.'/rss',
                            array('action' => $a.'rss'),
                            array('nickname' => Nickname::DISPLAY_FMT));
            }

963
            // people tags
Shashi Gowda's avatar
Shashi Gowda committed
964

965 966 967
            $m->connect(':nickname/peopletags',
                            array('action' => 'peopletagsbyuser',
                                  'nickname' => Nickname::DISPLAY_FMT));
Shashi Gowda's avatar
Shashi Gowda committed
968

969 970 971 972
            $m->connect(':nickname/peopletags/private',
                            array('action' => 'peopletagsbyuser',
                                  'nickname' => Nickname::DISPLAY_FMT,
                                  'private' => 1));
Shashi Gowda's avatar
Shashi Gowda committed
973

974 975 976 977
            $m->connect(':nickname/peopletags/public',
                            array('action' => 'peopletagsbyuser',
                                  'nickname' => Nickname::DISPLAY_FMT,
                                  'public' => 1));
Shashi Gowda's avatar
Shashi Gowda committed
978

979 980 981
            $m->connect(':nickname/othertags',
                            array('action' => 'peopletagsforuser',
                                  'nickname' => Nickname::DISPLAY_FMT));
Shashi Gowda's avatar
Shashi Gowda committed
982

983 984 985
            $m->connect(':nickname/peopletagsubscriptions',
                            array('action' => 'peopletagsubscriptions',
                                  'nickname' => Nickname::DISPLAY_FMT));
Shashi Gowda's avatar
Shashi Gowda committed
986

987 988 989 990
            $m->connect(':tagger/all/:tag/subscribers',
                            array('action' => 'peopletagsubscribers',
                                  'tagger' => Nickname::DISPLAY_FMT,
                                  'tag' => self::REGEX_TAG));
Shashi Gowda's avatar
Shashi Gowda committed
991

992 993 994 995
            $m->connect(':tagger/all/:tag/tagged',
                            array('action' => 'peopletagged',
                                  'tagger' => Nickname::DISPLAY_FMT,
                                  'tag' => self::REGEX_TAG));
Shashi Gowda's avatar
Shashi Gowda committed
996

997 998 999 1000
            $m->connect(':tagger/all/:tag/edit',
                            array('action' => 'editpeopletag',
                                  'tagger' => Nickname::DISPLAY_FMT,
                                  'tag' => self::REGEX_TAG));
Shashi Gowda's avatar
Shashi Gowda committed
1001

1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
            foreach(array('subscribe', 'unsubscribe') as $v) {
                $m->connect('peopletag/:id/'.$v,
                                array('action' => $v.'peopletag',
                                      'id' => '[0-9]{1,64}'));
            }
            $m->connect('user/:tagger_id/profiletag/:id/id',
                            array('action' => 'profiletagbyid',
                                  'tagger_id' => '[0-9]+',
                                  'id' => '[0-9]+'));

1012 1013 1014
            $m->connect(':nickname/all/:tag',
                            array('action' => 'showprofiletag'),
                            array('nickname' => Nickname::DISPLAY_FMT,
1015
                                  'tag' => self::REGEX_TAG));
Evan Prodromou's avatar
Evan Prodromou committed
1016

1017 1018 1019 1020 1021 1022
            foreach (array('subscriptions', 'subscribers') as $a) {
                $m->connect(':nickname/'.$a.'/:tag',
                            array('action' => $a),
                            array('tag' => self::REGEX_TAG,
                                  'nickname' => Nickname::DISPLAY_FMT));
            }
Evan Prodromou's avatar
Evan Prodromou committed
1023

1024 1025 1026 1027 1028
            foreach (array('rss', 'groups') as $a) {
                $m->connect(':nickname/'.$a,
                            array('action' => 'user'.$a),
                            array('nickname' => Nickname::DISPLAY_FMT));
            }
Evan Prodromou's avatar
Evan Prodromou committed
1029

1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
            $m->connect(':nickname/avatar',
                        array('action' => 'avatarbynickname'),
                        array('nickname' => Nickname::DISPLAY_FMT));
            $m->connect(':nickname/avatar/:size',
                        array('action' => 'avatarbynickname'),
                        array('size' => '(|original|\d+)',
                              'nickname' => Nickname::DISPLAY_FMT));

            $m->connect(':nickname/tag/:tag/rss',
                        array('action' => 'userrss'),
                        array('nickname' => Nickname::DISPLAY_FMT),
                        array('tag' => self::REGEX_TAG));
Evan Prodromou's avatar
Evan Prodromou committed
1042

1043 1044 1045 1046
            $m->connect(':nickname/tag/:tag',
                        array('action' => 'showstream'),
                        array('nickname' => Nickname::DISPLAY_FMT),
                        array('tag' => self::REGEX_TAG));
1047

1048 1049 1050
            $m->connect(':nickname/rsd.xml',
                        array('action' => 'rsd'),
                        array('nickname' => Nickname::DISPLAY_FMT));
1051

1052 1053 1054
            $m->connect(':nickname',
                        array('action' => 'showstream'),
                        array('nickname' => Nickname::DISPLAY_FMT));
1055

1056 1057 1058
            $m->connect(':nickname/',
                        array('action' => 'showstream'),
                        array('nickname' => Nickname::DISPLAY_FMT));
1059

1060 1061 1062
            // AtomPub API

            $m->connect('api/statusnet/app/service/:id.xml',
1063 1064
                        array('action' => 'ApiAtomService'),
                        array('id' => Nickname::DISPLAY_FMT));
1065 1066 1067 1068 1069 1070 1071 1072 1073

            $m->connect('api/statusnet/app/service.xml',
                        array('action' => 'ApiAtomService'));

            $m->connect('api/statusnet/app/subscriptions/:subscriber/:subscribed.atom',
                        array('action' => 'AtomPubShowSubscription'),
                        array('subscriber' => '[0-9]+',
                              'subscribed' => '[0-9]+'));

1074 1075 1076 1077
            $m->connect('api/statusnet/app/subscriptions/:subscriber.atom',
                        array('action' => 'AtomPubSubscriptionFeed'),
                        array('subscriber' => '[0-9]+'));

1078 1079 1080 1081 1082
            $m->connect('api/statusnet/app/memberships/:profile/:group.atom',
                        array('action' => 'AtomPubShowMembership'),
                        array('profile' => '[0-9]+',
                              'group' => '[0-9]+'));

1083
            $m->connect('api/statusnet/app/memberships/:profile.atom',
1084 1085 1086
                        array('action' => 'AtomPubMembershipFeed'),
                        array('profile' => '[0-9]+'));

1087 1088 1089 1090 1091 1092
            // URL shortening

            $m->connect('url/:id',
                        array('action' => 'redirecturl',
                              'id' => '[0-9]+'));

Evan Prodromou's avatar
Evan Prodromou committed
1093
            // user stuff
1094

1095 1096
            Event::handle('RouterInitialized', array($m));
        }
1097

1098
        return $m;
1099 1100 1101 1102
    }

    function map($path)
    {
1103
        try {
1104 1105 1106
            return $this->m->match($path);
        } catch (NoRouteMapException $e) {
            common_debug($e->getMessage());
1107
            // TRANS: Client error on action trying to visit a non-existing page.
1108
            throw new ClientException(_('Page not found.'), 404);
1109
        }
1110 1111
    }

1112
    function build($action, $args=null, $params=null, $fragment=null)
1113 1114 1115 1116
    {
        $action_arg = array('action' => $action);

        if ($args) {
Evan Prodromou's avatar
Evan Prodromou committed
1117
            $args = array_merge($action_arg, $args);
1118 1119 1120 1121
        } else {
            $args = $action_arg;
        }

1122
        $url = $this->m->generate($args, $params, $fragment);
1123 1124 1125
        // Due to a bug in the Net_URL_Mapper code, the returned URL may
        // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
        // repair that here rather than modifying the upstream code...
1126 1127

        $qpos = strpos($url, '?');
1128 1129
        if ($qpos !== false) {
            $url = substr($url, 0, $qpos+1) .
1130
                str_replace('?', '&', substr($url, $qpos+1));
1131 1132 1133 1134 1135 1136

            // @fixme this is a hacky workaround for http_build_query in the
            // lower-level code and bad configs that set the default separator
            // to &amp; instead of &. Encoded &s in parameters will not be
            // affected.
            $url = substr($url, 0, $qpos+1) .
1137
                str_replace('&amp;', '&', substr($url, $qpos+1));
1138

1139
        }
1140

1141
        return $url;
1142
    }
Adrian Lang's avatar
Adrian Lang committed
1143
}