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