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