]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/router.php
/main/all will give a network-wide public stream
[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 /**
35  * URL Router
36  *
37  * Cheap wrapper around Net_URL_Mapper
38  *
39  * @category URL
40  * @package  StatusNet
41  * @author   Evan Prodromou <evan@status.net>
42  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
43  * @link     http://status.net/
44  */
45 class Router
46 {
47     var $m = null;
48     static $inst = null;
49
50     const REGEX_TAG = '[^\/]+'; // [\pL\pN_\-\.]{1,64} better if we can do unicode regexes
51
52     static function get()
53     {
54         if (!Router::$inst) {
55             Router::$inst = new Router();
56         }
57         return Router::$inst;
58     }
59
60     /**
61      * Clear the global singleton instance for this class.
62      * Needed to ensure reset when switching site configurations.
63      */
64     static function clear()
65     {
66         Router::$inst = null;
67     }
68
69     function __construct()
70     {
71         if (empty($this->m)) {
72             $this->m = $this->initialize();
73         }
74     }
75
76     /**
77      * Create a unique hashkey for the router.
78      *
79      * The router's url map can change based on the version of the software
80      * you're running and the plugins that are enabled. To avoid having bad routes
81      * get stuck in the cache, the key includes a list of plugins and the software
82      * version.
83      * 
84     * There can still be problems with a) differences in versions of the plugins and
85      * b) people running code between official versions, but these tend to be more
86      * sophisticated users who can grok what's going on and clear their caches.
87      *
88      * @return string cache key string that should uniquely identify a router
89      */
90
91     static function cacheKey()
92     {
93         $parts = array('router');
94
95         // Many router paths depend on this setting.
96         if (common_config('singleuser', 'enabled')) {
97             $parts[] = '1user';
98         } else {
99             $parts[] = 'multi';
100         }
101
102         return Cache::codeKey(implode(':', $parts));
103     }
104
105     function initialize()
106     {
107         $m = new URLMapper();
108
109         if (Event::handle('StartInitializeRouter', array(&$m))) {
110
111             $m->connect('robots.txt', array('action' => 'robotstxt'));
112
113             $m->connect('opensearch/people', array('action' => 'opensearch',
114                                                    'type' => 'people'));
115             $m->connect('opensearch/notice', array('action' => 'opensearch',
116                                                    'type' => 'notice'));
117
118             // docs
119
120             $m->connect('doc/:title', array('action' => 'doc'));
121
122             $m->connect('main/otp/:user_id/:token',
123                         array('action' => 'otp'),
124                         array('user_id' => '[0-9]+',
125                               'token' => '.+'));
126
127             // these take a code; before the main part
128
129             foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) {
130                 $m->connect('main/'.$c.'/:code', array('action' => $c));
131             }
132
133             // Also need a block variant accepting ID on URL for mail links
134             $m->connect('main/block/:profileid',
135                         array('action' => 'block'),
136                         array('profileid' => '[0-9]+'));
137
138             $m->connect('main/sup/:seconds', array('action' => 'sup'),
139                         array('seconds' => '[0-9]+'));
140
141             // main stuff is repetitive
142
143             $main = array('login', 'logout', 'register', 'subscribe',
144                           'unsubscribe', 'cancelsubscription', 'approvesub',
145                           'confirmaddress', 'recoverpassword',
146                           'invite', 'sup',
147                           'block', 'unblock', 'subedit',
148                           'groupblock', 'groupunblock',
149                           'sandbox', 'unsandbox',
150                           'silence', 'unsilence',
151                           'grantrole', 'revokerole',
152                           'repeat',
153                           'deleteuser',
154                           'geocode',
155                           'version',
156                           'backupaccount',
157                           'deleteaccount',
158                           'restoreaccount',
159                           'top',
160             );
161
162             foreach ($main as $a) {
163                 $m->connect('main/'.$a, array('action' => $a));
164             }
165
166             $m->connect('main/public', array('action' => 'public'));
167             $m->connect('main/all', array('action' => 'networkpublic'));
168
169             $m->connect('main/tagprofile/:id', array('action' => 'tagprofile'),
170                                                array('id' => '[0-9]+'));
171
172             $m->connect('main/tagprofile', array('action' => 'tagprofile'));
173
174             $m->connect('main/xrds',
175                         array('action' => 'publicxrds'));
176
177             // settings
178
179             foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections',
180                            'oauthapps', 'email', 'sms', 'url') as $s) {
181                 $m->connect('settings/'.$s, array('action' => $s.'settings'));
182             }
183
184             if (common_config('oldschool', 'enabled')) {
185                 $m->connect('settings/oldschool', array('action' => 'oldschoolsettings'));
186             }
187
188             $m->connect('settings/oauthapps/show/:id',
189                         array('action' => 'showapplication'),
190                         array('id' => '[0-9]+')
191             );
192             $m->connect('settings/oauthapps/new',
193                         array('action' => 'newapplication')
194             );
195             $m->connect('settings/oauthapps/edit/:id',
196                         array('action' => 'editapplication'),
197                         array('id' => '[0-9]+')
198             );
199             $m->connect('settings/oauthapps/delete/:id',
200                         array('action' => 'deleteapplication'),
201                         array('id' => '[0-9]+')
202             );
203
204             // search
205
206             foreach (array('group', 'people', 'notice') as $s) {
207                 $m->connect('search/'.$s.'?q=:q',
208                             array('action' => $s.'search'),
209                             array('q' => '.+'));
210                 $m->connect('search/'.$s, array('action' => $s.'search'));
211             }
212
213             // The second of these is needed to make the link work correctly
214             // when inserted into the page. The first is needed to match the
215             // route on the way in. Seems to be another Net_URL_Mapper bug to me.
216             $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
217                         array('q' => '.+'));
218             $m->connect('search/notice/rss', array('action' => 'noticesearchrss'));
219
220             $m->connect('attachment/:attachment',
221                         array('action' => 'attachment'),
222                         array('attachment' => '[0-9]+'));
223
224             $m->connect('attachment/:attachment/thumbnail',
225                         array('action' => 'attachment_thumbnail'),
226                         array('attachment' => '[0-9]+'));
227
228             $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
229                         array('action' => 'newnotice'),
230                         array('replyto' => Nickname::DISPLAY_FMT),
231                         array('inreplyto' => '[0-9]+'));
232
233             $m->connect('notice/new?replyto=:replyto',
234                         array('action' => 'newnotice'),
235                         array('replyto' => Nickname::DISPLAY_FMT));
236
237             $m->connect('notice/new', array('action' => 'newnotice'));
238
239             $m->connect('notice/:notice',
240                         array('action' => 'shownotice'),
241                         array('notice' => '[0-9]+'));
242
243             $m->connect('notice/delete/:notice',
244                         array('action' => 'deletenotice'),
245                         array('notice' => '[0-9]+'));
246
247             $m->connect('notice/delete', array('action' => 'deletenotice'));
248
249             // conversation
250
251             $m->connect('conversation/:id',
252                         array('action' => 'conversation'),
253                         array('id' => '[0-9]+'));
254
255             $m->connect('user/:id',
256                         array('action' => 'userbyid'),
257                         array('id' => '[0-9]+'));
258
259             if (!common_config('performance', 'high')) {
260                 $m->connect('tags/', array('action' => 'publictagcloud'));
261                 $m->connect('tag/', array('action' => 'publictagcloud'));
262                 $m->connect('tags', array('action' => 'publictagcloud'));
263                 $m->connect('tag', array('action' => 'publictagcloud'));
264             }
265             $m->connect('tag/:tag/rss',
266                         array('action' => 'tagrss'),
267                         array('tag' => self::REGEX_TAG));
268             $m->connect('tag/:tag',
269                         array('action' => 'tag'),
270                         array('tag' => self::REGEX_TAG));
271
272             // groups
273
274             $m->connect('group/new', array('action' => 'newgroup'));
275
276             foreach (array('edit', 'join', 'leave', 'delete', 'cancel', 'approve') as $v) {
277                 $m->connect('group/:nickname/'.$v,
278                             array('action' => $v.'group'),
279                             array('nickname' => Nickname::DISPLAY_FMT));
280                 $m->connect('group/:id/id/'.$v,
281                             array('action' => $v.'group'),
282                             array('id' => '[0-9]+'));
283             }
284
285             foreach (array('members', 'logo', 'rss') as $n) {
286                 $m->connect('group/:nickname/'.$n,
287                             array('action' => 'group'.$n),
288                             array('nickname' => Nickname::DISPLAY_FMT));
289             }
290
291             $m->connect('group/:nickname/foaf',
292                         array('action' => 'foafgroup'),
293                         array('nickname' => Nickname::DISPLAY_FMT));
294
295             $m->connect('group/:nickname/blocked',
296                         array('action' => 'blockedfromgroup'),
297                         array('nickname' => Nickname::DISPLAY_FMT));
298
299             $m->connect('group/:nickname/makeadmin',
300                         array('action' => 'makeadmin'),
301                         array('nickname' => Nickname::DISPLAY_FMT));
302
303             $m->connect('group/:nickname/members/pending',
304                         array('action' => 'groupqueue'),
305                         array('nickname' => Nickname::DISPLAY_FMT));
306
307             $m->connect('group/:id/id',
308                         array('action' => 'groupbyid'),
309                         array('id' => '[0-9]+'));
310
311             $m->connect('group/:nickname',
312                         array('action' => 'showgroup'),
313                         array('nickname' => Nickname::DISPLAY_FMT));
314
315             $m->connect('group/:nickname/',
316                         array('action' => 'showgroup'),
317                         array('nickname' => Nickname::DISPLAY_FMT));
318
319             $m->connect('group/', array('action' => 'groups'));
320             $m->connect('group', array('action' => 'groups'));
321             $m->connect('groups/', array('action' => 'groups'));
322             $m->connect('groups', array('action' => 'groups'));
323
324             // Twitter-compatible API
325
326             // statuses API
327
328             $m->connect('api',
329                         array('action' => 'Redirect',
330                               'nextAction' => 'doc',
331                               'args' => array('title' => 'api')));
332
333             $m->connect('api/statuses/public_timeline.:format',
334                         array('action' => 'ApiTimelinePublic',
335                               'format' => '(xml|json|rss|atom|as)'));
336
337             $m->connect('api/statuses/friends_timeline/:id.:format',
338                         array('action' => 'ApiTimelineFriends',
339                               'id' => Nickname::INPUT_FMT,
340                               'format' => '(xml|json|rss|atom|as)'));
341
342             $m->connect('api/statuses/friends_timeline.:format',
343                         array('action' => 'ApiTimelineFriends',
344                               'format' => '(xml|json|rss|atom|as)'));
345
346             $m->connect('api/statuses/home_timeline/:id.:format',
347                         array('action' => 'ApiTimelineHome',
348                               'id' => Nickname::INPUT_FMT,
349                               'format' => '(xml|json|rss|atom|as)'));
350
351             $m->connect('api/statuses/home_timeline.:format',
352                         array('action' => 'ApiTimelineHome',
353                               'format' => '(xml|json|rss|atom|as)'));
354
355             $m->connect('api/statuses/user_timeline/:id.:format',
356                         array('action' => 'ApiTimelineUser',
357                               'id' => Nickname::INPUT_FMT,
358                               'format' => '(xml|json|rss|atom|as)'));
359
360             $m->connect('api/statuses/user_timeline.:format',
361                         array('action' => 'ApiTimelineUser',
362                               'format' => '(xml|json|rss|atom|as)'));
363
364             $m->connect('api/statuses/mentions/:id.:format',
365                         array('action' => 'ApiTimelineMentions',
366                               'id' => Nickname::INPUT_FMT,
367                               'format' => '(xml|json|rss|atom|as)'));
368
369             $m->connect('api/statuses/mentions.:format',
370                         array('action' => 'ApiTimelineMentions',
371                               'format' => '(xml|json|rss|atom|as)'));
372
373             $m->connect('api/statuses/replies/:id.:format',
374                         array('action' => 'ApiTimelineMentions',
375                               'id' => Nickname::INPUT_FMT,
376                               'format' => '(xml|json|rss|atom|as)'));
377
378             $m->connect('api/statuses/replies.:format',
379                         array('action' => 'ApiTimelineMentions',
380                               'format' => '(xml|json|rss|atom|as)'));
381  
382             $m->connect('api/statuses/mentions_timeline/:id.:format',
383                         array('action' => 'ApiTimelineMentions',
384                               'id' => Nickname::INPUT_FMT,
385                               'format' => '(xml|json|rss|atom|as)'));
386
387             $m->connect('api/statuses/mentions_timeline.:format',
388                         array('action' => 'ApiTimelineMentions',
389                               'format' => '(xml|json|rss|atom|as)'));
390
391             $m->connect('api/statuses/retweeted_by_me.:format',
392                         array('action' => 'ApiTimelineRetweetedByMe',
393                               'format' => '(xml|json|atom|as)'));
394
395             $m->connect('api/statuses/retweeted_to_me.:format',
396                         array('action' => 'ApiTimelineRetweetedToMe',
397                               'format' => '(xml|json|atom|as)'));
398
399             $m->connect('api/statuses/retweets_of_me.:format',
400                         array('action' => 'ApiTimelineRetweetsOfMe',
401                               'format' => '(xml|json|atom|as)'));
402
403             $m->connect('api/statuses/friends/:id.:format',
404                         array('action' => 'ApiUserFriends',
405                               'id' => Nickname::INPUT_FMT,
406                               'format' => '(xml|json)'));
407
408             $m->connect('api/statuses/friends.:format',
409                         array('action' => 'ApiUserFriends',
410                               'format' => '(xml|json)'));
411
412             $m->connect('api/statuses/followers/:id.:format',
413                         array('action' => 'ApiUserFollowers',
414                               'id' => Nickname::INPUT_FMT,
415                               'format' => '(xml|json)'));
416
417             $m->connect('api/statuses/followers.:format',
418                         array('action' => 'ApiUserFollowers',
419                               'format' => '(xml|json)'));
420
421             $m->connect('api/statuses/show/:id.:format',
422                         array('action' => 'ApiStatusesShow',
423                               'id' => '[0-9]+',
424                               'format' => '(xml|json|atom)'));
425
426             $m->connect('api/statuses/show.:format',
427                         array('action' => 'ApiStatusesShow',
428                               'format' => '(xml|json|atom)'));
429
430             $m->connect('api/statuses/update.:format',
431                         array('action' => 'ApiStatusesUpdate',
432                               'format' => '(xml|json)'));
433
434             $m->connect('api/statuses/destroy/:id.:format',
435                         array('action' => 'ApiStatusesDestroy',
436                               'id' => '[0-9]+',
437                               'format' => '(xml|json)'));
438
439             $m->connect('api/statuses/destroy.:format',
440                         array('action' => 'ApiStatusesDestroy',
441                               'format' => '(xml|json)'));
442
443             $m->connect('api/statuses/retweet/:id.:format',
444                         array('action' => 'ApiStatusesRetweet',
445                               'id' => '[0-9]+',
446                               'format' => '(xml|json)'));
447
448             $m->connect('api/statuses/retweets/:id.:format',
449                         array('action' => 'ApiStatusesRetweets',
450                               'id' => '[0-9]+',
451                               'format' => '(xml|json)'));
452
453             // START qvitter API additions
454             
455             $m->connect('api/attachment/:id.:format',
456                         array('action' => 'ApiAttachment',
457                               'id' => '[0-9]+',
458                               'format' => '(xml|json)'));
459             
460             $m->connect('api/checkhub.:format',
461                         array('action' => 'ApiCheckHub',
462                               'format' => '(xml|json)'));
463             
464             $m->connect('api/externalprofile/show.:format',
465                         array('action' => 'ApiExternalProfileShow',
466                               'format' => '(xml|json)'));
467
468             $m->connect('api/statusnet/groups/admins/:id.:format',
469                         array('action' => 'ApiGroupAdmins',
470                               'id' => Nickname::INPUT_FMT,
471                               'format' => '(xml|json)'));
472             
473             $m->connect('api/account/update_link_color.:format',
474                         array('action' => 'ApiAccountUpdateLinkColor',
475                               'format' => '(xml|json)'));
476                 
477             $m->connect('api/account/update_background_color.:format',
478                         array('action' => 'ApiAccountUpdateBackgroundColor',
479                               'format' => '(xml|json)'));
480
481             $m->connect('api/account/register.:format',
482                         array('action' => 'ApiAccountRegister',
483                               'format' => '(xml|json)'));
484             
485             $m->connect('api/check_nickname.:format',
486                         array('action' => 'ApiCheckNickname',
487                               'format' => '(xml|json)'));
488
489             // END qvitter API additions
490
491             // users
492
493             $m->connect('api/users/show/:id.:format',
494                         array('action' => 'ApiUserShow',
495                               'id' => Nickname::INPUT_FMT,
496                               'format' => '(xml|json)'));
497
498             $m->connect('api/users/show.:format',
499                         array('action' => 'ApiUserShow',
500                               'format' => '(xml|json)'));
501
502             $m->connect('api/users/profile_image/:screen_name.:format',
503                         array('action' => 'ApiUserProfileImage',
504                               'screen_name' => Nickname::DISPLAY_FMT,
505                               'format' => '(xml|json)'));
506
507             // friendships
508
509             $m->connect('api/friendships/show.:format',
510                         array('action' => 'ApiFriendshipsShow',
511                               'format' => '(xml|json)'));
512
513             $m->connect('api/friendships/exists.:format',
514                         array('action' => 'ApiFriendshipsExists',
515                               'format' => '(xml|json)'));
516
517             $m->connect('api/friendships/create/:id.:format',
518                         array('action' => 'ApiFriendshipsCreate',
519                               'id' => Nickname::INPUT_FMT,
520                               'format' => '(xml|json)'));
521
522             $m->connect('api/friendships/create.:format',
523                         array('action' => 'ApiFriendshipsCreate',
524                               'format' => '(xml|json)'));
525
526             $m->connect('api/friendships/destroy/:id.:format',
527                         array('action' => 'ApiFriendshipsDestroy',
528                               'id' => Nickname::INPUT_FMT,
529                               'format' => '(xml|json)'));
530
531             $m->connect('api/friendships/destroy.:format',
532                         array('action' => 'ApiFriendshipsDestroy',
533                               'format' => '(xml|json)'));
534
535             // Social graph
536
537             $m->connect('api/friends/ids/:id.:format',
538                         array('action' => 'ApiUserFriends',
539                               'ids_only' => true));
540
541             $m->connect('api/followers/ids/:id.:format',
542                         array('action' => 'ApiUserFollowers',
543                               'ids_only' => true));
544
545             $m->connect('api/friends/ids.:format',
546                         array('action' => 'ApiUserFriends',
547                               'ids_only' => true));
548
549             $m->connect('api/followers/ids.:format',
550                         array('action' => 'ApiUserFollowers',
551                               'ids_only' => true));
552
553             // account
554
555             $m->connect('api/account/verify_credentials.:format',
556                         array('action' => 'ApiAccountVerifyCredentials'));
557
558             $m->connect('api/account/update_profile.:format',
559                         array('action' => 'ApiAccountUpdateProfile'));
560
561             $m->connect('api/account/update_profile_image.:format',
562                         array('action' => 'ApiAccountUpdateProfileImage'));
563
564             $m->connect('api/account/update_delivery_device.:format',
565                         array('action' => 'ApiAccountUpdateDeliveryDevice'));
566
567             // special case where verify_credentials is called w/out a format
568
569             $m->connect('api/account/verify_credentials',
570                         array('action' => 'ApiAccountVerifyCredentials'));
571
572             $m->connect('api/account/rate_limit_status.:format',
573                         array('action' => 'ApiAccountRateLimitStatus'));
574
575             // blocks
576
577             $m->connect('api/blocks/create/:id.:format',
578                         array('action' => 'ApiBlockCreate',
579                               'id' => Nickname::INPUT_FMT,
580                               'format' => '(xml|json)'));
581
582             $m->connect('api/blocks/create.:format',
583                         array('action' => 'ApiBlockCreate',
584                               'format' => '(xml|json)'));
585
586             $m->connect('api/blocks/destroy/:id.:format',
587                         array('action' => 'ApiBlockDestroy',
588                               'id' => Nickname::INPUT_FMT,
589                               'format' => '(xml|json)'));
590
591             $m->connect('api/blocks/destroy.:format',
592                         array('action' => 'ApiBlockDestroy',
593                               'format' => '(xml|json)'));
594
595             // help
596
597             $m->connect('api/help/test.:format',
598                         array('action' => 'ApiHelpTest',
599                               'format' => '(xml|json)'));
600
601             // statusnet
602
603             $m->connect('api/statusnet/version.:format',
604                         array('action' => 'ApiGNUsocialVersion',
605                               'format' => '(xml|json)'));
606
607             $m->connect('api/statusnet/config.:format',
608                         array('action' => 'ApiGNUsocialConfig',
609                               'format' => '(xml|json)'));
610
611             // For our current software name, we provide "gnusocial" base action
612
613             $m->connect('api/gnusocial/version.:format',
614                         array('action' => 'ApiGNUsocialVersion',
615                               'format' => '(xml|json)'));
616
617             $m->connect('api/gnusocial/config.:format',
618                         array('action' => 'ApiGNUsocialConfig',
619                               'format' => '(xml|json)'));
620
621             // Groups and tags are newer than 0.8.1 so no backward-compatibility
622             // necessary
623
624             // Groups
625             //'list' has to be handled differently, as php will not allow a method to be named 'list'
626
627             $m->connect('api/statusnet/groups/timeline/:id.:format',
628                         array('action' => 'ApiTimelineGroup',
629                               'id' => Nickname::INPUT_FMT,
630                               'format' => '(xml|json|rss|atom|as)'));
631
632             $m->connect('api/statusnet/groups/show/:id.:format',
633                         array('action' => 'ApiGroupShow',
634                               'id' => Nickname::INPUT_FMT,
635                               'format' => '(xml|json)'));
636
637             $m->connect('api/statusnet/groups/show.:format',
638                         array('action' => 'ApiGroupShow',
639                               'format' => '(xml|json)'));
640
641             $m->connect('api/statusnet/groups/join/:id.:format',
642                         array('action' => 'ApiGroupJoin',
643                               'id' => Nickname::INPUT_FMT,
644                               'format' => '(xml|json)'));
645
646             $m->connect('api/statusnet/groups/join.:format',
647                         array('action' => 'ApiGroupJoin',
648                               'format' => '(xml|json)'));
649
650             $m->connect('api/statusnet/groups/leave/:id.:format',
651                         array('action' => 'ApiGroupLeave',
652                               'format' => '(xml|json)'));
653
654             $m->connect('api/statusnet/groups/leave.:format',
655                         array('action' => 'ApiGroupLeave',
656                               'id' => Nickname::INPUT_FMT,
657                               'format' => '(xml|json)'));
658
659             $m->connect('api/statusnet/groups/is_member.:format',
660                         array('action' => 'ApiGroupIsMember',
661                               'format' => '(xml|json)'));
662
663             $m->connect('api/statusnet/groups/list/:id.:format',
664                         array('action' => 'ApiGroupList',
665                               'id' => Nickname::INPUT_FMT,
666                               'format' => '(xml|json|rss|atom)'));
667
668             $m->connect('api/statusnet/groups/list.:format',
669                         array('action' => 'ApiGroupList',
670                               'format' => '(xml|json|rss|atom)'));
671
672             $m->connect('api/statusnet/groups/list_all.:format',
673                         array('action' => 'ApiGroupListAll',
674                               'format' => '(xml|json|rss|atom)'));
675
676             $m->connect('api/statusnet/groups/membership/:id.:format',
677                         array('action' => 'ApiGroupMembership',
678                               'id' => Nickname::INPUT_FMT,
679                               'format' => '(xml|json)'));
680
681             $m->connect('api/statusnet/groups/membership.:format',
682                         array('action' => 'ApiGroupMembership',
683                               'format' => '(xml|json)'));
684
685             $m->connect('api/statusnet/groups/create.:format',
686                         array('action' => 'ApiGroupCreate',
687                               'format' => '(xml|json)'));
688
689             $m->connect('api/statusnet/groups/update/:id.:format',
690                         array('action' => 'ApiGroupProfileUpdate',
691                               'id' => '[a-zA-Z0-9]+',
692                               'format' => '(xml|json)'));
693                               
694             $m->connect('api/statusnet/conversation/:id.:format',
695                         array('action' => 'apiconversation',
696                               'id' => '[0-9]+',
697                               'format' => '(xml|json|rss|atom|as)'));
698
699             // Lists (people tags)
700             $m->connect('api/lists/list.:format',
701                         array('action' => 'ApiListSubscriptions',
702                               'format' => '(xml|json)'));
703
704             $m->connect('api/lists/memberships.:format',
705                         array('action' => 'ApiListMemberships',
706                               'format' => '(xml|json)'));
707
708             $m->connect('api/:user/lists/memberships.:format',
709                         array('action' => 'ApiListMemberships',
710                               'user' => '[a-zA-Z0-9]+',
711                               'format' => '(xml|json)'));
712
713             $m->connect('api/lists/subscriptions.:format',
714                         array('action' => 'ApiListSubscriptions',
715                               'format' => '(xml|json)'));
716
717             $m->connect('api/:user/lists/subscriptions.:format',
718                         array('action' => 'ApiListSubscriptions',
719                               'user' => '[a-zA-Z0-9]+',
720                               'format' => '(xml|json)'));
721
722             $m->connect('api/lists.:format',
723                         array('action' => 'ApiLists',
724                               'format' => '(xml|json)'));
725
726             $m->connect('api/:user/lists/:id.:format',
727                         array('action' => 'ApiList',
728                               'user' => '[a-zA-Z0-9]+',
729                               'id' => '[a-zA-Z0-9]+',
730                               'format' => '(xml|json)'));
731
732             $m->connect('api/:user/lists.:format',
733                         array('action' => 'ApiLists',
734                               'user' => '[a-zA-Z0-9]+',
735                               'format' => '(xml|json)'));
736
737             $m->connect('api/:user/lists/:id/statuses.:format',
738                         array('action' => 'ApiTimelineList',
739                               'user' => '[a-zA-Z0-9]+',
740                               'id' => '[a-zA-Z0-9]+',
741                               'format' => '(xml|json|rss|atom)'));
742
743             $m->connect('api/:user/:list_id/members/:id.:format',
744                         array('action' => 'ApiListMember',
745                               'user' => '[a-zA-Z0-9]+',
746                               'list_id' => '[a-zA-Z0-9]+',
747                               'id' => '[a-zA-Z0-9]+',
748                               'format' => '(xml|json)'));
749
750             $m->connect('api/:user/:list_id/members.:format',
751                         array('action' => 'ApiListMembers',
752                               'user' => '[a-zA-Z0-9]+',
753                               'list_id' => '[a-zA-Z0-9]+',
754                               'format' => '(xml|json)'));
755
756             $m->connect('api/:user/:list_id/subscribers/:id.:format',
757                         array('action' => 'ApiListSubscriber',
758                               'user' => '[a-zA-Z0-9]+',
759                               'list_id' => '[a-zA-Z0-9]+',
760                               'id' => '[a-zA-Z0-9]+',
761                               'format' => '(xml|json)'));
762
763             $m->connect('api/:user/:list_id/subscribers.:format',
764                         array('action' => 'ApiListSubscribers',
765                               'user' => '[a-zA-Z0-9]+',
766                               'list_id' => '[a-zA-Z0-9]+',
767                               'format' => '(xml|json)'));
768
769             // Tags
770             $m->connect('api/statusnet/tags/timeline/:tag.:format',
771                         array('action' => 'ApiTimelineTag',
772                               'tag'    => self::REGEX_TAG,
773                               'format' => '(xml|json|rss|atom|as)'));
774
775             // media related
776             $m->connect(
777                 'api/statusnet/media/upload',
778                 array('action' => 'ApiMediaUpload')
779             );
780             $m->connect(
781                 'api/statuses/update_with_media.json',
782                 array('action' => 'ApiMediaUpload')
783             );
784
785             // search
786             $m->connect('api/search.atom', array('action' => 'ApiSearchAtom'));
787             $m->connect('api/search.json', array('action' => 'ApiSearchJSON'));
788             $m->connect('api/trends.json', array('action' => 'ApiTrends'));
789
790             $m->connect('api/oauth/request_token',
791                         array('action' => 'ApiOAuthRequestToken'));
792
793             $m->connect('api/oauth/access_token',
794                         array('action' => 'ApiOAuthAccessToken'));
795
796             $m->connect('api/oauth/authorize',
797                         array('action' => 'ApiOAuthAuthorize'));
798
799             // Admin
800
801             $m->connect('panel/site', array('action' => 'siteadminpanel'));
802             $m->connect('panel/user', array('action' => 'useradminpanel'));
803             $m->connect('panel/access', array('action' => 'accessadminpanel'));
804             $m->connect('panel/paths', array('action' => 'pathsadminpanel'));
805             $m->connect('panel/sessions', array('action' => 'sessionsadminpanel'));
806             $m->connect('panel/sitenotice', array('action' => 'sitenoticeadminpanel'));
807             $m->connect('panel/license', array('action' => 'licenseadminpanel'));
808
809             $m->connect('panel/plugins', array('action' => 'pluginsadminpanel'));
810             $m->connect('panel/plugins/enable/:plugin',
811                         array('action' => 'pluginenable'),
812                         array('plugin' => '[A-Za-z0-9_]+'));
813             $m->connect('panel/plugins/disable/:plugin',
814                         array('action' => 'plugindisable'),
815                         array('plugin' => '[A-Za-z0-9_]+'));
816
817             $m->connect('getfile/:filename',
818                         array('action' => 'getfile'),
819                         array('filename' => '[A-Za-z0-9._-]+'));
820
821             // Common people-tag stuff
822
823             $m->connect('peopletag/:tag', array('action' => 'peopletag',
824                                                 'tag'    => self::REGEX_TAG));
825
826             $m->connect('selftag/:tag', array('action' => 'selftag',
827                                               'tag'    => self::REGEX_TAG));
828
829             $m->connect('main/addpeopletag', array('action' => 'addpeopletag'));
830
831             $m->connect('main/removepeopletag', array('action' => 'removepeopletag'));
832
833             $m->connect('main/profilecompletion', array('action' => 'profilecompletion'));
834
835             $m->connect('main/peopletagautocomplete', array('action' => 'peopletagautocomplete'));
836
837             // In the "root"
838
839             if (common_config('singleuser', 'enabled')) {
840
841                 $nickname = User::singleUserNickname();
842
843                 foreach (array('subscriptions', 'subscribers',
844                                'all', 'foaf', 'replies',
845                                'microsummary') as $a) {
846                     $m->connect($a,
847                                 array('action' => $a,
848                                       'nickname' => $nickname));
849                 }
850
851                 foreach (array('subscriptions', 'subscribers') as $a) {
852                     $m->connect($a.'/:tag',
853                                 array('action' => $a,
854                                       'nickname' => $nickname),
855                                 array('tag' => self::REGEX_TAG));
856                 }
857
858                 $m->connect('subscribers/pending',
859                             array('action' => 'subqueue',
860                                   'nickname' => $nickname));
861
862                 foreach (array('rss', 'groups') as $a) {
863                     $m->connect($a,
864                                 array('action' => 'user'.$a,
865                                       'nickname' => $nickname));
866                 }
867
868                 foreach (array('all', 'replies') as $a) {
869                     $m->connect($a.'/rss',
870                                 array('action' => $a.'rss',
871                                       'nickname' => $nickname));
872                 }
873
874                 $m->connect('avatar',
875                             array('action' => 'avatarbynickname',
876                                   'nickname' => $nickname));
877                 $m->connect('avatar/:size',
878                             array('action' => 'avatarbynickname',
879                                   'nickname' => $nickname),
880                             array('size' => '(|original|\d+)'));
881
882                 $m->connect('tag/:tag/rss',
883                             array('action' => 'userrss',
884                                   'nickname' => $nickname),
885                             array('tag' => self::REGEX_TAG));
886
887                 $m->connect('tag/:tag',
888                             array('action' => 'showstream',
889                                   'nickname' => $nickname),
890                             array('tag' => self::REGEX_TAG));
891
892                 $m->connect('rsd.xml',
893                             array('action' => 'rsd',
894                                   'nickname' => $nickname));
895
896                 $m->connect('',
897                             array('action' => 'showstream',
898                                   'nickname' => $nickname));
899
900                 // peopletags
901
902                 $m->connect('peopletags',
903                             array('action' => 'peopletagsbyuser'));
904
905                 $m->connect('peopletags/private',
906                             array('action' => 'peopletagsbyuser',
907                                   'private' => 1));
908
909                 $m->connect('peopletags/public',
910                             array('action' => 'peopletagsbyuser',
911                                   'public' => 1));
912
913                 $m->connect('othertags',
914                             array('action' => 'peopletagsforuser'));
915
916                 $m->connect('peopletagsubscriptions',
917                             array('action' => 'peopletagsubscriptions'));
918
919                 $m->connect('all/:tag/subscribers',
920                             array('action' => 'peopletagsubscribers',
921                                   'tag' => self::REGEX_TAG));
922
923                 $m->connect('all/:tag/tagged',
924                                 array('action' => 'peopletagged',
925                                       'tag' => self::REGEX_TAG));
926
927                 $m->connect('all/:tag/edit',
928                                 array('action' => 'editpeopletag',
929                                       'tag' => self::REGEX_TAG));
930
931                 foreach(array('subscribe', 'unsubscribe') as $v) {
932                     $m->connect('peopletag/:id/'.$v,
933                                     array('action' => $v.'peopletag',
934                                           'id' => '[0-9]{1,64}'));
935                 }
936                 $m->connect('user/:tagger_id/profiletag/:id/id',
937                                 array('action' => 'profiletagbyid',
938                                       'tagger_id' => '[0-9]+',
939                                       'id' => '[0-9]+'));
940
941                 $m->connect('all/:tag',
942                                 array('action' => 'showprofiletag',
943                                       'tag' => self::REGEX_TAG));
944
945                 foreach (array('subscriptions', 'subscribers') as $a) {
946                     $m->connect($a.'/:tag',
947                                 array('action' => $a),
948                                 array('tag' => self::REGEX_TAG));
949                 }
950             } else {
951                 $m->connect('main/public', array('action' => 'public'));
952                 $m->connect('main/all', array('action' => 'networkpublic'));
953                 if (common_config('site', 'localonly')) {
954                     $m->connect('', array('action' => 'public'));
955                 } else {
956                     $m->connect('', array('action' => 'networkpublic'));
957                 }
958                 $m->connect('rss', array('action' => 'publicrss'));
959                 $m->connect('featuredrss', array('action' => 'featuredrss'));
960                 $m->connect('featured/', array('action' => 'featured'));
961                 $m->connect('featured', array('action' => 'featured'));
962                 $m->connect('rsd.xml', array('action' => 'rsd'));
963
964                 foreach (array('subscriptions', 'subscribers',
965                                'nudge', 'all', 'foaf', 'replies',
966                                'inbox', 'outbox', 'microsummary') as $a) {
967                     $m->connect(':nickname/'.$a,
968                                 array('action' => $a),
969                                 array('nickname' => Nickname::DISPLAY_FMT));
970                 }
971                 $m->connect(':nickname/subscribers/pending',
972                             array('action' => 'subqueue'),
973                             array('nickname' => Nickname::DISPLAY_FMT));
974
975                 // people tags
976
977                 $m->connect(':nickname/peopletags',
978                                 array('action' => 'peopletagsbyuser',
979                                       'nickname' => Nickname::DISPLAY_FMT));
980
981                 $m->connect(':nickname/peopletags/private',
982                                 array('action' => 'peopletagsbyuser',
983                                       'nickname' => Nickname::DISPLAY_FMT,
984                                       'private' => 1));
985
986                 $m->connect(':nickname/peopletags/public',
987                                 array('action' => 'peopletagsbyuser',
988                                       'nickname' => Nickname::DISPLAY_FMT,
989                                       'public' => 1));
990
991                 $m->connect(':nickname/othertags',
992                                 array('action' => 'peopletagsforuser',
993                                       'nickname' => Nickname::DISPLAY_FMT));
994
995                 $m->connect(':nickname/peopletagsubscriptions',
996                                 array('action' => 'peopletagsubscriptions',
997                                       'nickname' => Nickname::DISPLAY_FMT));
998
999                 $m->connect(':tagger/all/:tag/subscribers',
1000                                 array('action' => 'peopletagsubscribers',
1001                                       'tagger' => Nickname::DISPLAY_FMT,
1002                                       'tag' => self::REGEX_TAG));
1003
1004                 $m->connect(':tagger/all/:tag/tagged',
1005                                 array('action' => 'peopletagged',
1006                                       'tagger' => Nickname::DISPLAY_FMT,
1007                                       'tag' => self::REGEX_TAG));
1008
1009                 $m->connect(':tagger/all/:tag/edit',
1010                                 array('action' => 'editpeopletag',
1011                                       'tagger' => Nickname::DISPLAY_FMT,
1012                                       'tag' => self::REGEX_TAG));
1013
1014                 foreach(array('subscribe', 'unsubscribe') as $v) {
1015                     $m->connect('peopletag/:id/'.$v,
1016                                     array('action' => $v.'peopletag',
1017                                           'id' => '[0-9]{1,64}'));
1018                 }
1019                 $m->connect('user/:tagger_id/profiletag/:id/id',
1020                                 array('action' => 'profiletagbyid',
1021                                       'tagger_id' => '[0-9]+',
1022                                       'id' => '[0-9]+'));
1023
1024                 $m->connect(':tagger/all/:tag',
1025                                 array('action' => 'showprofiletag',
1026                                       'tagger' => Nickname::DISPLAY_FMT,
1027                                       'tag' => self::REGEX_TAG));
1028
1029                 foreach (array('subscriptions', 'subscribers') as $a) {
1030                     $m->connect(':nickname/'.$a.'/:tag',
1031                                 array('action' => $a),
1032                                 array('tag' => self::REGEX_TAG,
1033                                       'nickname' => Nickname::DISPLAY_FMT));
1034                 }
1035
1036                 foreach (array('rss', 'groups') as $a) {
1037                     $m->connect(':nickname/'.$a,
1038                                 array('action' => 'user'.$a),
1039                                 array('nickname' => Nickname::DISPLAY_FMT));
1040                 }
1041
1042                 foreach (array('all', 'replies') as $a) {
1043                     $m->connect(':nickname/'.$a.'/rss',
1044                                 array('action' => $a.'rss'),
1045                                 array('nickname' => Nickname::DISPLAY_FMT));
1046                 }
1047
1048                 $m->connect(':nickname/avatar',
1049                             array('action' => 'avatarbynickname'),
1050                             array('nickname' => Nickname::DISPLAY_FMT));
1051                 $m->connect(':nickname/avatar/:size',
1052                             array('action' => 'avatarbynickname'),
1053                             array('size' => '(|original|\d+)',
1054                                   'nickname' => Nickname::DISPLAY_FMT));
1055
1056                 $m->connect(':nickname/tag/:tag/rss',
1057                             array('action' => 'userrss'),
1058                             array('nickname' => Nickname::DISPLAY_FMT),
1059                             array('tag' => self::REGEX_TAG));
1060
1061                 $m->connect(':nickname/tag/:tag',
1062                             array('action' => 'showstream'),
1063                             array('nickname' => Nickname::DISPLAY_FMT),
1064                             array('tag' => self::REGEX_TAG));
1065
1066                 $m->connect(':nickname/rsd.xml',
1067                             array('action' => 'rsd'),
1068                             array('nickname' => Nickname::DISPLAY_FMT));
1069
1070                 $m->connect(':nickname',
1071                             array('action' => 'showstream'),
1072                             array('nickname' => Nickname::DISPLAY_FMT));
1073
1074                 $m->connect(':nickname/',
1075                             array('action' => 'showstream'),
1076                             array('nickname' => Nickname::DISPLAY_FMT));
1077             }
1078
1079             // AtomPub API
1080
1081             $m->connect('api/statusnet/app/service/:id.xml',
1082                         array('action' => 'ApiAtomService'),
1083                         array('id' => Nickname::DISPLAY_FMT));
1084
1085             $m->connect('api/statusnet/app/service.xml',
1086                         array('action' => 'ApiAtomService'));
1087
1088             $m->connect('api/statusnet/app/subscriptions/:subscriber/:subscribed.atom',
1089                         array('action' => 'AtomPubShowSubscription'),
1090                         array('subscriber' => '[0-9]+',
1091                               'subscribed' => '[0-9]+'));
1092
1093             $m->connect('api/statusnet/app/subscriptions/:subscriber.atom',
1094                         array('action' => 'AtomPubSubscriptionFeed'),
1095                         array('subscriber' => '[0-9]+'));
1096
1097             $m->connect('api/statusnet/app/memberships/:profile/:group.atom',
1098                         array('action' => 'AtomPubShowMembership'),
1099                         array('profile' => '[0-9]+',
1100                               'group' => '[0-9]+'));
1101
1102             $m->connect('api/statusnet/app/memberships/:profile.atom',
1103                         array('action' => 'AtomPubMembershipFeed'),
1104                         array('profile' => '[0-9]+'));
1105
1106             // URL shortening
1107
1108             $m->connect('url/:id',
1109                         array('action' => 'redirecturl',
1110                               'id' => '[0-9]+'));
1111
1112             // user stuff
1113
1114             Event::handle('RouterInitialized', array($m));
1115         }
1116
1117         return $m;
1118     }
1119
1120     function map($path)
1121     {
1122         try {
1123             $match = $this->m->match($path);
1124         } catch (Exception $e) {
1125             common_log(LOG_ERR, "Problem getting route for $path - " .
1126                        $e->getMessage());
1127             // TRANS: Client error on action trying to visit a non-existing page.
1128             $cac = new ClientErrorAction(_('Page not found.'), 404);
1129             $cac->showPage();
1130         }
1131
1132         return $match;
1133     }
1134
1135     function build($action, $args=null, $params=null, $fragment=null)
1136     {
1137         $action_arg = array('action' => $action);
1138
1139         if ($args) {
1140             $args = array_merge($action_arg, $args);
1141         } else {
1142             $args = $action_arg;
1143         }
1144
1145         $url = $this->m->generate($args, $params, $fragment);
1146         // Due to a bug in the Net_URL_Mapper code, the returned URL may
1147         // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
1148         // repair that here rather than modifying the upstream code...
1149
1150         $qpos = strpos($url, '?');
1151         if ($qpos !== false) {
1152             $url = substr($url, 0, $qpos+1) .
1153                 str_replace('?', '&', substr($url, $qpos+1));
1154
1155             // @fixme this is a hacky workaround for http_build_query in the
1156             // lower-level code and bad configs that set the default separator
1157             // to &amp; instead of &. Encoded &s in parameters will not be
1158             // affected.
1159             $url = substr($url, 0, $qpos+1) .
1160                 str_replace('&amp;', '&', substr($url, $qpos+1));
1161
1162         }
1163
1164         return $url;
1165     }
1166 }