]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/router.php
Let's not limit qvitter stuff to 'json' requests
[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', 'favor', 'disfavor', '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/tagprofile/:id', array('action' => 'tagprofile'),
167                                                array('id' => '[0-9]+'));
168
169             $m->connect('main/tagprofile', array('action' => 'tagprofile'));
170
171             $m->connect('main/oembed',
172                         array('action' => 'oembed'));
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/ajax',
225                         array('action' => 'attachment_ajax'),
226                         array('attachment' => '[0-9]+'));
227
228             $m->connect('attachment/:attachment/thumbnail',
229                         array('action' => 'attachment_thumbnail'),
230                         array('attachment' => '[0-9]+'));
231
232             $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
233                         array('action' => 'newnotice'),
234                         array('replyto' => Nickname::DISPLAY_FMT),
235                         array('inreplyto' => '[0-9]+'));
236
237             $m->connect('notice/new?replyto=:replyto',
238                         array('action' => 'newnotice'),
239                         array('replyto' => Nickname::DISPLAY_FMT));
240
241             $m->connect('notice/new', array('action' => 'newnotice'));
242
243             $m->connect('notice/:notice/file',
244                         array('action' => 'file'),
245                         array('notice' => '[0-9]+'));
246
247             $m->connect('notice/:notice',
248                         array('action' => 'shownotice'),
249                         array('notice' => '[0-9]+'));
250
251             $m->connect('notice/delete/:notice',
252                         array('action' => 'deletenotice'),
253                         array('notice' => '[0-9]+'));
254
255             $m->connect('notice/delete', array('action' => 'deletenotice'));
256
257             $m->connect('bookmarklet/new', array('action' => 'bookmarklet'));
258
259             // conversation
260
261             $m->connect('conversation/:id',
262                         array('action' => 'conversation'),
263                         array('id' => '[0-9]+'));
264             $m->connect('conversation/:id/replies',
265                         array('action' => 'conversationreplies'),
266                         array('id' => '[0-9]+'));
267
268             $m->connect('message/new', array('action' => 'newmessage'));
269             $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => Nickname::DISPLAY_FMT));
270             $m->connect('message/:message',
271                         array('action' => 'showmessage'),
272                         array('message' => '[0-9]+'));
273
274             $m->connect('user/:id',
275                         array('action' => 'userbyid'),
276                         array('id' => '[0-9]+'));
277
278             if (!common_config('performance', 'high')) {
279                 $m->connect('tags/', array('action' => 'publictagcloud'));
280                 $m->connect('tag/', array('action' => 'publictagcloud'));
281                 $m->connect('tags', array('action' => 'publictagcloud'));
282                 $m->connect('tag', array('action' => 'publictagcloud'));
283             }
284             $m->connect('tag/:tag/rss',
285                         array('action' => 'tagrss'),
286                         array('tag' => self::REGEX_TAG));
287             $m->connect('tag/:tag',
288                         array('action' => 'tag'),
289                         array('tag' => self::REGEX_TAG));
290
291             // groups
292
293             $m->connect('group/new', array('action' => 'newgroup'));
294
295             foreach (array('edit', 'join', 'leave', 'delete', 'cancel', 'approve') as $v) {
296                 $m->connect('group/:nickname/'.$v,
297                             array('action' => $v.'group'),
298                             array('nickname' => Nickname::DISPLAY_FMT));
299                 $m->connect('group/:id/id/'.$v,
300                             array('action' => $v.'group'),
301                             array('id' => '[0-9]+'));
302             }
303
304             foreach (array('members', 'logo', 'rss') as $n) {
305                 $m->connect('group/:nickname/'.$n,
306                             array('action' => 'group'.$n),
307                             array('nickname' => Nickname::DISPLAY_FMT));
308             }
309
310             $m->connect('group/:nickname/foaf',
311                         array('action' => 'foafgroup'),
312                         array('nickname' => Nickname::DISPLAY_FMT));
313
314             $m->connect('group/:nickname/blocked',
315                         array('action' => 'blockedfromgroup'),
316                         array('nickname' => Nickname::DISPLAY_FMT));
317
318             $m->connect('group/:nickname/makeadmin',
319                         array('action' => 'makeadmin'),
320                         array('nickname' => Nickname::DISPLAY_FMT));
321
322             $m->connect('group/:nickname/members/pending',
323                         array('action' => 'groupqueue'),
324                         array('nickname' => Nickname::DISPLAY_FMT));
325
326             $m->connect('group/:id/id',
327                         array('action' => 'groupbyid'),
328                         array('id' => '[0-9]+'));
329
330             $m->connect('group/:nickname',
331                         array('action' => 'showgroup'),
332                         array('nickname' => Nickname::DISPLAY_FMT));
333
334             $m->connect('group/:nickname/',
335                         array('action' => 'showgroup'),
336                         array('nickname' => Nickname::DISPLAY_FMT));
337
338             $m->connect('group/', array('action' => 'groups'));
339             $m->connect('group', array('action' => 'groups'));
340             $m->connect('groups/', array('action' => 'groups'));
341             $m->connect('groups', array('action' => 'groups'));
342
343             // Twitter-compatible API
344
345             // statuses API
346
347             $m->connect('api',
348                         array('action' => 'Redirect',
349                               'nextAction' => 'doc',
350                               'args' => array('title' => 'api')));
351
352             $m->connect('api/statuses/public_timeline.:format',
353                         array('action' => 'ApiTimelinePublic',
354                               'format' => '(xml|json|rss|atom|as)'));
355
356             $m->connect('api/statuses/friends_timeline/:id.:format',
357                         array('action' => 'ApiTimelineFriends',
358                               'id' => Nickname::INPUT_FMT,
359                               'format' => '(xml|json|rss|atom|as)'));
360
361             $m->connect('api/statuses/friends_timeline.:format',
362                         array('action' => 'ApiTimelineFriends',
363                               'format' => '(xml|json|rss|atom|as)'));
364
365             $m->connect('api/statuses/home_timeline/:id.:format',
366                         array('action' => 'ApiTimelineHome',
367                               'id' => Nickname::INPUT_FMT,
368                               'format' => '(xml|json|rss|atom|as)'));
369
370             $m->connect('api/statuses/home_timeline.:format',
371                         array('action' => 'ApiTimelineHome',
372                               'format' => '(xml|json|rss|atom|as)'));
373
374             $m->connect('api/statuses/user_timeline/:id.:format',
375                         array('action' => 'ApiTimelineUser',
376                               'id' => Nickname::INPUT_FMT,
377                               'format' => '(xml|json|rss|atom|as)'));
378
379             $m->connect('api/statuses/user_timeline.:format',
380                         array('action' => 'ApiTimelineUser',
381                               'format' => '(xml|json|rss|atom|as)'));
382
383             $m->connect('api/statuses/mentions/:id.:format',
384                         array('action' => 'ApiTimelineMentions',
385                               'id' => Nickname::INPUT_FMT,
386                               'format' => '(xml|json|rss|atom|as)'));
387
388             $m->connect('api/statuses/mentions.:format',
389                         array('action' => 'ApiTimelineMentions',
390                               'format' => '(xml|json|rss|atom|as)'));
391
392             $m->connect('api/statuses/replies/:id.:format',
393                         array('action' => 'ApiTimelineMentions',
394                               'id' => Nickname::INPUT_FMT,
395                               'format' => '(xml|json|rss|atom|as)'));
396
397             $m->connect('api/statuses/replies.:format',
398                         array('action' => 'ApiTimelineMentions',
399                               'format' => '(xml|json|rss|atom|as)'));
400
401             $m->connect('api/statuses/retweeted_by_me.:format',
402                         array('action' => 'ApiTimelineRetweetedByMe',
403                               'format' => '(xml|json|atom|as)'));
404
405             $m->connect('api/statuses/retweeted_to_me.:format',
406                         array('action' => 'ApiTimelineRetweetedToMe',
407                               'format' => '(xml|json|atom|as)'));
408
409             $m->connect('api/statuses/retweets_of_me.:format',
410                         array('action' => 'ApiTimelineRetweetsOfMe',
411                               'format' => '(xml|json|atom|as)'));
412
413             $m->connect('api/statuses/friends/:id.:format',
414                         array('action' => 'ApiUserFriends',
415                               'id' => Nickname::INPUT_FMT,
416                               'format' => '(xml|json)'));
417
418             $m->connect('api/statuses/friends.:format',
419                         array('action' => 'ApiUserFriends',
420                               'format' => '(xml|json)'));
421
422             $m->connect('api/statuses/followers/:id.:format',
423                         array('action' => 'ApiUserFollowers',
424                               'id' => Nickname::INPUT_FMT,
425                               'format' => '(xml|json)'));
426
427             $m->connect('api/statuses/followers.:format',
428                         array('action' => 'ApiUserFollowers',
429                               'format' => '(xml|json)'));
430
431             $m->connect('api/statuses/show/:id.:format',
432                         array('action' => 'ApiStatusesShow',
433                               'id' => '[0-9]+',
434                               'format' => '(xml|json|atom)'));
435
436             $m->connect('api/statuses/show.:format',
437                         array('action' => 'ApiStatusesShow',
438                               'format' => '(xml|json|atom)'));
439
440             $m->connect('api/statuses/update.:format',
441                         array('action' => 'ApiStatusesUpdate',
442                               'format' => '(xml|json)'));
443
444             $m->connect('api/statuses/destroy/:id.:format',
445                         array('action' => 'ApiStatusesDestroy',
446                               'id' => '[0-9]+',
447                               'format' => '(xml|json)'));
448
449             $m->connect('api/statuses/destroy.:format',
450                         array('action' => 'ApiStatusesDestroy',
451                               'format' => '(xml|json)'));
452
453             $m->connect('api/statuses/retweet/:id.:format',
454                         array('action' => 'ApiStatusesRetweet',
455                               'id' => '[0-9]+',
456                               'format' => '(xml|json)'));
457
458             $m->connect('api/statuses/retweets/:id.:format',
459                         array('action' => 'ApiStatusesRetweets',
460                               'id' => '[0-9]+',
461                               'format' => '(xml|json)'));
462
463             // START qvitter API additions
464
465             $m->connect('api/statuses/favs/:id.:format',
466                         array('action' => 'ApiStatusesFavs',
467                               'id' => '[0-9]+',
468                               'format' => '(xml|json)'));
469             
470             $m->connect('api/attachment/:id.:format',
471                         array('action' => 'ApiAttachment',
472                               'id' => '[0-9]+',
473                               'format' => '(xml|json)'));
474             
475             $m->connect('api/checkhub.:format',
476                         array('action' => 'ApiCheckHub',
477                               'format' => '(xml|json)'));
478             
479             $m->connect('api/externalprofile/show.:format',
480                         array('action' => 'ApiExternalProfileShow',
481                               'format' => '(xml|json)'));
482
483             $m->connect('api/statusnet/groups/admins/:id.:format',
484                         array('action' => 'ApiGroupAdmins',
485                               'id' => Nickname::INPUT_FMT,
486                               'format' => '(xml|json)'));
487             
488             $m->connect('api/account/update_link_color.:format',
489                         array('action' => 'ApiAccountUpdateLinkColor',
490                               'format' => '(xml|json)'));
491                 
492             $m->connect('api/account/update_background_color.:format',
493                         array('action' => 'ApiAccountUpdateBackgroundColor',
494                               'format' => '(xml|json)'));
495
496             $m->connect('api/account/register.:format',
497                         array('action' => 'ApiAccountRegister',
498                               'format' => '(xml|json)'));
499             
500             $m->connect('api/check_nickname.:format',
501                         array('action' => 'ApiCheckNickname',
502                               'format' => '(xml|json)'));
503
504             // END qvitter API additions
505
506             // users
507
508             $m->connect('api/users/show/:id.:format',
509                         array('action' => 'ApiUserShow',
510                               'id' => Nickname::INPUT_FMT,
511                               'format' => '(xml|json)'));
512
513             $m->connect('api/users/show.:format',
514                         array('action' => 'ApiUserShow',
515                               'format' => '(xml|json)'));
516
517             $m->connect('api/users/profile_image/:screen_name.:format',
518                         array('action' => 'ApiUserProfileImage',
519                               'screen_name' => Nickname::DISPLAY_FMT,
520                               'format' => '(xml|json)'));
521
522             // direct messages
523
524             $m->connect('api/direct_messages.:format',
525                         array('action' => 'ApiDirectMessage',
526                               'format' => '(xml|json|rss|atom)'));
527
528             $m->connect('api/direct_messages/sent.:format',
529                         array('action' => 'ApiDirectMessage',
530                               'format' => '(xml|json|rss|atom)',
531                               'sent' => true));
532
533             $m->connect('api/direct_messages/new.:format',
534                         array('action' => 'ApiDirectMessageNew',
535                               'format' => '(xml|json)'));
536
537             // friendships
538
539             $m->connect('api/friendships/show.:format',
540                         array('action' => 'ApiFriendshipsShow',
541                               'format' => '(xml|json)'));
542
543             $m->connect('api/friendships/exists.:format',
544                         array('action' => 'ApiFriendshipsExists',
545                               'format' => '(xml|json)'));
546
547             $m->connect('api/friendships/create/:id.:format',
548                         array('action' => 'ApiFriendshipsCreate',
549                               'id' => Nickname::INPUT_FMT,
550                               'format' => '(xml|json)'));
551
552             $m->connect('api/friendships/create.:format',
553                         array('action' => 'ApiFriendshipsCreate',
554                               'format' => '(xml|json)'));
555
556             $m->connect('api/friendships/destroy/:id.:format',
557                         array('action' => 'ApiFriendshipsDestroy',
558                               'id' => Nickname::INPUT_FMT,
559                               'format' => '(xml|json)'));
560
561             $m->connect('api/friendships/destroy.:format',
562                         array('action' => 'ApiFriendshipsDestroy',
563                               'format' => '(xml|json)'));
564
565             // Social graph
566
567             $m->connect('api/friends/ids/:id.:format',
568                         array('action' => 'ApiUserFriends',
569                               'ids_only' => true));
570
571             $m->connect('api/followers/ids/:id.:format',
572                         array('action' => 'ApiUserFollowers',
573                               'ids_only' => true));
574
575             $m->connect('api/friends/ids.:format',
576                         array('action' => 'ApiUserFriends',
577                               'ids_only' => true));
578
579             $m->connect('api/followers/ids.:format',
580                         array('action' => 'ApiUserFollowers',
581                               'ids_only' => true));
582
583             // account
584
585             $m->connect('api/account/verify_credentials.:format',
586                         array('action' => 'ApiAccountVerifyCredentials'));
587
588             $m->connect('api/account/update_profile.:format',
589                         array('action' => 'ApiAccountUpdateProfile'));
590
591             $m->connect('api/account/update_profile_image.:format',
592                         array('action' => 'ApiAccountUpdateProfileImage'));
593
594             $m->connect('api/account/update_delivery_device.:format',
595                         array('action' => 'ApiAccountUpdateDeliveryDevice'));
596
597             // special case where verify_credentials is called w/out a format
598
599             $m->connect('api/account/verify_credentials',
600                         array('action' => 'ApiAccountVerifyCredentials'));
601
602             $m->connect('api/account/rate_limit_status.:format',
603                         array('action' => 'ApiAccountRateLimitStatus'));
604
605             // favorites
606
607             $m->connect('api/favorites/:id.:format',
608                         array('action' => 'ApiTimelineFavorites',
609                               'id' => Nickname::INPUT_FMT,
610                               'format' => '(xml|json|rss|atom|as)'));
611
612             $m->connect('api/favorites.:format',
613                         array('action' => 'ApiTimelineFavorites',
614                               'format' => '(xml|json|rss|atom|as)'));
615
616             $m->connect('api/favorites/create/:id.:format',
617                         array('action' => 'ApiFavoriteCreate',
618                               'id' => '[0-9]+',
619                               'format' => '(xml|json)'));
620
621             $m->connect('api/favorites/destroy/:id.:format',
622                         array('action' => 'ApiFavoriteDestroy',
623                               'id' => '[0-9]+',
624                               'format' => '(xml|json)'));
625             // blocks
626
627             $m->connect('api/blocks/create/:id.:format',
628                         array('action' => 'ApiBlockCreate',
629                               'id' => Nickname::INPUT_FMT,
630                               'format' => '(xml|json)'));
631
632             $m->connect('api/blocks/create.:format',
633                         array('action' => 'ApiBlockCreate',
634                               'format' => '(xml|json)'));
635
636             $m->connect('api/blocks/destroy/:id.:format',
637                         array('action' => 'ApiBlockDestroy',
638                               'id' => Nickname::INPUT_FMT,
639                               'format' => '(xml|json)'));
640
641             $m->connect('api/blocks/destroy.:format',
642                         array('action' => 'ApiBlockDestroy',
643                               'format' => '(xml|json)'));
644
645             // help
646
647             $m->connect('api/help/test.:format',
648                         array('action' => 'ApiHelpTest',
649                               'format' => '(xml|json)'));
650
651             // statusnet
652
653             $m->connect('api/statusnet/version.:format',
654                         array('action' => 'ApiStatusnetVersion',
655                               'format' => '(xml|json)'));
656
657             $m->connect('api/statusnet/config.:format',
658                         array('action' => 'ApiStatusnetConfig',
659                               'format' => '(xml|json)'));
660
661             // For older methods, we provide "laconica" base action
662
663             $m->connect('api/laconica/version.:format',
664                         array('action' => 'ApiStatusnetVersion',
665                               'format' => '(xml|json)'));
666
667             $m->connect('api/laconica/config.:format',
668                         array('action' => 'ApiStatusnetConfig',
669                               'format' => '(xml|json)'));
670
671             // Groups and tags are newer than 0.8.1 so no backward-compatibility
672             // necessary
673
674             // Groups
675             //'list' has to be handled differently, as php will not allow a method to be named 'list'
676
677             $m->connect('api/statusnet/groups/timeline/:id.:format',
678                         array('action' => 'ApiTimelineGroup',
679                               'id' => Nickname::INPUT_FMT,
680                               'format' => '(xml|json|rss|atom|as)'));
681
682             $m->connect('api/statusnet/groups/show/:id.:format',
683                         array('action' => 'ApiGroupShow',
684                               'id' => Nickname::INPUT_FMT,
685                               'format' => '(xml|json)'));
686
687             $m->connect('api/statusnet/groups/show.:format',
688                         array('action' => 'ApiGroupShow',
689                               'format' => '(xml|json)'));
690
691             $m->connect('api/statusnet/groups/join/:id.:format',
692                         array('action' => 'ApiGroupJoin',
693                               'format' => '(xml|json)'));
694
695             $m->connect('api/statusnet/groups/join.:format',
696                         array('action' => 'ApiGroupJoin',
697                               'id' => Nickname::INPUT_FMT,
698                               'format' => '(xml|json)'));
699
700             $m->connect('api/statusnet/groups/leave/:id.:format',
701                         array('action' => 'ApiGroupLeave',
702                               'format' => '(xml|json)'));
703
704             $m->connect('api/statusnet/groups/leave.:format',
705                         array('action' => 'ApiGroupLeave',
706                               'id' => Nickname::INPUT_FMT,
707                               'format' => '(xml|json)'));
708
709             $m->connect('api/statusnet/groups/is_member.:format',
710                         array('action' => 'ApiGroupIsMember',
711                               'format' => '(xml|json)'));
712
713             $m->connect('api/statusnet/groups/list/:id.:format',
714                         array('action' => 'ApiGroupList',
715                               'id' => Nickname::INPUT_FMT,
716                               'format' => '(xml|json|rss|atom)'));
717
718             $m->connect('api/statusnet/groups/list.:format',
719                         array('action' => 'ApiGroupList',
720                               'format' => '(xml|json|rss|atom)'));
721
722             $m->connect('api/statusnet/groups/list_all.:format',
723                         array('action' => 'ApiGroupListAll',
724                               'format' => '(xml|json|rss|atom)'));
725
726             $m->connect('api/statusnet/groups/membership/:id.:format',
727                         array('action' => 'ApiGroupMembership',
728                               'id' => Nickname::INPUT_FMT,
729                               'format' => '(xml|json)'));
730
731             $m->connect('api/statusnet/groups/membership.:format',
732                         array('action' => 'ApiGroupMembership',
733                               'format' => '(xml|json)'));
734
735             $m->connect('api/statusnet/groups/create.:format',
736                         array('action' => 'ApiGroupCreate',
737                               'format' => '(xml|json)'));
738
739             $m->connect('api/statusnet/groups/update/:id.:format',
740                         array('action' => 'ApiGroupProfileUpdate',
741                               'id' => '[a-zA-Z0-9]+',
742                               'format' => '(xml|json)'));
743                               
744             $m->connect('api/statusnet/conversation/:id.:format',
745                         array('action' => 'apiconversation',
746                               'id' => '[0-9]+',
747                               'format' => '(xml|json|rss|atom|as)'));
748
749             // Lists (people tags)
750
751             $m->connect('api/lists/memberships.:format',
752                         array('action' => 'ApiListMemberships',
753                               'format' => '(xml|json)'));
754
755             $m->connect('api/:user/lists/memberships.:format',
756                         array('action' => 'ApiListMemberships',
757                               'user' => '[a-zA-Z0-9]+',
758                               'format' => '(xml|json)'));
759
760             $m->connect('api/lists/subscriptions.:format',
761                         array('action' => 'ApiListSubscriptions',
762                               'format' => '(xml|json)'));
763
764             $m->connect('api/:user/lists/subscriptions.:format',
765                         array('action' => 'ApiListSubscriptions',
766                               'user' => '[a-zA-Z0-9]+',
767                               'format' => '(xml|json)'));
768
769             $m->connect('api/lists.:format',
770                         array('action' => 'ApiLists',
771                               'format' => '(xml|json)'));
772
773             $m->connect('api/:user/lists/:id.:format',
774                         array('action' => 'ApiList',
775                               'user' => '[a-zA-Z0-9]+',
776                               'id' => '[a-zA-Z0-9]+',
777                               'format' => '(xml|json)'));
778
779             $m->connect('api/:user/lists.:format',
780                         array('action' => 'ApiLists',
781                               'user' => '[a-zA-Z0-9]+',
782                               'format' => '(xml|json)'));
783
784             $m->connect('api/:user/lists/:id/statuses.:format',
785                         array('action' => 'ApiTimelineList',
786                               'user' => '[a-zA-Z0-9]+',
787                               'id' => '[a-zA-Z0-9]+',
788                               'format' => '(xml|json|rss|atom)'));
789
790             $m->connect('api/:user/:list_id/members/:id.:format',
791                         array('action' => 'ApiListMember',
792                               'user' => '[a-zA-Z0-9]+',
793                               'list_id' => '[a-zA-Z0-9]+',
794                               'id' => '[a-zA-Z0-9]+',
795                               'format' => '(xml|json)'));
796
797             $m->connect('api/:user/:list_id/members.:format',
798                         array('action' => 'ApiListMembers',
799                               'user' => '[a-zA-Z0-9]+',
800                               'list_id' => '[a-zA-Z0-9]+',
801                               'format' => '(xml|json)'));
802
803             $m->connect('api/:user/:list_id/subscribers/:id.:format',
804                         array('action' => 'ApiListSubscriber',
805                               'user' => '[a-zA-Z0-9]+',
806                               'list_id' => '[a-zA-Z0-9]+',
807                               'id' => '[a-zA-Z0-9]+',
808                               'format' => '(xml|json)'));
809
810             $m->connect('api/:user/:list_id/subscribers.:format',
811                         array('action' => 'ApiListSubscribers',
812                               'user' => '[a-zA-Z0-9]+',
813                               'list_id' => '[a-zA-Z0-9]+',
814                               'format' => '(xml|json)'));
815
816             // Tags
817             $m->connect('api/statusnet/tags/timeline/:tag.:format',
818                         array('action' => 'ApiTimelineTag',
819                               'tag'    => self::REGEX_TAG,
820                               'format' => '(xml|json|rss|atom|as)'));
821
822             // media related
823             $m->connect(
824                 'api/statusnet/media/upload',
825                 array('action' => 'ApiMediaUpload')
826             );
827
828             // search
829             $m->connect('api/search.atom', array('action' => 'ApiSearchAtom'));
830             $m->connect('api/search.json', array('action' => 'ApiSearchJSON'));
831             $m->connect('api/trends.json', array('action' => 'ApiTrends'));
832
833             $m->connect('api/oauth/request_token',
834                         array('action' => 'ApiOAuthRequestToken'));
835
836             $m->connect('api/oauth/access_token',
837                         array('action' => 'ApiOAuthAccessToken'));
838
839             $m->connect('api/oauth/authorize',
840                         array('action' => 'ApiOAuthAuthorize'));
841
842             // Admin
843
844             $m->connect('panel/site', array('action' => 'siteadminpanel'));
845             $m->connect('panel/user', array('action' => 'useradminpanel'));
846                 $m->connect('panel/access', array('action' => 'accessadminpanel'));
847             $m->connect('panel/paths', array('action' => 'pathsadminpanel'));
848             $m->connect('panel/sessions', array('action' => 'sessionsadminpanel'));
849             $m->connect('panel/sitenotice', array('action' => 'sitenoticeadminpanel'));
850             $m->connect('panel/snapshot', array('action' => 'snapshotadminpanel'));
851             $m->connect('panel/license', array('action' => 'licenseadminpanel'));
852
853             $m->connect('panel/plugins', array('action' => 'pluginsadminpanel'));
854             $m->connect('panel/plugins/enable/:plugin',
855                         array('action' => 'pluginenable'),
856                         array('plugin' => '[A-Za-z0-9_]+'));
857             $m->connect('panel/plugins/disable/:plugin',
858                         array('action' => 'plugindisable'),
859                         array('plugin' => '[A-Za-z0-9_]+'));
860
861             $m->connect('getfile/:filename',
862                         array('action' => 'getfile'),
863                         array('filename' => '[A-Za-z0-9._-]+'));
864
865             // Common people-tag stuff
866
867             $m->connect('peopletag/:tag', array('action' => 'peopletag',
868                                                 'tag'    => self::REGEX_TAG));
869
870             $m->connect('selftag/:tag', array('action' => 'selftag',
871                                               'tag'    => self::REGEX_TAG));
872
873             $m->connect('main/addpeopletag', array('action' => 'addpeopletag'));
874
875             $m->connect('main/removepeopletag', array('action' => 'removepeopletag'));
876
877             $m->connect('main/profilecompletion', array('action' => 'profilecompletion'));
878
879             $m->connect('main/peopletagautocomplete', array('action' => 'peopletagautocomplete'));
880
881             // In the "root"
882
883             if (common_config('singleuser', 'enabled')) {
884
885                 $nickname = User::singleUserNickname();
886
887                 foreach (array('subscriptions', 'subscribers',
888                                'all', 'foaf', 'replies',
889                                'microsummary') as $a) {
890                     $m->connect($a,
891                                 array('action' => $a,
892                                       'nickname' => $nickname));
893                 }
894
895                 foreach (array('subscriptions', 'subscribers') as $a) {
896                     $m->connect($a.'/:tag',
897                                 array('action' => $a,
898                                       'nickname' => $nickname),
899                                 array('tag' => self::REGEX_TAG));
900                 }
901
902                 $m->connect('subscribers/pending',
903                             array('action' => 'subqueue',
904                                   'nickname' => $nickname));
905
906                 foreach (array('rss', 'groups') as $a) {
907                     $m->connect($a,
908                                 array('action' => 'user'.$a,
909                                       'nickname' => $nickname));
910                 }
911
912                 foreach (array('all', 'replies', 'favorites') as $a) {
913                     $m->connect($a.'/rss',
914                                 array('action' => $a.'rss',
915                                       'nickname' => $nickname));
916                 }
917
918                 $m->connect('favorites',
919                             array('action' => 'showfavorites',
920                                   'nickname' => $nickname));
921
922                 $m->connect('avatar',
923                             array('action' => 'avatarbynickname',
924                                   'nickname' => $nickname));
925                 $m->connect('avatar/:size',
926                             array('action' => 'avatarbynickname',
927                                   'nickname' => $nickname),
928                             array('size' => '(|original|\d+)'));
929
930                 $m->connect('tag/:tag/rss',
931                             array('action' => 'userrss',
932                                   'nickname' => $nickname),
933                             array('tag' => self::REGEX_TAG));
934
935                 $m->connect('tag/:tag',
936                             array('action' => 'showstream',
937                                   'nickname' => $nickname),
938                             array('tag' => self::REGEX_TAG));
939
940                 $m->connect('rsd.xml',
941                             array('action' => 'rsd',
942                                   'nickname' => $nickname));
943
944                 $m->connect('',
945                             array('action' => 'showstream',
946                                   'nickname' => $nickname));
947
948                 // peopletags
949
950                 $m->connect('peopletags',
951                             array('action' => 'peopletagsbyuser'));
952
953                 $m->connect('peopletags/private',
954                             array('action' => 'peopletagsbyuser',
955                                   'private' => 1));
956
957                 $m->connect('peopletags/public',
958                             array('action' => 'peopletagsbyuser',
959                                   'public' => 1));
960
961                 $m->connect('othertags',
962                             array('action' => 'peopletagsforuser'));
963
964                 $m->connect('peopletagsubscriptions',
965                             array('action' => 'peopletagsubscriptions'));
966
967                 $m->connect('all/:tag/subscribers',
968                             array('action' => 'peopletagsubscribers',
969                                   'tag' => self::REGEX_TAG));
970
971                 $m->connect('all/:tag/tagged',
972                                 array('action' => 'peopletagged',
973                                       'tag' => self::REGEX_TAG));
974
975                 $m->connect('all/:tag/edit',
976                                 array('action' => 'editpeopletag',
977                                       'tag' => self::REGEX_TAG));
978
979                 foreach(array('subscribe', 'unsubscribe') as $v) {
980                     $m->connect('peopletag/:id/'.$v,
981                                     array('action' => $v.'peopletag',
982                                           'id' => '[0-9]{1,64}'));
983                 }
984                 $m->connect('user/:tagger_id/profiletag/:id/id',
985                                 array('action' => 'profiletagbyid',
986                                       'tagger_id' => '[0-9]+',
987                                       'id' => '[0-9]+'));
988
989                 $m->connect('all/:tag',
990                                 array('action' => 'showprofiletag',
991                                       'tag' => self::REGEX_TAG));
992
993                 foreach (array('subscriptions', 'subscribers') as $a) {
994                     $m->connect($a.'/:tag',
995                                 array('action' => $a),
996                                 array('tag' => self::REGEX_TAG));
997                 }
998             } else {
999                 $m->connect('', array('action' => 'public'));
1000                 $m->connect('rss', array('action' => 'publicrss'));
1001                 $m->connect('featuredrss', array('action' => 'featuredrss'));
1002                 $m->connect('favoritedrss', array('action' => 'favoritedrss'));
1003                 $m->connect('featured/', array('action' => 'featured'));
1004                 $m->connect('featured', array('action' => 'featured'));
1005                 $m->connect('favorited/', array('action' => 'favorited'));
1006                 $m->connect('favorited', array('action' => 'favorited'));
1007                 $m->connect('rsd.xml', array('action' => 'rsd'));
1008
1009                 foreach (array('subscriptions', 'subscribers',
1010                                'nudge', 'all', 'foaf', 'replies',
1011                                'inbox', 'outbox', 'microsummary') as $a) {
1012                     $m->connect(':nickname/'.$a,
1013                                 array('action' => $a),
1014                                 array('nickname' => Nickname::DISPLAY_FMT));
1015                 }
1016                 $m->connect(':nickname/subscribers/pending',
1017                             array('action' => 'subqueue'),
1018                             array('nickname' => Nickname::DISPLAY_FMT));
1019
1020                 // people tags
1021
1022                 $m->connect(':nickname/peopletags',
1023                                 array('action' => 'peopletagsbyuser',
1024                                       'nickname' => Nickname::DISPLAY_FMT));
1025
1026                 $m->connect(':nickname/peopletags/private',
1027                                 array('action' => 'peopletagsbyuser',
1028                                       'nickname' => Nickname::DISPLAY_FMT,
1029                                       'private' => 1));
1030
1031                 $m->connect(':nickname/peopletags/public',
1032                                 array('action' => 'peopletagsbyuser',
1033                                       'nickname' => Nickname::DISPLAY_FMT,
1034                                       'public' => 1));
1035
1036                 $m->connect(':nickname/othertags',
1037                                 array('action' => 'peopletagsforuser',
1038                                       'nickname' => Nickname::DISPLAY_FMT));
1039
1040                 $m->connect(':nickname/peopletagsubscriptions',
1041                                 array('action' => 'peopletagsubscriptions',
1042                                       'nickname' => Nickname::DISPLAY_FMT));
1043
1044                 $m->connect(':tagger/all/:tag/subscribers',
1045                                 array('action' => 'peopletagsubscribers',
1046                                       'tagger' => Nickname::DISPLAY_FMT,
1047                                       'tag' => self::REGEX_TAG));
1048
1049                 $m->connect(':tagger/all/:tag/tagged',
1050                                 array('action' => 'peopletagged',
1051                                       'tagger' => Nickname::DISPLAY_FMT,
1052                                       'tag' => self::REGEX_TAG));
1053
1054                 $m->connect(':tagger/all/:tag/edit',
1055                                 array('action' => 'editpeopletag',
1056                                       'tagger' => Nickname::DISPLAY_FMT,
1057                                       'tag' => self::REGEX_TAG));
1058
1059                 foreach(array('subscribe', 'unsubscribe') as $v) {
1060                     $m->connect('peopletag/:id/'.$v,
1061                                     array('action' => $v.'peopletag',
1062                                           'id' => '[0-9]{1,64}'));
1063                 }
1064                 $m->connect('user/:tagger_id/profiletag/:id/id',
1065                                 array('action' => 'profiletagbyid',
1066                                       'tagger_id' => '[0-9]+',
1067                                       'id' => '[0-9]+'));
1068
1069                 $m->connect(':tagger/all/:tag',
1070                                 array('action' => 'showprofiletag',
1071                                       'tagger' => Nickname::DISPLAY_FMT,
1072                                       'tag' => self::REGEX_TAG));
1073
1074                 foreach (array('subscriptions', 'subscribers') as $a) {
1075                     $m->connect(':nickname/'.$a.'/:tag',
1076                                 array('action' => $a),
1077                                 array('tag' => self::REGEX_TAG,
1078                                       'nickname' => Nickname::DISPLAY_FMT));
1079                 }
1080
1081                 foreach (array('rss', 'groups') as $a) {
1082                     $m->connect(':nickname/'.$a,
1083                                 array('action' => 'user'.$a),
1084                                 array('nickname' => Nickname::DISPLAY_FMT));
1085                 }
1086
1087                 foreach (array('all', 'replies', 'favorites') as $a) {
1088                     $m->connect(':nickname/'.$a.'/rss',
1089                                 array('action' => $a.'rss'),
1090                                 array('nickname' => Nickname::DISPLAY_FMT));
1091                 }
1092
1093                 $m->connect(':nickname/favorites',
1094                             array('action' => 'showfavorites'),
1095                             array('nickname' => Nickname::DISPLAY_FMT));
1096
1097                 $m->connect(':nickname/avatar',
1098                             array('action' => 'avatarbynickname'),
1099                             array('nickname' => Nickname::DISPLAY_FMT));
1100                 $m->connect(':nickname/avatar/:size',
1101                             array('action' => 'avatarbynickname'),
1102                             array('size' => '(|original|\d+)',
1103                                   'nickname' => Nickname::DISPLAY_FMT));
1104
1105                 $m->connect(':nickname/tag/:tag/rss',
1106                             array('action' => 'userrss'),
1107                             array('nickname' => Nickname::DISPLAY_FMT),
1108                             array('tag' => self::REGEX_TAG));
1109
1110                 $m->connect(':nickname/tag/:tag',
1111                             array('action' => 'showstream'),
1112                             array('nickname' => Nickname::DISPLAY_FMT),
1113                             array('tag' => self::REGEX_TAG));
1114
1115                 $m->connect(':nickname/rsd.xml',
1116                             array('action' => 'rsd'),
1117                             array('nickname' => Nickname::DISPLAY_FMT));
1118
1119                 $m->connect(':nickname',
1120                             array('action' => 'showstream'),
1121                             array('nickname' => Nickname::DISPLAY_FMT));
1122
1123                 $m->connect(':nickname/',
1124                             array('action' => 'showstream'),
1125                             array('nickname' => Nickname::DISPLAY_FMT));
1126             }
1127
1128             // AtomPub API
1129
1130             $m->connect('api/statusnet/app/service/:id.xml',
1131                         array('action' => 'ApiAtomService'),
1132                         array('id' => Nickname::DISPLAY_FMT));
1133
1134             $m->connect('api/statusnet/app/service.xml',
1135                         array('action' => 'ApiAtomService'));
1136
1137             $m->connect('api/statusnet/app/subscriptions/:subscriber/:subscribed.atom',
1138                         array('action' => 'AtomPubShowSubscription'),
1139                         array('subscriber' => '[0-9]+',
1140                               'subscribed' => '[0-9]+'));
1141
1142             $m->connect('api/statusnet/app/subscriptions/:subscriber.atom',
1143                         array('action' => 'AtomPubSubscriptionFeed'),
1144                         array('subscriber' => '[0-9]+'));
1145
1146             $m->connect('api/statusnet/app/favorites/:profile/:notice.atom',
1147                         array('action' => 'AtomPubShowFavorite'),
1148                         array('profile' => '[0-9]+',
1149                               'notice' => '[0-9]+'));
1150
1151             $m->connect('api/statusnet/app/favorites/:profile.atom',
1152                         array('action' => 'AtomPubFavoriteFeed'),
1153                         array('profile' => '[0-9]+'));
1154
1155             $m->connect('api/statusnet/app/memberships/:profile/:group.atom',
1156                         array('action' => 'AtomPubShowMembership'),
1157                         array('profile' => '[0-9]+',
1158                               'group' => '[0-9]+'));
1159
1160             $m->connect('api/statusnet/app/memberships/:profile.atom',
1161                         array('action' => 'AtomPubMembershipFeed'),
1162                         array('profile' => '[0-9]+'));
1163
1164             // URL shortening
1165
1166             $m->connect('url/:id',
1167                         array('action' => 'redirecturl',
1168                               'id' => '[0-9]+'));
1169
1170             // user stuff
1171
1172             Event::handle('RouterInitialized', array($m));
1173         }
1174
1175         return $m;
1176     }
1177
1178     function map($path)
1179     {
1180         try {
1181             $match = $this->m->match($path);
1182         } catch (Exception $e) {
1183             common_log(LOG_ERR, "Problem getting route for $path - " .
1184                        $e->getMessage());
1185             // TRANS: Client error on action trying to visit a non-existing page.
1186             $cac = new ClientErrorAction(_('Page not found.'), 404);
1187             $cac->showPage();
1188         }
1189
1190         return $match;
1191     }
1192
1193     function build($action, $args=null, $params=null, $fragment=null)
1194     {
1195         $action_arg = array('action' => $action);
1196
1197         if ($args) {
1198             $args = array_merge($action_arg, $args);
1199         } else {
1200             $args = $action_arg;
1201         }
1202
1203         $url = $this->m->generate($args, $params, $fragment);
1204         // Due to a bug in the Net_URL_Mapper code, the returned URL may
1205         // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
1206         // repair that here rather than modifying the upstream code...
1207
1208         $qpos = strpos($url, '?');
1209         if ($qpos !== false) {
1210             $url = substr($url, 0, $qpos+1) .
1211                 str_replace('?', '&', substr($url, $qpos+1));
1212
1213             // @fixme this is a hacky workaround for http_build_query in the
1214             // lower-level code and bad configs that set the default separator
1215             // to &amp; instead of &. Encoded &s in parameters will not be
1216             // affected.
1217             $url = substr($url, 0, $qpos+1) .
1218                 str_replace('&amp;', '&', substr($url, $qpos+1));
1219
1220         }
1221
1222         return $url;
1223     }
1224 }