]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/router.php
Use Nickname::DISPLAY_FMT instead of manual regex fragments in router setup for nickn...
[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             $m->connect('main/xrd',
155                         array('action' => 'userxrd'));
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' => Nickname::DISPLAY_FMT));
227             $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
228                         array('action' => 'newnotice'),
229                         array('replyto' => Nickname::DISPLAY_FMT),
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' => Nickname::DISPLAY_FMT));
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' => Nickname::DISPLAY_FMT));
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' => Nickname::DISPLAY_FMT));
294             }
295
296             $m->connect('group/:nickname/foaf',
297                         array('action' => 'foafgroup'),
298                         array('nickname' => Nickname::DISPLAY_FMT));
299
300             $m->connect('group/:nickname/blocked',
301                         array('action' => 'blockedfromgroup'),
302                         array('nickname' => Nickname::DISPLAY_FMT));
303
304             $m->connect('group/:nickname/makeadmin',
305                         array('action' => 'makeadmin'),
306                         array('nickname' => Nickname::DISPLAY_FMT));
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' => Nickname::DISPLAY_FMT));
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/statuses/public_timeline.:format',
326                         array('action' => 'ApiTimelinePublic',
327                               'format' => '(xml|json|rss|atom)'));
328
329             $m->connect('api/statuses/friends_timeline.:format',
330                         array('action' => 'ApiTimelineFriends',
331                               'format' => '(xml|json|rss|atom)'));
332
333             $m->connect('api/statuses/friends_timeline/:id.:format',
334                         array('action' => 'ApiTimelineFriends',
335                               'id' => Nickname::DISPLAY_FMT,
336                               'format' => '(xml|json|rss|atom)'));
337
338             $m->connect('api/statuses/home_timeline.:format',
339                         array('action' => 'ApiTimelineHome',
340                               'format' => '(xml|json|rss|atom)'));
341
342             $m->connect('api/statuses/home_timeline/:id.:format',
343                         array('action' => 'ApiTimelineHome',
344                               'id' => Nickname::DISPLAY_FMT,
345                               'format' => '(xml|json|rss|atom)'));
346
347             $m->connect('api/statuses/user_timeline.:format',
348                         array('action' => 'ApiTimelineUser',
349                               'format' => '(xml|json|rss|atom)'));
350
351             $m->connect('api/statuses/user_timeline/:id.:format',
352                         array('action' => 'ApiTimelineUser',
353                               'id' => Nickname::DISPLAY_FMT,
354                               'format' => '(xml|json|rss|atom)'));
355
356             $m->connect('api/statuses/mentions.:format',
357                         array('action' => 'ApiTimelineMentions',
358                               'format' => '(xml|json|rss|atom)'));
359
360             $m->connect('api/statuses/mentions/:id.:format',
361                         array('action' => 'ApiTimelineMentions',
362                               'id' => Nickname::DISPLAY_FMT,
363                               'format' => '(xml|json|rss|atom)'));
364
365             $m->connect('api/statuses/replies.:format',
366                         array('action' => 'ApiTimelineMentions',
367                               'format' => '(xml|json|rss|atom)'));
368
369             $m->connect('api/statuses/replies/:id.:format',
370                         array('action' => 'ApiTimelineMentions',
371                               'id' => Nickname::DISPLAY_FMT,
372                               'format' => '(xml|json|rss|atom)'));
373
374             $m->connect('api/statuses/retweeted_by_me.:format',
375                         array('action' => 'ApiTimelineRetweetedByMe',
376                               'format' => '(xml|json|atom)'));
377
378             $m->connect('api/statuses/retweeted_to_me.:format',
379                         array('action' => 'ApiTimelineRetweetedToMe',
380                               'format' => '(xml|json|atom)'));
381
382             $m->connect('api/statuses/retweets_of_me.:format',
383                         array('action' => 'ApiTimelineRetweetsOfMe',
384                               'format' => '(xml|json|atom)'));
385
386             $m->connect('api/statuses/friends.:format',
387                         array('action' => 'ApiUserFriends',
388                               'format' => '(xml|json)'));
389
390             $m->connect('api/statuses/friends/:id.:format',
391                         array('action' => 'ApiUserFriends',
392                               'id' => Nickname::DISPLAY_FMT,
393                               'format' => '(xml|json)'));
394
395             $m->connect('api/statuses/followers.:format',
396                         array('action' => 'ApiUserFollowers',
397                               'format' => '(xml|json)'));
398
399             $m->connect('api/statuses/followers/:id.:format',
400                         array('action' => 'ApiUserFollowers',
401                               'id' => Nickname::DISPLAY_FMT,
402                               'format' => '(xml|json)'));
403
404             $m->connect('api/statuses/show.:format',
405                         array('action' => 'ApiStatusesShow',
406                               'format' => '(xml|json|atom)'));
407
408             $m->connect('api/statuses/show/:id.:format',
409                         array('action' => 'ApiStatusesShow',
410                               'id' => '[0-9]+',
411                               'format' => '(xml|json|atom)'));
412
413             $m->connect('api/statuses/update.:format',
414                         array('action' => 'ApiStatusesUpdate',
415                               'format' => '(xml|json)'));
416
417             $m->connect('api/statuses/destroy.:format',
418                         array('action' => 'ApiStatusesDestroy',
419                               'format' => '(xml|json)'));
420
421             $m->connect('api/statuses/destroy/:id.:format',
422                         array('action' => 'ApiStatusesDestroy',
423                               'id' => '[0-9]+',
424                               'format' => '(xml|json)'));
425
426             $m->connect('api/statuses/retweet/:id.:format',
427                         array('action' => 'ApiStatusesRetweet',
428                               'id' => '[0-9]+',
429                               'format' => '(xml|json)'));
430
431             $m->connect('api/statuses/retweets/:id.:format',
432                         array('action' => 'ApiStatusesRetweets',
433                               'id' => '[0-9]+',
434                               'format' => '(xml|json)'));
435
436             // users
437
438             $m->connect('api/users/show.:format',
439                         array('action' => 'ApiUserShow',
440                               'format' => '(xml|json)'));
441
442             $m->connect('api/users/show/:id.:format',
443                         array('action' => 'ApiUserShow',
444                               'id' => Nickname::DISPLAY_FMT,
445                               'format' => '(xml|json)'));
446
447             // direct messages
448
449             $m->connect('api/direct_messages.:format',
450                         array('action' => 'ApiDirectMessage',
451                               'format' => '(xml|json|rss|atom)'));
452
453             $m->connect('api/direct_messages/sent.:format',
454                         array('action' => 'ApiDirectMessage',
455                               'format' => '(xml|json|rss|atom)',
456                               'sent' => true));
457
458             $m->connect('api/direct_messages/new.:format',
459                         array('action' => 'ApiDirectMessageNew',
460                               'format' => '(xml|json)'));
461
462             // friendships
463
464             $m->connect('api/friendships/show.:format',
465                         array('action' => 'ApiFriendshipsShow',
466                               'format' => '(xml|json)'));
467
468             $m->connect('api/friendships/exists.:format',
469                         array('action' => 'ApiFriendshipsExists',
470                               'format' => '(xml|json)'));
471
472             $m->connect('api/friendships/create.:format',
473                         array('action' => 'ApiFriendshipsCreate',
474                               'format' => '(xml|json)'));
475
476             $m->connect('api/friendships/destroy.:format',
477                         array('action' => 'ApiFriendshipsDestroy',
478                               'format' => '(xml|json)'));
479
480             $m->connect('api/friendships/create/:id.:format',
481                         array('action' => 'ApiFriendshipsCreate',
482                               'id' => Nickname::DISPLAY_FMT,
483                               'format' => '(xml|json)'));
484
485             $m->connect('api/friendships/destroy/:id.:format',
486                         array('action' => 'ApiFriendshipsDestroy',
487                               'id' => Nickname::DISPLAY_FMT,
488                               'format' => '(xml|json)'));
489
490             // Social graph
491
492             $m->connect('api/friends/ids/:id.:format',
493                         array('action' => 'ApiUserFriends',
494                               'ids_only' => true));
495
496             $m->connect('api/followers/ids/:id.:format',
497                         array('action' => 'ApiUserFollowers',
498                               'ids_only' => true));
499
500             $m->connect('api/friends/ids.:format',
501                         array('action' => 'ApiUserFriends',
502                               'ids_only' => true));
503
504             $m->connect('api/followers/ids.:format',
505                         array('action' => 'ApiUserFollowers',
506                               'ids_only' => true));
507
508             // account
509
510             $m->connect('api/account/verify_credentials.:format',
511                         array('action' => 'ApiAccountVerifyCredentials'));
512
513             $m->connect('api/account/update_profile.:format',
514                         array('action' => 'ApiAccountUpdateProfile'));
515
516             $m->connect('api/account/update_profile_image.:format',
517                         array('action' => 'ApiAccountUpdateProfileImage'));
518
519             $m->connect('api/account/update_profile_background_image.:format',
520                         array('action' => 'ApiAccountUpdateProfileBackgroundImage'));
521
522             $m->connect('api/account/update_profile_colors.:format',
523                         array('action' => 'ApiAccountUpdateProfileColors'));
524
525             $m->connect('api/account/update_delivery_device.:format',
526                         array('action' => 'ApiAccountUpdateDeliveryDevice'));
527
528             // special case where verify_credentials is called w/out a format
529
530             $m->connect('api/account/verify_credentials',
531                         array('action' => 'ApiAccountVerifyCredentials'));
532
533             $m->connect('api/account/rate_limit_status.:format',
534                         array('action' => 'ApiAccountRateLimitStatus'));
535
536             // favorites
537
538             $m->connect('api/favorites.:format',
539                         array('action' => 'ApiTimelineFavorites',
540                               'format' => '(xml|json|rss|atom)'));
541
542             $m->connect('api/favorites/:id.:format',
543                         array('action' => 'ApiTimelineFavorites',
544                               'id' => Nickname::DISPLAY_FMT,
545                               'format' => '(xml|json|rss|atom)'));
546
547             $m->connect('api/favorites/create/:id.:format',
548                         array('action' => 'ApiFavoriteCreate',
549                               'id' => Nickname::DISPLAY_FMT,
550                               'format' => '(xml|json)'));
551
552             $m->connect('api/favorites/destroy/:id.:format',
553                         array('action' => 'ApiFavoriteDestroy',
554                               'id' => Nickname::DISPLAY_FMT,
555                               'format' => '(xml|json)'));
556             // blocks
557
558             $m->connect('api/blocks/create.:format',
559                         array('action' => 'ApiBlockCreate',
560                               'format' => '(xml|json)'));
561
562             $m->connect('api/blocks/create/:id.:format',
563                         array('action' => 'ApiBlockCreate',
564                               'id' => Nickname::DISPLAY_FMT,
565                               'format' => '(xml|json)'));
566
567             $m->connect('api/blocks/destroy.:format',
568                         array('action' => 'ApiBlockDestroy',
569                               'format' => '(xml|json)'));
570
571             $m->connect('api/blocks/destroy/:id.:format',
572                         array('action' => 'ApiBlockDestroy',
573                               'id' => Nickname::DISPLAY_FMT,
574                               'format' => '(xml|json)'));
575             // help
576
577             $m->connect('api/help/test.:format',
578                         array('action' => 'ApiHelpTest',
579                               'format' => '(xml|json)'));
580
581             // statusnet
582
583             $m->connect('api/statusnet/version.:format',
584                         array('action' => 'ApiStatusnetVersion',
585                               'format' => '(xml|json)'));
586
587             $m->connect('api/statusnet/config.:format',
588                         array('action' => 'ApiStatusnetConfig',
589                               'format' => '(xml|json)'));
590
591             // For older methods, we provide "laconica" base action
592
593             $m->connect('api/laconica/version.:format',
594                         array('action' => 'ApiStatusnetVersion',
595                               'format' => '(xml|json)'));
596
597             $m->connect('api/laconica/config.:format',
598                         array('action' => 'ApiStatusnetConfig',
599                               'format' => '(xml|json)'));
600
601             // Groups and tags are newer than 0.8.1 so no backward-compatibility
602             // necessary
603
604             // Groups
605             //'list' has to be handled differently, as php will not allow a method to be named 'list'
606
607             $m->connect('api/statusnet/groups/timeline/:id.:format',
608                         array('action' => 'ApiTimelineGroup',
609                               'id' => Nickname::DISPLAY_FMT,
610                               'format' => '(xml|json|rss|atom)'));
611
612             $m->connect('api/statusnet/groups/show.:format',
613                         array('action' => 'ApiGroupShow',
614                               'format' => '(xml|json)'));
615
616             $m->connect('api/statusnet/groups/show/:id.:format',
617                         array('action' => 'ApiGroupShow',
618                               'id' => Nickname::DISPLAY_FMT,
619                               'format' => '(xml|json)'));
620
621             $m->connect('api/statusnet/groups/join.:format',
622                         array('action' => 'ApiGroupJoin',
623                               'id' => Nickname::DISPLAY_FMT,
624                               'format' => '(xml|json)'));
625
626             $m->connect('api/statusnet/groups/join/:id.:format',
627                         array('action' => 'ApiGroupJoin',
628                               'format' => '(xml|json)'));
629
630             $m->connect('api/statusnet/groups/leave.:format',
631                         array('action' => 'ApiGroupLeave',
632                               'id' => Nickname::DISPLAY_FMT,
633                               'format' => '(xml|json)'));
634
635             $m->connect('api/statusnet/groups/leave/:id.:format',
636                         array('action' => 'ApiGroupLeave',
637                               'format' => '(xml|json)'));
638
639             $m->connect('api/statusnet/groups/is_member.:format',
640                         array('action' => 'ApiGroupIsMember',
641                               'format' => '(xml|json)'));
642
643             $m->connect('api/statusnet/groups/list.:format',
644                         array('action' => 'ApiGroupList',
645                               'format' => '(xml|json|rss|atom)'));
646
647             $m->connect('api/statusnet/groups/list/:id.:format',
648                         array('action' => 'ApiGroupList',
649                               'id' => Nickname::DISPLAY_FMT,
650                               'format' => '(xml|json|rss|atom)'));
651
652             $m->connect('api/statusnet/groups/list_all.:format',
653                         array('action' => 'ApiGroupListAll',
654                               'format' => '(xml|json|rss|atom)'));
655
656             $m->connect('api/statusnet/groups/membership.:format',
657                         array('action' => 'ApiGroupMembership',
658                               'format' => '(xml|json)'));
659
660             $m->connect('api/statusnet/groups/membership/:id.:format',
661                         array('action' => 'ApiGroupMembership',
662                               'id' => Nickname::DISPLAY_FMT,
663                               'format' => '(xml|json)'));
664
665             $m->connect('api/statusnet/groups/create.:format',
666                         array('action' => 'ApiGroupCreate',
667                               'format' => '(xml|json)'));
668             // Tags
669             $m->connect('api/statusnet/tags/timeline/:tag.:format',
670                         array('action' => 'ApiTimelineTag',
671                               'format' => '(xml|json|rss|atom)'));
672
673             // media related
674             $m->connect(
675                 'api/statusnet/media/upload',
676                 array('action' => 'ApiMediaUpload')
677             );
678
679             // search
680             $m->connect('api/search.atom', array('action' => 'ApiSearchAtom'));
681             $m->connect('api/search.json', array('action' => 'ApiSearchJSON'));
682             $m->connect('api/trends.json', array('action' => 'ApiTrends'));
683
684             $m->connect('api/oauth/request_token',
685                         array('action' => 'ApiOauthRequestToken'));
686
687             $m->connect('api/oauth/access_token',
688                         array('action' => 'ApiOauthAccessToken'));
689
690             $m->connect('api/oauth/authorize',
691                         array('action' => 'ApiOauthAuthorize'));
692
693             $m->connect('api/statusnet/app/service/:id.xml',
694                         array('action' => 'ApiAtomService',
695                               'id' => Nickname::DISPLAY_FMT));
696
697             $m->connect('api/statusnet/app/service.xml',
698                         array('action' => 'ApiAtomService'));
699
700             // Admin
701
702             $m->connect('admin/site', array('action' => 'siteadminpanel'));
703             $m->connect('admin/design', array('action' => 'designadminpanel'));
704             $m->connect('admin/user', array('action' => 'useradminpanel'));
705                 $m->connect('admin/access', array('action' => 'accessadminpanel'));
706             $m->connect('admin/paths', array('action' => 'pathsadminpanel'));
707             $m->connect('admin/sessions', array('action' => 'sessionsadminpanel'));
708             $m->connect('admin/sitenotice', array('action' => 'sitenoticeadminpanel'));
709             $m->connect('admin/snapshot', array('action' => 'snapshotadminpanel'));
710             $m->connect('admin/license', array('action' => 'licenseadminpanel'));
711
712             $m->connect('getfile/:filename',
713                         array('action' => 'getfile'),
714                         array('filename' => '[A-Za-z0-9._-]+'));
715
716             // In the "root"
717
718             if (common_config('singleuser', 'enabled')) {
719
720                 $user = User::singleUser();
721                 $nickname = $user->nickname;
722
723                 foreach (array('subscriptions', 'subscribers',
724                                'all', 'foaf', 'xrds',
725                                'replies', 'microsummary', 'hcard') as $a) {
726                     $m->connect($a,
727                                 array('action' => $a,
728                                       'nickname' => $nickname));
729                 }
730
731                 foreach (array('subscriptions', 'subscribers') as $a) {
732                     $m->connect($a.'/:tag',
733                                 array('action' => $a,
734                                       'nickname' => $nickname),
735                                 array('tag' => '[a-zA-Z0-9]+'));
736                 }
737
738                 foreach (array('rss', 'groups') as $a) {
739                     $m->connect($a,
740                                 array('action' => 'user'.$a,
741                                       'nickname' => $nickname));
742                 }
743
744                 foreach (array('all', 'replies', 'favorites') as $a) {
745                     $m->connect($a.'/rss',
746                                 array('action' => $a.'rss',
747                                       'nickname' => $nickname));
748                 }
749
750                 $m->connect('favorites',
751                             array('action' => 'showfavorites',
752                                   'nickname' => $nickname));
753
754                 $m->connect('avatar/:size',
755                             array('action' => 'avatarbynickname',
756                                   'nickname' => $nickname),
757                             array('size' => '(original|96|48|24)'));
758
759                 $m->connect('tag/:tag/rss',
760                             array('action' => 'userrss',
761                                   'nickname' => $nickname),
762                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
763
764                 $m->connect('tag/:tag',
765                             array('action' => 'showstream',
766                                   'nickname' => $nickname),
767                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
768
769                 $m->connect('rsd.xml',
770                             array('action' => 'rsd',
771                                   'nickname' => $nickname));
772
773                 $m->connect('',
774                             array('action' => 'showstream',
775                                   'nickname' => $nickname));
776             } else {
777                 $m->connect('', array('action' => 'public'));
778                 $m->connect('rss', array('action' => 'publicrss'));
779                 $m->connect('featuredrss', array('action' => 'featuredrss'));
780                 $m->connect('favoritedrss', array('action' => 'favoritedrss'));
781                 $m->connect('featured/', array('action' => 'featured'));
782                 $m->connect('featured', array('action' => 'featured'));
783                 $m->connect('favorited/', array('action' => 'favorited'));
784                 $m->connect('favorited', array('action' => 'favorited'));
785                 $m->connect('rsd.xml', array('action' => 'rsd'));
786
787                 foreach (array('subscriptions', 'subscribers',
788                                'nudge', 'all', 'foaf', 'xrds',
789                                'replies', 'inbox', 'outbox', 'microsummary', 'hcard') as $a) {
790                     $m->connect(':nickname/'.$a,
791                                 array('action' => $a),
792                                 array('nickname' => Nickname::DISPLAY_FMT));
793                 }
794
795                 foreach (array('subscriptions', 'subscribers') as $a) {
796                     $m->connect(':nickname/'.$a.'/:tag',
797                                 array('action' => $a),
798                                 array('tag' => '[a-zA-Z0-9]+',
799                                       'nickname' => Nickname::DISPLAY_FMT));
800                 }
801
802                 foreach (array('rss', 'groups') as $a) {
803                     $m->connect(':nickname/'.$a,
804                                 array('action' => 'user'.$a),
805                                 array('nickname' => Nickname::DISPLAY_FMT));
806                 }
807
808                 foreach (array('all', 'replies', 'favorites') as $a) {
809                     $m->connect(':nickname/'.$a.'/rss',
810                                 array('action' => $a.'rss'),
811                                 array('nickname' => Nickname::DISPLAY_FMT));
812                 }
813
814                 $m->connect(':nickname/favorites',
815                             array('action' => 'showfavorites'),
816                             array('nickname' => Nickname::DISPLAY_FMT));
817
818                 $m->connect(':nickname/avatar/:size',
819                             array('action' => 'avatarbynickname'),
820                             array('size' => '(original|96|48|24)',
821                                   'nickname' => Nickname::DISPLAY_FMT));
822
823                 $m->connect(':nickname/tag/:tag/rss',
824                             array('action' => 'userrss'),
825                             array('nickname' => Nickname::DISPLAY_FMT),
826                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
827
828                 $m->connect(':nickname/tag/:tag',
829                             array('action' => 'showstream'),
830                             array('nickname' => Nickname::DISPLAY_FMT),
831                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
832
833                 $m->connect(':nickname/rsd.xml',
834                             array('action' => 'rsd'),
835                             array('nickname' => Nickname::DISPLAY_FMT));
836
837                 $m->connect(':nickname',
838                             array('action' => 'showstream'),
839                             array('nickname' => Nickname::DISPLAY_FMT));
840             }
841
842             // user stuff
843
844             Event::handle('RouterInitialized', array($m));
845         }
846
847         return $m;
848     }
849
850     function map($path)
851     {
852         try {
853             $match = $this->m->match($path);
854         } catch (Net_URL_Mapper_InvalidException $e) {
855             common_log(LOG_ERR, "Problem getting route for $path - " .
856                        $e->getMessage());
857             // TRANS: Client error on action trying to visit a non-existing page.
858             $cac = new ClientErrorAction(_('Page not found.'), 404);
859             $cac->showPage();
860         }
861
862         return $match;
863     }
864
865     function build($action, $args=null, $params=null, $fragment=null)
866     {
867         $action_arg = array('action' => $action);
868
869         if ($args) {
870             $args = array_merge($action_arg, $args);
871         } else {
872             $args = $action_arg;
873         }
874
875         $url = $this->m->generate($args, $params, $fragment);
876
877         // Due to a bug in the Net_URL_Mapper code, the returned URL may
878         // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
879         // repair that here rather than modifying the upstream code...
880
881         $qpos = strpos($url, '?');
882         if ($qpos !== false) {
883             $url = substr($url, 0, $qpos+1) .
884               str_replace('?', '&', substr($url, $qpos+1));
885
886             // @fixme this is a hacky workaround for http_build_query in the
887             // lower-level code and bad configs that set the default separator
888             // to &amp; instead of &. Encoded &s in parameters will not be
889             // affected.
890             $url = substr($url, 0, $qpos+1) .
891               str_replace('&amp;', '&', substr($url, $qpos+1));
892
893         }
894
895         return $url;
896     }
897 }