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