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