]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/router.php
Fix for compile error (misnamed function) in 4211b7f01188b4ab64407e32b380366a048102f4
[quix0rs-gnu-social.git] / lib / router.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * URL routing utilities
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  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('STATUSNET') && !defined('LACONICA')) {
31     exit(1);
32 }
33
34 require_once 'Net/URL/Mapper.php';
35
36 class StatusNet_URL_Mapper extends Net_URL_Mapper {
37
38     private static $_singleton = null;
39
40     private function __construct()
41     {
42     }
43
44     public static function getInstance($id = '__default__')
45     {
46         if (empty(self::$_singleton)) {
47             self::$_singleton = new StatusNet_URL_Mapper();
48         }
49         return self::$_singleton;
50     }
51
52     public function connect($path, $defaults = array(), $rules = array())
53     {
54         $result = null;
55         if (Event::handle('StartConnectPath', array(&$path, &$defaults, &$rules, &$result))) {
56             $result = parent::connect($path, $defaults, $rules);
57             Event::handle('EndConnectPath', array($path, $defaults, $rules, $result));
58         }
59         return $result;
60     }
61 }
62
63 /**
64  * URL Router
65  *
66  * Cheap wrapper around Net_URL_Mapper
67  *
68  * @category URL
69  * @package  StatusNet
70  * @author   Evan Prodromou <evan@status.net>
71  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
72  * @link     http://status.net/
73  */
74
75 class Router
76 {
77     var $m = null;
78     static $inst = null;
79     static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
80                          'postnotice', 'updateprofile', 'finishremotesubscribe');
81
82     static function get()
83     {
84         if (!Router::$inst) {
85             Router::$inst = new Router();
86         }
87         return Router::$inst;
88     }
89
90     function __construct()
91     {
92         if (!$this->m) {
93             $this->m = $this->initialize();
94         }
95     }
96
97     function initialize()
98     {
99         $m = StatusNet_URL_Mapper::getInstance();
100
101         if (Event::handle('StartInitializeRouter', array(&$m))) {
102
103             $m->connect('robots.txt', array('action' => 'robotstxt'));
104
105             $m->connect('opensearch/people', array('action' => 'opensearch',
106                                                    'type' => 'people'));
107             $m->connect('opensearch/notice', array('action' => 'opensearch',
108                                                    'type' => 'notice'));
109
110             // docs
111
112             $m->connect('doc/:title', array('action' => 'doc'));
113
114             $m->connect('main/otp/:user_id/:token',
115                         array('action' => 'otp'),
116                         array('user_id' => '[0-9]+',
117                               'token' => '.+'));
118
119             // main stuff is repetitive
120
121             $main = array('login', 'logout', 'register', 'subscribe',
122                           'unsubscribe', 'confirmaddress', 'recoverpassword',
123                           'invite', 'favor', 'disfavor', 'sup',
124                           'block', 'unblock', 'subedit',
125                           'groupblock', 'groupunblock',
126                           'sandbox', 'unsandbox',
127                           'silence', 'unsilence',
128                           'grantrole', 'revokerole',
129                           'repeat',
130                           'deleteuser',
131                           'geocode',
132                           'version',
133                           );
134
135             foreach ($main as $a) {
136                 $m->connect('main/'.$a, array('action' => $a));
137             }
138
139             $m->connect('main/sup/:seconds', array('action' => 'sup'),
140                         array('seconds' => '[0-9]+'));
141
142             $m->connect('main/tagother/:id', array('action' => 'tagother'));
143
144             $m->connect('main/oembed',
145                         array('action' => 'oembed'));
146
147             $m->connect('main/xrds',
148                         array('action' => 'publicxrds'));
149
150             // these take a code
151
152             foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) {
153                 $m->connect('main/'.$c.'/:code', array('action' => $c));
154             }
155
156             // exceptional
157
158             $m->connect('main/remote', array('action' => 'remotesubscribe'));
159             $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
160
161             foreach (Router::$bare as $action) {
162                 $m->connect('index.php?action=' . $action, array('action' => $action));
163             }
164
165             // settings
166
167             foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections',
168                            'oauthapps', 'email', 'sms', 'userdesign', 'other') as $s) {
169                 $m->connect('settings/'.$s, array('action' => $s.'settings'));
170             }
171
172             $m->connect('settings/oauthapps/show/:id',
173                 array('action' => 'showapplication'),
174                 array('id' => '[0-9]+')
175             );
176             $m->connect('settings/oauthapps/new',
177                 array('action' => 'newapplication')
178             );
179             $m->connect('settings/oauthapps/edit/:id',
180                 array('action' => 'editapplication'),
181                 array('id' => '[0-9]+')
182             );
183             $m->connect('settings/oauthapps/delete/:id',
184                 array('action' => 'deleteapplication'),
185                 array('id' => '[0-9]+')
186             );
187
188             // search
189
190             foreach (array('group', 'people', 'notice') as $s) {
191                 $m->connect('search/'.$s, array('action' => $s.'search'));
192                 $m->connect('search/'.$s.'?q=:q',
193                             array('action' => $s.'search'),
194                             array('q' => '.+'));
195             }
196
197             // The second of these is needed to make the link work correctly
198             // when inserted into the page. The first is needed to match the
199             // route on the way in. Seems to be another Net_URL_Mapper bug to me.
200             $m->connect('search/notice/rss', array('action' => 'noticesearchrss'));
201             $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
202                         array('q' => '.+'));
203
204             $m->connect('attachment/:attachment',
205                         array('action' => 'attachment'),
206                         array('attachment' => '[0-9]+'));
207
208             $m->connect('attachment/:attachment/ajax',
209                         array('action' => 'attachment_ajax'),
210                         array('attachment' => '[0-9]+'));
211
212             $m->connect('attachment/:attachment/thumbnail',
213                         array('action' => 'attachment_thumbnail'),
214                         array('attachment' => '[0-9]+'));
215
216             $m->connect('notice/new', array('action' => 'newnotice'));
217             $m->connect('notice/new?replyto=:replyto',
218                         array('action' => 'newnotice'),
219                         array('replyto' => '[A-Za-z0-9_-]+'));
220             $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
221                         array('action' => 'newnotice'),
222                         array('replyto' => '[A-Za-z0-9_-]+'),
223                         array('inreplyto' => '[0-9]+'));
224
225             $m->connect('notice/:notice/file',
226                         array('action' => 'file'),
227                         array('notice' => '[0-9]+'));
228
229             $m->connect('notice/:notice',
230                         array('action' => 'shownotice'),
231                         array('notice' => '[0-9]+'));
232             $m->connect('notice/delete', array('action' => 'deletenotice'));
233             $m->connect('notice/delete/:notice',
234                         array('action' => 'deletenotice'),
235                         array('notice' => '[0-9]+'));
236
237             $m->connect('bookmarklet/new', array('action' => 'bookmarklet'));
238
239             // conversation
240
241             $m->connect('conversation/:id',
242                         array('action' => 'conversation'),
243                         array('id' => '[0-9]+'));
244
245             $m->connect('message/new', array('action' => 'newmessage'));
246             $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => '[A-Za-z0-9_-]+'));
247             $m->connect('message/:message',
248                         array('action' => 'showmessage'),
249                         array('message' => '[0-9]+'));
250
251             $m->connect('user/:id',
252                         array('action' => 'userbyid'),
253                         array('id' => '[0-9]+'));
254
255             $m->connect('tags/', array('action' => 'publictagcloud'));
256             $m->connect('tag/', array('action' => 'publictagcloud'));
257             $m->connect('tags', array('action' => 'publictagcloud'));
258             $m->connect('tag', array('action' => 'publictagcloud'));
259             $m->connect('tag/:tag/rss',
260                         array('action' => 'tagrss'),
261                         array('tag' => '[a-zA-Z0-9]+'));
262             $m->connect('tag/:tag',
263                         array('action' => 'tag'),
264                         array('tag' => '[\pL\pN_\-\.]{1,64}'));
265
266             $m->connect('peopletag/:tag',
267                         array('action' => 'peopletag'),
268                         array('tag' => '[a-zA-Z0-9]+'));
269
270             // groups
271
272             $m->connect('group/new', array('action' => 'newgroup'));
273
274             foreach (array('edit', 'join', 'leave') as $v) {
275                 $m->connect('group/:nickname/'.$v,
276                             array('action' => $v.'group'),
277                             array('nickname' => '[a-zA-Z0-9]+'));
278                 $m->connect('group/:id/id/'.$v,
279                             array('action' => $v.'group'),
280                             array('id' => '[0-9]+'));
281             }
282
283             foreach (array('members', 'logo', 'rss', 'designsettings') as $n) {
284                 $m->connect('group/:nickname/'.$n,
285                             array('action' => 'group'.$n),
286                             array('nickname' => '[a-zA-Z0-9]+'));
287             }
288
289             $m->connect('group/:nickname/foaf',
290                         array('action' => 'foafgroup'),
291                         array('nickname' => '[a-zA-Z0-9]+'));
292
293             $m->connect('group/:nickname/blocked',
294                         array('action' => 'blockedfromgroup'),
295                         array('nickname' => '[a-zA-Z0-9]+'));
296
297             $m->connect('group/:nickname/makeadmin',
298                         array('action' => 'makeadmin'),
299                         array('nickname' => '[a-zA-Z0-9]+'));
300
301             $m->connect('group/:id/id',
302                         array('action' => 'groupbyid'),
303                         array('id' => '[0-9]+'));
304
305             $m->connect('group/:nickname',
306                         array('action' => 'showgroup'),
307                         array('nickname' => '[a-zA-Z0-9]+'));
308
309             $m->connect('group/', array('action' => 'groups'));
310             $m->connect('group', array('action' => 'groups'));
311             $m->connect('groups/', array('action' => 'groups'));
312             $m->connect('groups', array('action' => 'groups'));
313
314             // Twitter-compatible API
315
316             // statuses API
317
318             $m->connect('api/statuses/public_timeline.:format',
319                         array('action' => 'ApiTimelinePublic',
320                               'format' => '(xml|json|rss|atom)'));
321
322             $m->connect('api/statuses/friends_timeline.:format',
323                         array('action' => 'ApiTimelineFriends',
324                               'format' => '(xml|json|rss|atom)'));
325
326             $m->connect('api/statuses/friends_timeline/:id.:format',
327                         array('action' => 'ApiTimelineFriends',
328                               'id' => '[a-zA-Z0-9]+',
329                               'format' => '(xml|json|rss|atom)'));
330
331             $m->connect('api/statuses/home_timeline.:format',
332                         array('action' => 'ApiTimelineHome',
333                               'format' => '(xml|json|rss|atom)'));
334
335             $m->connect('api/statuses/home_timeline/:id.:format',
336                         array('action' => 'ApiTimelineHome',
337                               'id' => '[a-zA-Z0-9]+',
338                               'format' => '(xml|json|rss|atom)'));
339
340             $m->connect('api/statuses/user_timeline.:format',
341                         array('action' => 'ApiTimelineUser',
342                               'format' => '(xml|json|rss|atom)'));
343
344             $m->connect('api/statuses/user_timeline/:id.:format',
345                         array('action' => 'ApiTimelineUser',
346                               'id' => '[a-zA-Z0-9]+',
347                               'format' => '(xml|json|rss|atom)'));
348
349             $m->connect('api/statuses/mentions.:format',
350                         array('action' => 'ApiTimelineMentions',
351                               'format' => '(xml|json|rss|atom)'));
352
353             $m->connect('api/statuses/mentions/:id.:format',
354                         array('action' => 'ApiTimelineMentions',
355                               'id' => '[a-zA-Z0-9]+',
356                               'format' => '(xml|json|rss|atom)'));
357
358             $m->connect('api/statuses/replies.:format',
359                         array('action' => 'ApiTimelineMentions',
360                               'format' => '(xml|json|rss|atom)'));
361
362             $m->connect('api/statuses/replies/:id.:format',
363                         array('action' => 'ApiTimelineMentions',
364                               'id' => '[a-zA-Z0-9]+',
365                               'format' => '(xml|json|rss|atom)'));
366
367             $m->connect('api/statuses/retweeted_by_me.:format',
368                         array('action' => 'ApiTimelineRetweetedByMe',
369                               'format' => '(xml|json|atom)'));
370
371             $m->connect('api/statuses/retweeted_to_me.:format',
372                         array('action' => 'ApiTimelineRetweetedToMe',
373                               'format' => '(xml|json|atom)'));
374
375             $m->connect('api/statuses/retweets_of_me.:format',
376                         array('action' => 'ApiTimelineRetweetsOfMe',
377                               'format' => '(xml|json|atom)'));
378
379             $m->connect('api/statuses/friends.:format',
380                         array('action' => 'ApiUserFriends',
381                               'format' => '(xml|json)'));
382
383             $m->connect('api/statuses/friends/:id.:format',
384                         array('action' => 'ApiUserFriends',
385                               'id' => '[a-zA-Z0-9]+',
386                               'format' => '(xml|json)'));
387
388             $m->connect('api/statuses/followers.:format',
389                         array('action' => 'ApiUserFollowers',
390                               'format' => '(xml|json)'));
391
392             $m->connect('api/statuses/followers/:id.:format',
393                         array('action' => 'ApiUserFollowers',
394                               'id' => '[a-zA-Z0-9]+',
395                               'format' => '(xml|json)'));
396
397             $m->connect('api/statuses/show.:format',
398                         array('action' => 'ApiStatusesShow',
399                               'format' => '(xml|json)'));
400
401             $m->connect('api/statuses/show/:id.:format',
402                         array('action' => 'ApiStatusesShow',
403                               'id' => '[0-9]+',
404                               'format' => '(xml|json)'));
405
406             $m->connect('api/statuses/update.:format',
407                         array('action' => 'ApiStatusesUpdate',
408                               'format' => '(xml|json)'));
409
410             $m->connect('api/statuses/destroy.:format',
411                         array('action' => 'ApiStatusesDestroy',
412                               'format' => '(xml|json)'));
413
414             $m->connect('api/statuses/destroy/:id.:format',
415                         array('action' => 'ApiStatusesDestroy',
416                               'id' => '[0-9]+',
417                               'format' => '(xml|json)'));
418
419             $m->connect('api/statuses/retweet/:id.:format',
420                         array('action' => 'ApiStatusesRetweet',
421                               'id' => '[0-9]+',
422                               'format' => '(xml|json)'));
423
424             $m->connect('api/statuses/retweets/:id.:format',
425                         array('action' => 'ApiStatusesRetweets',
426                               'id' => '[0-9]+',
427                               'format' => '(xml|json)'));
428
429             // users
430
431             $m->connect('api/users/show.:format',
432                         array('action' => 'ApiUserShow',
433                               'format' => '(xml|json)'));
434
435             $m->connect('api/users/show/:id.:format',
436                         array('action' => 'ApiUserShow',
437                               'id' => '[a-zA-Z0-9]+',
438                               'format' => '(xml|json)'));
439
440             // direct messages
441
442             $m->connect('api/direct_messages.:format',
443                         array('action' => 'ApiDirectMessage',
444                               'format' => '(xml|json|rss|atom)'));
445
446             $m->connect('api/direct_messages/sent.:format',
447                         array('action' => 'ApiDirectMessage',
448                               'format' => '(xml|json|rss|atom)',
449                               'sent' => true));
450
451             $m->connect('api/direct_messages/new.:format',
452                         array('action' => 'ApiDirectMessageNew',
453                               'format' => '(xml|json)'));
454
455             // friendships
456
457             $m->connect('api/friendships/show.:format',
458                         array('action' => 'ApiFriendshipsShow',
459                               'format' => '(xml|json)'));
460
461             $m->connect('api/friendships/exists.:format',
462                         array('action' => 'ApiFriendshipsExists',
463                               'format' => '(xml|json)'));
464
465             $m->connect('api/friendships/create.:format',
466                         array('action' => 'ApiFriendshipsCreate',
467                               'format' => '(xml|json)'));
468
469             $m->connect('api/friendships/destroy.:format',
470                         array('action' => 'ApiFriendshipsDestroy',
471                               'format' => '(xml|json)'));
472
473             $m->connect('api/friendships/create/:id.:format',
474                         array('action' => 'ApiFriendshipsCreate',
475                               'id' => '[a-zA-Z0-9]+',
476                               'format' => '(xml|json)'));
477
478             $m->connect('api/friendships/destroy/:id.:format',
479                         array('action' => 'ApiFriendshipsDestroy',
480                               'id' => '[a-zA-Z0-9]+',
481                               'format' => '(xml|json)'));
482
483             // Social graph
484
485             $m->connect('api/friends/ids/:id.:format',
486                         array('action' => 'apiuserfriends',
487                               'ids_only' => true));
488
489             $m->connect('api/followers/ids/:id.:format',
490                         array('action' => 'apiuserfollowers',
491                               'ids_only' => true));
492
493             $m->connect('api/friends/ids.:format',
494                         array('action' => 'apiuserfriends',
495                               'ids_only' => true));
496
497             $m->connect('api/followers/ids.:format',
498                         array('action' => 'apiuserfollowers',
499                               'ids_only' => true));
500
501             // account
502
503             $m->connect('api/account/verify_credentials.:format',
504                         array('action' => 'ApiAccountVerifyCredentials'));
505
506             $m->connect('api/account/update_profile.:format',
507                         array('action' => 'ApiAccountUpdateProfile'));
508
509             $m->connect('api/account/update_profile_image.:format',
510                         array('action' => 'ApiAccountUpdateProfileImage'));
511
512             $m->connect('api/account/update_profile_background_image.:format',
513                         array('action' => 'ApiAccountUpdateProfileBackgroundImage'));
514
515             $m->connect('api/account/update_profile_colors.:format',
516                         array('action' => 'ApiAccountUpdateProfileColors'));
517
518             $m->connect('api/account/update_delivery_device.:format',
519                         array('action' => 'ApiAccountUpdateDeliveryDevice'));
520
521             // special case where verify_credentials is called w/out a format
522
523             $m->connect('api/account/verify_credentials',
524                         array('action' => 'ApiAccountVerifyCredentials'));
525
526             $m->connect('api/account/rate_limit_status.:format',
527                         array('action' => 'ApiAccountRateLimitStatus'));
528
529             // favorites
530
531             $m->connect('api/favorites.:format',
532                         array('action' => 'ApiTimelineFavorites',
533                               'format' => '(xml|json|rss|atom)'));
534
535             $m->connect('api/favorites/:id.:format',
536                         array('action' => 'ApiTimelineFavorites',
537                               'id' => '[a-zA-Z0-9]+',
538                               'format' => '(xmljson|rss|atom)'));
539
540             $m->connect('api/favorites/create/:id.:format',
541                         array('action' => 'ApiFavoriteCreate',
542                               'id' => '[a-zA-Z0-9]+',
543                               'format' => '(xml|json)'));
544
545             $m->connect('api/favorites/destroy/:id.:format',
546                         array('action' => 'ApiFavoriteDestroy',
547                               'id' => '[a-zA-Z0-9]+',
548                               'format' => '(xml|json)'));
549             // blocks
550
551             $m->connect('api/blocks/create/:id.:format',
552                         array('action' => 'ApiBlockCreate',
553                               'id' => '[a-zA-Z0-9]+',
554                               'format' => '(xml|json)'));
555
556             $m->connect('api/blocks/destroy/:id.:format',
557                         array('action' => 'ApiBlockDestroy',
558                               'id' => '[a-zA-Z0-9]+',
559                               'format' => '(xml|json)'));
560             // help
561
562             $m->connect('api/help/test.:format',
563                         array('action' => 'ApiHelpTest',
564                               'format' => '(xml|json)'));
565
566             // statusnet
567
568             $m->connect('api/statusnet/version.:format',
569                         array('action' => 'ApiStatusnetVersion',
570                               'format' => '(xml|json)'));
571
572             $m->connect('api/statusnet/config.:format',
573                         array('action' => 'ApiStatusnetConfig',
574                               'format' => '(xml|json)'));
575
576             // For older methods, we provide "laconica" base action
577
578             $m->connect('api/laconica/version.:format',
579                         array('action' => 'ApiStatusnetVersion',
580                               'format' => '(xml|json)'));
581
582             $m->connect('api/laconica/config.:format',
583                         array('action' => 'ApiStatusnetConfig',
584                               'format' => '(xml|json)'));
585
586             // Groups and tags are newer than 0.8.1 so no backward-compatibility
587             // necessary
588
589             // Groups
590             //'list' has to be handled differently, as php will not allow a method to be named 'list'
591
592             $m->connect('api/statusnet/groups/timeline/:id.:format',
593                         array('action' => 'ApiTimelineGroup',
594                               'id' => '[a-zA-Z0-9]+',
595                               'format' => '(xmljson|rss|atom)'));
596
597             $m->connect('api/statusnet/groups/show.:format',
598                         array('action' => 'ApiGroupShow',
599                               'format' => '(xml|json)'));
600
601             $m->connect('api/statusnet/groups/show/:id.:format',
602                         array('action' => 'ApiGroupShow',
603                               'id' => '[a-zA-Z0-9]+',
604                               'format' => '(xml|json)'));
605
606             $m->connect('api/statusnet/groups/join.:format',
607                         array('action' => 'ApiGroupJoin',
608                               'id' => '[a-zA-Z0-9]+',
609                               'format' => '(xml|json)'));
610
611             $m->connect('api/statusnet/groups/join/:id.:format',
612                         array('action' => 'ApiGroupJoin',
613                               'format' => '(xml|json)'));
614
615             $m->connect('api/statusnet/groups/leave.:format',
616                         array('action' => 'ApiGroupLeave',
617                               'id' => '[a-zA-Z0-9]+',
618                               'format' => '(xml|json)'));
619
620             $m->connect('api/statusnet/groups/leave/:id.:format',
621                         array('action' => 'ApiGroupLeave',
622                               'format' => '(xml|json)'));
623
624             $m->connect('api/statusnet/groups/is_member.:format',
625                         array('action' => 'ApiGroupIsMember',
626                               'format' => '(xml|json)'));
627
628             $m->connect('api/statusnet/groups/list.:format',
629                         array('action' => 'ApiGroupList',
630                               'format' => '(xml|json|rss|atom)'));
631
632             $m->connect('api/statusnet/groups/list/:id.:format',
633                         array('action' => 'ApiGroupList',
634                               'id' => '[a-zA-Z0-9]+',
635                               'format' => '(xml|json|rss|atom)'));
636
637             $m->connect('api/statusnet/groups/list_all.:format',
638                         array('action' => 'ApiGroupListAll',
639                               'format' => '(xml|json|rss|atom)'));
640
641             $m->connect('api/statusnet/groups/membership.:format',
642                         array('action' => 'ApiGroupMembership',
643                               'format' => '(xml|json)'));
644
645             $m->connect('api/statusnet/groups/membership/:id.:format',
646                         array('action' => 'ApiGroupMembership',
647                               'id' => '[a-zA-Z0-9]+',
648                               'format' => '(xml|json)'));
649
650             $m->connect('api/statusnet/groups/create.:format',
651                         array('action' => 'ApiGroupCreate',
652                               'format' => '(xml|json)'));
653
654             $m->connect('api/statusnet/groups/update/:id.:format',
655                         array('action' => 'ApiGroupProfileUpdate',
656                               'id' => '[a-zA-Z0-9]+',
657                               'format' => '(xml|json)'));
658
659             // Tags
660             $m->connect('api/statusnet/tags/timeline/:tag.:format',
661                         array('action' => 'ApiTimelineTag',
662                               'format' => '(xmljson|rss|atom)'));
663
664             // media related
665             $m->connect(
666                 'api/statusnet/media/upload',
667                 array('action' => 'ApiMediaUpload')
668             );
669
670             // search
671             $m->connect('api/search.atom', array('action' => 'twitapisearchatom'));
672             $m->connect('api/search.json', array('action' => 'twitapisearchjson'));
673             $m->connect('api/trends.json', array('action' => 'twitapitrends'));
674
675             $m->connect('api/oauth/request_token',
676                         array('action' => 'apioauthrequesttoken'));
677
678             $m->connect('api/oauth/access_token',
679                         array('action' => 'apioauthaccesstoken'));
680
681             $m->connect('api/oauth/authorize',
682                         array('action' => 'apioauthauthorize'));
683
684             // Admin
685
686             $m->connect('admin/site', array('action' => 'siteadminpanel'));
687             $m->connect('admin/design', array('action' => 'designadminpanel'));
688             $m->connect('admin/user', array('action' => 'useradminpanel'));
689                 $m->connect('admin/access', array('action' => 'accessadminpanel'));
690             $m->connect('admin/paths', array('action' => 'pathsadminpanel'));
691             $m->connect('admin/sessions', array('action' => 'sessionsadminpanel'));
692             $m->connect('admin/sitenotice', array('action' => 'sitenoticeadminpanel'));
693             $m->connect('admin/snapshot', array('action' => 'snapshotadminpanel'));
694             $m->connect('admin/plugins', array('action' => 'pluginsadminpanel'));
695             $m->connect('admin/plugins/enable/:plugin',
696                         array('action' => 'pluginenable'),
697                         array('plugin' => '[A-Za-z0-9_]+'));
698             $m->connect('admin/plugins/disable/:plugin',
699                         array('action' => 'plugindisable'),
700                         array('plugin' => '[A-Za-z0-9_]+'));
701
702             $m->connect('getfile/:filename',
703                         array('action' => 'getfile'),
704                         array('filename' => '[A-Za-z0-9._-]+'));
705
706             // In the "root"
707
708             if (common_config('singleuser', 'enabled')) {
709
710                 $user = User::siteOwner();
711
712                 if (!empty($user)) {
713                     $nickname = $user->nickname;
714                 } else {
715                     $nickname = common_config('singleuser', 'nickname');
716                     if (empty($nickname)) {
717                         throw new ServerException(_("No single user defined for single-user mode."));
718                     }
719                 }
720
721                 foreach (array('subscriptions', 'subscribers',
722                                'all', 'foaf', 'xrds',
723                                'replies', 'microsummary', 'hcard') as $a) {
724                     $m->connect($a,
725                                 array('action' => $a,
726                                       'nickname' => $nickname));
727                 }
728
729                 foreach (array('subscriptions', 'subscribers') as $a) {
730                     $m->connect($a.'/:tag',
731                                 array('action' => $a,
732                                       'nickname' => $nickname),
733                                 array('tag' => '[a-zA-Z0-9]+'));
734                 }
735
736                 foreach (array('rss', 'groups') as $a) {
737                     $m->connect($a,
738                                 array('action' => 'user'.$a,
739                                       'nickname' => $nickname));
740                 }
741
742                 foreach (array('all', 'replies', 'favorites') as $a) {
743                     $m->connect($a.'/rss',
744                                 array('action' => $a.'rss',
745                                       'nickname' => $nickname));
746                 }
747
748                 $m->connect('favorites',
749                             array('action' => 'showfavorites',
750                                   'nickname' => $nickname));
751
752                 $m->connect('avatar/:size',
753                             array('action' => 'avatarbynickname',
754                                   'nickname' => $nickname),
755                             array('size' => '(original|96|48|24)'));
756
757                 $m->connect('tag/:tag/rss',
758                             array('action' => 'userrss',
759                                   'nickname' => $nickname),
760                             array('tag' => '[a-zA-Z0-9]+'));
761
762                 $m->connect('tag/:tag',
763                             array('action' => 'showstream',
764                                   'nickname' => $nickname),
765                             array('tag' => '[a-zA-Z0-9]+'));
766
767                 $m->connect('rsd.xml',
768                             array('action' => 'rsd',
769                                   'nickname' => $nickname));
770
771                 $m->connect('',
772                             array('action' => 'showstream',
773                                   'nickname' => $nickname));
774
775             } else {
776
777                 $m->connect('', array('action' => 'public'));
778                 $m->connect('rss', array('action' => 'publicrss'));
779                 $m->connect('featuredrss', array('action' => 'featuredrss'));
780                 $m->connect('favoritedrss', array('action' => 'favoritedrss'));
781                 $m->connect('featured/', array('action' => 'featured'));
782                 $m->connect('featured', array('action' => 'featured'));
783                 $m->connect('favorited/', array('action' => 'favorited'));
784                 $m->connect('favorited', array('action' => 'favorited'));
785                 $m->connect('rsd.xml', array('action' => 'rsd'));
786
787                 foreach (array('subscriptions', 'subscribers',
788                                'nudge', 'all', 'foaf', 'xrds',
789                                'replies', 'inbox', 'outbox', 'microsummary', 'hcard') as $a) {
790                     $m->connect(':nickname/'.$a,
791                                 array('action' => $a),
792                                 array('nickname' => '[a-zA-Z0-9]{1,64}'));
793                 }
794
795                 foreach (array('subscriptions', 'subscribers') as $a) {
796                     $m->connect(':nickname/'.$a.'/:tag',
797                                 array('action' => $a),
798                                 array('tag' => '[a-zA-Z0-9]+',
799                                       'nickname' => '[a-zA-Z0-9]{1,64}'));
800                 }
801
802                 foreach (array('rss', 'groups') as $a) {
803                     $m->connect(':nickname/'.$a,
804                                 array('action' => 'user'.$a),
805                                 array('nickname' => '[a-zA-Z0-9]{1,64}'));
806                 }
807
808                 foreach (array('all', 'replies', 'favorites') as $a) {
809                     $m->connect(':nickname/'.$a.'/rss',
810                                 array('action' => $a.'rss'),
811                                 array('nickname' => '[a-zA-Z0-9]{1,64}'));
812                 }
813
814                 $m->connect(':nickname/favorites',
815                             array('action' => 'showfavorites'),
816                             array('nickname' => '[a-zA-Z0-9]{1,64}'));
817
818                 $m->connect(':nickname/avatar/:size',
819                             array('action' => 'avatarbynickname'),
820                             array('size' => '(original|96|48|24)',
821                                   'nickname' => '[a-zA-Z0-9]{1,64}'));
822
823                 $m->connect(':nickname/tag/:tag/rss',
824                             array('action' => 'userrss'),
825                             array('nickname' => '[a-zA-Z0-9]{1,64}'),
826                             array('tag' => '[a-zA-Z0-9]+'));
827
828                 $m->connect(':nickname/tag/:tag',
829                             array('action' => 'showstream'),
830                             array('nickname' => '[a-zA-Z0-9]{1,64}'),
831                             array('tag' => '[a-zA-Z0-9]+'));
832
833                 $m->connect(':nickname/rsd.xml',
834                             array('action' => 'rsd'),
835                             array('nickname' => '[a-zA-Z0-9]{1,64}'));
836
837                 $m->connect(':nickname',
838                             array('action' => 'showstream'),
839                             array('nickname' => '[a-zA-Z0-9]{1,64}'));
840             }
841
842             // user stuff
843
844             Event::handle('RouterInitialized', array($m));
845         }
846
847         return $m;
848     }
849
850     function map($path)
851     {
852         try {
853             $match = $this->m->match($path);
854         } catch (Net_URL_Mapper_InvalidException $e) {
855             common_log(LOG_ERR, "Problem getting route for $path - " .
856                        $e->getMessage());
857             $cac = new ClientErrorAction("Page not found.", 404);
858             $cac->showPage();
859         }
860
861         return $match;
862     }
863
864     function build($action, $args=null, $params=null, $fragment=null)
865     {
866         $action_arg = array('action' => $action);
867
868         if ($args) {
869             $args = array_merge($action_arg, $args);
870         } else {
871             $args = $action_arg;
872         }
873
874         $url = $this->m->generate($args, $params, $fragment);
875
876         // Due to a bug in the Net_URL_Mapper code, the returned URL may
877         // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
878         // repair that here rather than modifying the upstream code...
879
880         $qpos = strpos($url, '?');
881         if ($qpos !== false) {
882             $url = substr($url, 0, $qpos+1) .
883               str_replace('?', '&', substr($url, $qpos+1));
884         }
885         return $url;
886     }
887 }