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