]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/router.php
Merge branch '0.9.x' into righttoleave
[quix0rs-gnu-social.git] / lib / router.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * URL routing utilities
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  URL
23  * @package   StatusNet
24  * @author    Evan Prodromou <evan@status.net>
25  * @copyright 2009 StatusNet, Inc.
26  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
27  * @link      http://status.net/
28  */
29
30 if (!defined('STATUSNET') && !defined('LACONICA')) {
31     exit(1);
32 }
33
34 require_once 'Net/URL/Mapper.php';
35
36 class StatusNet_URL_Mapper extends Net_URL_Mapper
37 {
38     private static $_singleton = null;
39     private $_actionToPath = array();
40
41     private function __construct()
42     {
43     }
44     
45     public static function getInstance($id = '__default__')
46     {
47         if (empty(self::$_singleton)) {
48             self::$_singleton = new StatusNet_URL_Mapper();
49         }
50         return self::$_singleton;
51     }
52
53     public function connect($path, $defaults = array(), $rules = array())
54     {
55         $result = null;
56         if (Event::handle('StartConnectPath', array(&$path, &$defaults, &$rules, &$result))) {
57             $result = parent::connect($path, $defaults, $rules);
58             if (array_key_exists('action', $defaults)) {
59                 $action = $defaults['action'];
60             } elseif (array_key_exists('action', $rules)) {
61                 $action = $rules['action'];
62             } else {
63                 $action = null;
64             }
65             $this->_mapAction($action, $result);
66             Event::handle('EndConnectPath', array($path, $defaults, $rules, $result));
67         }
68         return $result;
69     }
70     
71     protected function _mapAction($action, $path)
72     {
73         if (!array_key_exists($action, $this->_actionToPath)) {
74             $this->_actionToPath[$action] = array();
75         }
76         $this->_actionToPath[$action][] = $path;
77         return;
78     }
79     
80     public function generate($values = array(), $qstring = array(), $anchor = '')
81     {
82         if (!array_key_exists('action', $values)) {
83             return parent::generate($values, $qstring, $anchor);
84         }
85         
86         $action = $values['action'];
87
88         if (!array_key_exists($action, $this->_actionToPath)) {
89             return parent::generate($values, $qstring, $anchor);
90         }
91         
92         $oldPaths    = $this->paths;
93         $this->paths = $this->_actionToPath[$action];
94         $result      = parent::generate($values, $qstring, $anchor);
95         $this->paths = $oldPaths;
96
97         return $result;
98     }
99 }
100
101 /**
102  * URL Router
103  *
104  * Cheap wrapper around Net_URL_Mapper
105  *
106  * @category URL
107  * @package  StatusNet
108  * @author   Evan Prodromou <evan@status.net>
109  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
110  * @link     http://status.net/
111  */
112 class Router
113 {
114     var $m = null;
115     static $inst = null;
116     static $bare = array('requesttoken', 'accesstoken', 'userauthorization',
117                          'postnotice', 'updateprofile', 'finishremotesubscribe');
118
119     static function get()
120     {
121         if (!Router::$inst) {
122             Router::$inst = new Router();
123         }
124         return Router::$inst;
125     }
126
127     function __construct()
128     {
129         if (empty($this->m)) {
130             if (!common_config('router', 'cache')) {
131                 $this->m = $this->initialize();
132             } else {
133                 $k = self::cacheKey();
134                 $c = Cache::instance();
135                 $m = $c->get($k);
136                 if (!empty($m)) {
137                     $this->m = $m;
138                 } else {
139                     $this->m = $this->initialize();
140                     $c->set($k, $this->m);
141                 }
142             }
143         }
144     }
145
146     /**
147      * Create a unique hashkey for the router.
148      * 
149      * The router's url map can change based on the version of the software
150      * you're running and the plugins that are enabled. To avoid having bad routes
151      * get stuck in the cache, the key includes a list of plugins and the software
152      * version.
153      * 
154      * There can still be problems with a) differences in versions of the plugins and 
155      * b) people running code between official versions, but these tend to be more
156      * sophisticated users who can grok what's going on and clear their caches.
157      * 
158      * @return string cache key string that should uniquely identify a router
159      */
160     
161     static function cacheKey()
162     {
163         return Cache::codeKey('router');
164     }
165     
166     function initialize()
167     {
168         $m = StatusNet_URL_Mapper::getInstance();
169
170         if (Event::handle('StartInitializeRouter', array(&$m))) {
171
172             $m->connect('robots.txt', array('action' => 'robotstxt'));
173
174             $m->connect('opensearch/people', array('action' => 'opensearch',
175                                                    'type' => 'people'));
176             $m->connect('opensearch/notice', array('action' => 'opensearch',
177                                                    'type' => 'notice'));
178
179             // docs
180
181             $m->connect('doc/:title', array('action' => 'doc'));
182
183             $m->connect('main/otp/:user_id/:token',
184                         array('action' => 'otp'),
185                         array('user_id' => '[0-9]+',
186                               'token' => '.+'));
187
188             // main stuff is repetitive
189
190             $main = array('login', 'logout', 'register', 'subscribe',
191                           'unsubscribe', 'confirmaddress', 'recoverpassword',
192                           'invite', 'favor', 'disfavor', 'sup',
193                           'block', 'unblock', 'subedit',
194                           'groupblock', 'groupunblock',
195                           'sandbox', 'unsandbox',
196                           'silence', 'unsilence',
197                           'grantrole', 'revokerole',
198                           'repeat',
199                           'deleteuser',
200                           'geocode',
201                           'version',
202                           'backupaccount',
203                           'deleteaccount',
204             );
205
206             foreach ($main as $a) {
207                 $m->connect('main/'.$a, array('action' => $a));
208             }
209
210             // Also need a block variant accepting ID on URL for mail links
211             $m->connect('main/block/:profileid',
212                         array('action' => 'block'),
213                         array('profileid' => '[0-9]+'));
214
215             $m->connect('main/sup/:seconds', array('action' => 'sup'),
216                         array('seconds' => '[0-9]+'));
217
218             $m->connect('main/tagother/:id', array('action' => 'tagother'));
219
220             $m->connect('main/oembed',
221                         array('action' => 'oembed'));
222
223             $m->connect('main/xrds',
224                         array('action' => 'publicxrds'));
225             $m->connect('.well-known/host-meta',
226                         array('action' => 'hostmeta'));
227             $m->connect('main/xrd',
228                         array('action' => 'userxrd'));
229
230             // these take a code
231
232             foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) {
233                 $m->connect('main/'.$c.'/:code', array('action' => $c));
234             }
235
236             // exceptional
237
238             $m->connect('main/remote', array('action' => 'remotesubscribe'));
239             $m->connect('main/remote?nickname=:nickname', array('action' => 'remotesubscribe'), array('nickname' => '[A-Za-z0-9_-]+'));
240
241             foreach (Router::$bare as $action) {
242                 $m->connect('index.php?action=' . $action, array('action' => $action));
243             }
244
245             // settings
246
247             foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections',
248                            'oauthapps', 'email', 'sms', 'userdesign', 'other') as $s) {
249                 $m->connect('settings/'.$s, array('action' => $s.'settings'));
250             }
251
252             $m->connect('settings/oauthapps/show/:id',
253                         array('action' => 'showapplication'),
254                         array('id' => '[0-9]+')
255             );
256             $m->connect('settings/oauthapps/new',
257                         array('action' => 'newapplication')
258             );
259             $m->connect('settings/oauthapps/edit/:id',
260                         array('action' => 'editapplication'),
261                         array('id' => '[0-9]+')
262             );
263             $m->connect('settings/oauthapps/delete/:id',
264                         array('action' => 'deleteapplication'),
265                         array('id' => '[0-9]+')
266             );
267
268             // search
269
270             foreach (array('group', 'people', 'notice') as $s) {
271                 $m->connect('search/'.$s, array('action' => $s.'search'));
272                 $m->connect('search/'.$s.'?q=:q',
273                             array('action' => $s.'search'),
274                             array('q' => '.+'));
275             }
276
277             // The second of these is needed to make the link work correctly
278             // when inserted into the page. The first is needed to match the
279             // route on the way in. Seems to be another Net_URL_Mapper bug to me.
280             $m->connect('search/notice/rss', array('action' => 'noticesearchrss'));
281             $m->connect('search/notice/rss?q=:q', array('action' => 'noticesearchrss'),
282                         array('q' => '.+'));
283
284             $m->connect('attachment/:attachment',
285                         array('action' => 'attachment'),
286                         array('attachment' => '[0-9]+'));
287
288             $m->connect('attachment/:attachment/ajax',
289                         array('action' => 'attachment_ajax'),
290                         array('attachment' => '[0-9]+'));
291
292             $m->connect('attachment/:attachment/thumbnail',
293                         array('action' => 'attachment_thumbnail'),
294                         array('attachment' => '[0-9]+'));
295
296             $m->connect('notice/new', array('action' => 'newnotice'));
297             $m->connect('notice/new?replyto=:replyto',
298                         array('action' => 'newnotice'),
299                         array('replyto' => Nickname::DISPLAY_FMT));
300             $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto',
301                         array('action' => 'newnotice'),
302                         array('replyto' => Nickname::DISPLAY_FMT),
303                         array('inreplyto' => '[0-9]+'));
304
305             $m->connect('notice/:notice/file',
306                         array('action' => 'file'),
307                         array('notice' => '[0-9]+'));
308
309             $m->connect('notice/:notice',
310                         array('action' => 'shownotice'),
311                         array('notice' => '[0-9]+'));
312             $m->connect('notice/delete', array('action' => 'deletenotice'));
313             $m->connect('notice/delete/:notice',
314                         array('action' => 'deletenotice'),
315                         array('notice' => '[0-9]+'));
316
317             $m->connect('bookmarklet/new', array('action' => 'bookmarklet'));
318
319             // conversation
320
321             $m->connect('conversation/:id',
322                         array('action' => 'conversation'),
323                         array('id' => '[0-9]+'));
324
325             $m->connect('message/new', array('action' => 'newmessage'));
326             $m->connect('message/new?to=:to', array('action' => 'newmessage'), array('to' => Nickname::DISPLAY_FMT));
327             $m->connect('message/:message',
328                         array('action' => 'showmessage'),
329                         array('message' => '[0-9]+'));
330
331             $m->connect('user/:id',
332                         array('action' => 'userbyid'),
333                         array('id' => '[0-9]+'));
334
335             $m->connect('tags/', array('action' => 'publictagcloud'));
336             $m->connect('tag/', array('action' => 'publictagcloud'));
337             $m->connect('tags', array('action' => 'publictagcloud'));
338             $m->connect('tag', array('action' => 'publictagcloud'));
339             $m->connect('tag/:tag/rss',
340                         array('action' => 'tagrss'),
341                         array('tag' => '[\pL\pN_\-\.]{1,64}'));
342             $m->connect('tag/:tag',
343                         array('action' => 'tag'),
344                         array('tag' => '[\pL\pN_\-\.]{1,64}'));
345
346             $m->connect('peopletag/:tag',
347                         array('action' => 'peopletag'),
348                         array('tag' => '[a-zA-Z0-9]+'));
349
350             // groups
351
352             $m->connect('group/new', array('action' => 'newgroup'));
353
354             foreach (array('edit', 'join', 'leave', 'delete') as $v) {
355                 $m->connect('group/:nickname/'.$v,
356                             array('action' => $v.'group'),
357                             array('nickname' => Nickname::DISPLAY_FMT));
358                 $m->connect('group/:id/id/'.$v,
359                             array('action' => $v.'group'),
360                             array('id' => '[0-9]+'));
361             }
362
363             foreach (array('members', 'logo', 'rss', 'designsettings') as $n) {
364                 $m->connect('group/:nickname/'.$n,
365                             array('action' => 'group'.$n),
366                             array('nickname' => Nickname::DISPLAY_FMT));
367             }
368
369             $m->connect('group/:nickname/foaf',
370                         array('action' => 'foafgroup'),
371                         array('nickname' => Nickname::DISPLAY_FMT));
372
373             $m->connect('group/:nickname/blocked',
374                         array('action' => 'blockedfromgroup'),
375                         array('nickname' => Nickname::DISPLAY_FMT));
376
377             $m->connect('group/:nickname/makeadmin',
378                         array('action' => 'makeadmin'),
379                         array('nickname' => Nickname::DISPLAY_FMT));
380
381             $m->connect('group/:id/id',
382                         array('action' => 'groupbyid'),
383                         array('id' => '[0-9]+'));
384
385             $m->connect('group/:nickname',
386                         array('action' => 'showgroup'),
387                         array('nickname' => Nickname::DISPLAY_FMT));
388
389             $m->connect('group/', array('action' => 'groups'));
390             $m->connect('group', array('action' => 'groups'));
391             $m->connect('groups/', array('action' => 'groups'));
392             $m->connect('groups', array('action' => 'groups'));
393
394             // Twitter-compatible API
395
396             // statuses API
397
398             $m->connect('api/statuses/public_timeline.:format',
399                         array('action' => 'ApiTimelinePublic',
400                               'format' => '(xml|json|rss|atom)'));
401
402             $m->connect('api/statuses/friends_timeline.:format',
403                         array('action' => 'ApiTimelineFriends',
404                               'format' => '(xml|json|rss|atom)'));
405
406             $m->connect('api/statuses/friends_timeline/:id.:format',
407                         array('action' => 'ApiTimelineFriends',
408                               'id' => Nickname::INPUT_FMT,
409                               'format' => '(xml|json|rss|atom)'));
410
411             $m->connect('api/statuses/home_timeline.:format',
412                         array('action' => 'ApiTimelineHome',
413                               'format' => '(xml|json|rss|atom)'));
414
415             $m->connect('api/statuses/home_timeline/:id.:format',
416                         array('action' => 'ApiTimelineHome',
417                               'id' => Nickname::INPUT_FMT,
418                               'format' => '(xml|json|rss|atom)'));
419
420             $m->connect('api/statuses/user_timeline.:format',
421                         array('action' => 'ApiTimelineUser',
422                               'format' => '(xml|json|rss|atom)'));
423
424             $m->connect('api/statuses/user_timeline/:id.:format',
425                         array('action' => 'ApiTimelineUser',
426                               'id' => Nickname::INPUT_FMT,
427                               'format' => '(xml|json|rss|atom)'));
428
429             $m->connect('api/statuses/mentions.:format',
430                         array('action' => 'ApiTimelineMentions',
431                               'format' => '(xml|json|rss|atom)'));
432
433             $m->connect('api/statuses/mentions/:id.:format',
434                         array('action' => 'ApiTimelineMentions',
435                               'id' => Nickname::INPUT_FMT,
436                               'format' => '(xml|json|rss|atom)'));
437
438             $m->connect('api/statuses/replies.:format',
439                         array('action' => 'ApiTimelineMentions',
440                               'format' => '(xml|json|rss|atom)'));
441
442             $m->connect('api/statuses/replies/:id.:format',
443                         array('action' => 'ApiTimelineMentions',
444                               'id' => Nickname::INPUT_FMT,
445                               'format' => '(xml|json|rss|atom)'));
446
447             $m->connect('api/statuses/retweeted_by_me.:format',
448                         array('action' => 'ApiTimelineRetweetedByMe',
449                               'format' => '(xml|json|atom)'));
450
451             $m->connect('api/statuses/retweeted_to_me.:format',
452                         array('action' => 'ApiTimelineRetweetedToMe',
453                               'format' => '(xml|json|atom)'));
454
455             $m->connect('api/statuses/retweets_of_me.:format',
456                         array('action' => 'ApiTimelineRetweetsOfMe',
457                               'format' => '(xml|json|atom)'));
458
459             $m->connect('api/statuses/friends.:format',
460                         array('action' => 'ApiUserFriends',
461                               'format' => '(xml|json)'));
462
463             $m->connect('api/statuses/friends/:id.:format',
464                         array('action' => 'ApiUserFriends',
465                               'id' => Nickname::INPUT_FMT,
466                               'format' => '(xml|json)'));
467
468             $m->connect('api/statuses/followers.:format',
469                         array('action' => 'ApiUserFollowers',
470                               'format' => '(xml|json)'));
471
472             $m->connect('api/statuses/followers/:id.:format',
473                         array('action' => 'ApiUserFollowers',
474                               'id' => Nickname::INPUT_FMT,
475                               'format' => '(xml|json)'));
476
477             $m->connect('api/statuses/show.:format',
478                         array('action' => 'ApiStatusesShow',
479                               'format' => '(xml|json|atom)'));
480
481             $m->connect('api/statuses/show/:id.:format',
482                         array('action' => 'ApiStatusesShow',
483                               'id' => '[0-9]+',
484                               'format' => '(xml|json|atom)'));
485
486             $m->connect('api/statuses/update.:format',
487                         array('action' => 'ApiStatusesUpdate',
488                               'format' => '(xml|json)'));
489
490             $m->connect('api/statuses/destroy.:format',
491                         array('action' => 'ApiStatusesDestroy',
492                               'format' => '(xml|json)'));
493
494             $m->connect('api/statuses/destroy/:id.:format',
495                         array('action' => 'ApiStatusesDestroy',
496                               'id' => '[0-9]+',
497                               'format' => '(xml|json)'));
498
499             $m->connect('api/statuses/retweet/:id.:format',
500                         array('action' => 'ApiStatusesRetweet',
501                               'id' => '[0-9]+',
502                               'format' => '(xml|json)'));
503
504             $m->connect('api/statuses/retweets/:id.:format',
505                         array('action' => 'ApiStatusesRetweets',
506                               'id' => '[0-9]+',
507                               'format' => '(xml|json)'));
508
509             // users
510
511             $m->connect('api/users/show.:format',
512                         array('action' => 'ApiUserShow',
513                               'format' => '(xml|json)'));
514
515             $m->connect('api/users/show/:id.:format',
516                         array('action' => 'ApiUserShow',
517                               'id' => Nickname::INPUT_FMT,
518                               'format' => '(xml|json)'));
519
520             // direct messages
521
522             $m->connect('api/direct_messages.:format',
523                         array('action' => 'ApiDirectMessage',
524                               'format' => '(xml|json|rss|atom)'));
525
526             $m->connect('api/direct_messages/sent.:format',
527                         array('action' => 'ApiDirectMessage',
528                               'format' => '(xml|json|rss|atom)',
529                               'sent' => true));
530
531             $m->connect('api/direct_messages/new.:format',
532                         array('action' => 'ApiDirectMessageNew',
533                               'format' => '(xml|json)'));
534
535             // friendships
536
537             $m->connect('api/friendships/show.:format',
538                         array('action' => 'ApiFriendshipsShow',
539                               'format' => '(xml|json)'));
540
541             $m->connect('api/friendships/exists.:format',
542                         array('action' => 'ApiFriendshipsExists',
543                               'format' => '(xml|json)'));
544
545             $m->connect('api/friendships/create.:format',
546                         array('action' => 'ApiFriendshipsCreate',
547                               'format' => '(xml|json)'));
548
549             $m->connect('api/friendships/destroy.:format',
550                         array('action' => 'ApiFriendshipsDestroy',
551                               'format' => '(xml|json)'));
552
553             $m->connect('api/friendships/create/:id.:format',
554                         array('action' => 'ApiFriendshipsCreate',
555                               'id' => Nickname::INPUT_FMT,
556                               'format' => '(xml|json)'));
557
558             $m->connect('api/friendships/destroy/:id.:format',
559                         array('action' => 'ApiFriendshipsDestroy',
560                               'id' => Nickname::INPUT_FMT,
561                               'format' => '(xml|json)'));
562
563             // Social graph
564
565             $m->connect('api/friends/ids/:id.:format',
566                         array('action' => 'ApiUserFriends',
567                               'ids_only' => true));
568
569             $m->connect('api/followers/ids/:id.:format',
570                         array('action' => 'ApiUserFollowers',
571                               'ids_only' => true));
572
573             $m->connect('api/friends/ids.:format',
574                         array('action' => 'ApiUserFriends',
575                               'ids_only' => true));
576
577             $m->connect('api/followers/ids.:format',
578                         array('action' => 'ApiUserFollowers',
579                               'ids_only' => true));
580
581             // account
582
583             $m->connect('api/account/verify_credentials.:format',
584                         array('action' => 'ApiAccountVerifyCredentials'));
585
586             $m->connect('api/account/update_profile.:format',
587                         array('action' => 'ApiAccountUpdateProfile'));
588
589             $m->connect('api/account/update_profile_image.:format',
590                         array('action' => 'ApiAccountUpdateProfileImage'));
591
592             $m->connect('api/account/update_profile_background_image.:format',
593                         array('action' => 'ApiAccountUpdateProfileBackgroundImage'));
594
595             $m->connect('api/account/update_profile_colors.:format',
596                         array('action' => 'ApiAccountUpdateProfileColors'));
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.:format',
612                         array('action' => 'ApiTimelineFavorites',
613                               'format' => '(xml|json|rss|atom)'));
614
615             $m->connect('api/favorites/:id.:format',
616                         array('action' => 'ApiTimelineFavorites',
617                               'id' => Nickname::INPUT_FMT,
618                               'format' => '(xml|json|rss|atom)'));
619
620             $m->connect('api/favorites/create/:id.:format',
621                         array('action' => 'ApiFavoriteCreate',
622                               'id' => '[0-9]+',
623                               'format' => '(xml|json)'));
624
625             $m->connect('api/favorites/destroy/:id.:format',
626                         array('action' => 'ApiFavoriteDestroy',
627                               'id' => '[0-9]+',
628                               'format' => '(xml|json)'));
629             // blocks
630
631             $m->connect('api/blocks/create.:format',
632                         array('action' => 'ApiBlockCreate',
633                               'format' => '(xml|json)'));
634
635             $m->connect('api/blocks/create/:id.:format',
636                         array('action' => 'ApiBlockCreate',
637                               'id' => Nickname::INPUT_FMT,
638                               'format' => '(xml|json)'));
639
640             $m->connect('api/blocks/destroy.:format',
641                         array('action' => 'ApiBlockDestroy',
642                               'format' => '(xml|json)'));
643
644             $m->connect('api/blocks/destroy/:id.:format',
645                         array('action' => 'ApiBlockDestroy',
646                               'id' => Nickname::INPUT_FMT,
647                               'format' => '(xml|json)'));
648             // help
649
650             $m->connect('api/help/test.:format',
651                         array('action' => 'ApiHelpTest',
652                               'format' => '(xml|json)'));
653
654             // statusnet
655
656             $m->connect('api/statusnet/version.:format',
657                         array('action' => 'ApiStatusnetVersion',
658                               'format' => '(xml|json)'));
659
660             $m->connect('api/statusnet/config.:format',
661                         array('action' => 'ApiStatusnetConfig',
662                               'format' => '(xml|json)'));
663
664             // For older methods, we provide "laconica" base action
665
666             $m->connect('api/laconica/version.:format',
667                         array('action' => 'ApiStatusnetVersion',
668                               'format' => '(xml|json)'));
669
670             $m->connect('api/laconica/config.:format',
671                         array('action' => 'ApiStatusnetConfig',
672                               'format' => '(xml|json)'));
673
674             // Groups and tags are newer than 0.8.1 so no backward-compatibility
675             // necessary
676
677             // Groups
678             //'list' has to be handled differently, as php will not allow a method to be named 'list'
679
680             $m->connect('api/statusnet/groups/timeline/:id.:format',
681                         array('action' => 'ApiTimelineGroup',
682                               'id' => Nickname::INPUT_FMT,
683                               'format' => '(xml|json|rss|atom)'));
684
685             $m->connect('api/statusnet/groups/show.:format',
686                         array('action' => 'ApiGroupShow',
687                               'format' => '(xml|json)'));
688
689             $m->connect('api/statusnet/groups/show/:id.:format',
690                         array('action' => 'ApiGroupShow',
691                               'id' => Nickname::INPUT_FMT,
692                               'format' => '(xml|json)'));
693
694             $m->connect('api/statusnet/groups/join.:format',
695                         array('action' => 'ApiGroupJoin',
696                               'id' => Nickname::INPUT_FMT,
697                               'format' => '(xml|json)'));
698
699             $m->connect('api/statusnet/groups/join/:id.:format',
700                         array('action' => 'ApiGroupJoin',
701                               'format' => '(xml|json)'));
702
703             $m->connect('api/statusnet/groups/leave.:format',
704                         array('action' => 'ApiGroupLeave',
705                               'id' => Nickname::INPUT_FMT,
706                               'format' => '(xml|json)'));
707
708             $m->connect('api/statusnet/groups/leave/:id.:format',
709                         array('action' => 'ApiGroupLeave',
710                               'format' => '(xml|json)'));
711
712             $m->connect('api/statusnet/groups/is_member.:format',
713                         array('action' => 'ApiGroupIsMember',
714                               'format' => '(xml|json)'));
715
716             $m->connect('api/statusnet/groups/list.:format',
717                         array('action' => 'ApiGroupList',
718                               'format' => '(xml|json|rss|atom)'));
719
720             $m->connect('api/statusnet/groups/list/:id.:format',
721                         array('action' => 'ApiGroupList',
722                               'id' => Nickname::INPUT_FMT,
723                               'format' => '(xml|json|rss|atom)'));
724
725             $m->connect('api/statusnet/groups/list_all.:format',
726                         array('action' => 'ApiGroupListAll',
727                               'format' => '(xml|json|rss|atom)'));
728
729             $m->connect('api/statusnet/groups/membership.:format',
730                         array('action' => 'ApiGroupMembership',
731                               'format' => '(xml|json)'));
732
733             $m->connect('api/statusnet/groups/membership/:id.:format',
734                         array('action' => 'ApiGroupMembership',
735                               'id' => Nickname::INPUT_FMT,
736                               'format' => '(xml|json)'));
737
738             $m->connect('api/statusnet/groups/create.:format',
739                         array('action' => 'ApiGroupCreate',
740                               'format' => '(xml|json)'));
741             // Tags
742             $m->connect('api/statusnet/tags/timeline/:tag.:format',
743                         array('action' => 'ApiTimelineTag',
744                               'format' => '(xml|json|rss|atom)'));
745
746             // media related
747             $m->connect(
748                 'api/statusnet/media/upload',
749                 array('action' => 'ApiMediaUpload')
750             );
751
752             // search
753             $m->connect('api/search.atom', array('action' => 'ApiSearchAtom'));
754             $m->connect('api/search.json', array('action' => 'ApiSearchJSON'));
755             $m->connect('api/trends.json', array('action' => 'ApiTrends'));
756
757             $m->connect('api/oauth/request_token',
758                         array('action' => 'ApiOauthRequestToken'));
759
760             $m->connect('api/oauth/access_token',
761                         array('action' => 'ApiOauthAccessToken'));
762
763             $m->connect('api/oauth/authorize',
764                         array('action' => 'ApiOauthAuthorize'));
765
766             // Admin
767
768             $m->connect('admin/site', array('action' => 'siteadminpanel'));
769             $m->connect('admin/design', array('action' => 'designadminpanel'));
770             $m->connect('admin/user', array('action' => 'useradminpanel'));
771                 $m->connect('admin/access', array('action' => 'accessadminpanel'));
772             $m->connect('admin/paths', array('action' => 'pathsadminpanel'));
773             $m->connect('admin/sessions', array('action' => 'sessionsadminpanel'));
774             $m->connect('admin/sitenotice', array('action' => 'sitenoticeadminpanel'));
775             $m->connect('admin/snapshot', array('action' => 'snapshotadminpanel'));
776             $m->connect('admin/license', array('action' => 'licenseadminpanel'));
777
778             $m->connect('getfile/:filename',
779                         array('action' => 'getfile'),
780                         array('filename' => '[A-Za-z0-9._-]+'));
781
782             // In the "root"
783
784             if (common_config('singleuser', 'enabled')) {
785
786                 $nickname = User::singleUserNickname();
787
788                 foreach (array('subscriptions', 'subscribers',
789                                'all', 'foaf', 'xrds',
790                                'replies', 'microsummary', 'hcard') as $a) {
791                     $m->connect($a,
792                                 array('action' => $a,
793                                       'nickname' => $nickname));
794                 }
795
796                 foreach (array('subscriptions', 'subscribers') as $a) {
797                     $m->connect($a.'/:tag',
798                                 array('action' => $a,
799                                       'nickname' => $nickname),
800                                 array('tag' => '[a-zA-Z0-9]+'));
801                 }
802
803                 foreach (array('rss', 'groups') as $a) {
804                     $m->connect($a,
805                                 array('action' => 'user'.$a,
806                                       'nickname' => $nickname));
807                 }
808
809                 foreach (array('all', 'replies', 'favorites') as $a) {
810                     $m->connect($a.'/rss',
811                                 array('action' => $a.'rss',
812                                       'nickname' => $nickname));
813                 }
814
815                 $m->connect('favorites',
816                             array('action' => 'showfavorites',
817                                   'nickname' => $nickname));
818
819                 $m->connect('avatar/:size',
820                             array('action' => 'avatarbynickname',
821                                   'nickname' => $nickname),
822                             array('size' => '(original|96|48|24)'));
823
824                 $m->connect('tag/:tag/rss',
825                             array('action' => 'userrss',
826                                   'nickname' => $nickname),
827                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
828
829                 $m->connect('tag/:tag',
830                             array('action' => 'showstream',
831                                   'nickname' => $nickname),
832                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
833
834                 $m->connect('rsd.xml',
835                             array('action' => 'rsd',
836                                   'nickname' => $nickname));
837
838                 $m->connect('',
839                             array('action' => 'showstream',
840                                   'nickname' => $nickname));
841             } else {
842                 $m->connect('', array('action' => 'public'));
843                 $m->connect('rss', array('action' => 'publicrss'));
844                 $m->connect('featuredrss', array('action' => 'featuredrss'));
845                 $m->connect('favoritedrss', array('action' => 'favoritedrss'));
846                 $m->connect('featured/', array('action' => 'featured'));
847                 $m->connect('featured', array('action' => 'featured'));
848                 $m->connect('favorited/', array('action' => 'favorited'));
849                 $m->connect('favorited', array('action' => 'favorited'));
850                 $m->connect('rsd.xml', array('action' => 'rsd'));
851
852                 foreach (array('subscriptions', 'subscribers',
853                                'nudge', 'all', 'foaf', 'xrds',
854                                'replies', 'inbox', 'outbox', 'microsummary', 'hcard') as $a) {
855                     $m->connect(':nickname/'.$a,
856                                 array('action' => $a),
857                                 array('nickname' => Nickname::DISPLAY_FMT));
858                 }
859
860                 foreach (array('subscriptions', 'subscribers') as $a) {
861                     $m->connect(':nickname/'.$a.'/:tag',
862                                 array('action' => $a),
863                                 array('tag' => '[a-zA-Z0-9]+',
864                                       'nickname' => Nickname::DISPLAY_FMT));
865                 }
866
867                 foreach (array('rss', 'groups') as $a) {
868                     $m->connect(':nickname/'.$a,
869                                 array('action' => 'user'.$a),
870                                 array('nickname' => Nickname::DISPLAY_FMT));
871                 }
872
873                 foreach (array('all', 'replies', 'favorites') as $a) {
874                     $m->connect(':nickname/'.$a.'/rss',
875                                 array('action' => $a.'rss'),
876                                 array('nickname' => Nickname::DISPLAY_FMT));
877                 }
878
879                 $m->connect(':nickname/favorites',
880                             array('action' => 'showfavorites'),
881                             array('nickname' => Nickname::DISPLAY_FMT));
882
883                 $m->connect(':nickname/avatar/:size',
884                             array('action' => 'avatarbynickname'),
885                             array('size' => '(original|96|48|24)',
886                                   'nickname' => Nickname::DISPLAY_FMT));
887
888                 $m->connect(':nickname/tag/:tag/rss',
889                             array('action' => 'userrss'),
890                             array('nickname' => Nickname::DISPLAY_FMT),
891                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
892
893                 $m->connect(':nickname/tag/:tag',
894                             array('action' => 'showstream'),
895                             array('nickname' => Nickname::DISPLAY_FMT),
896                             array('tag' => '[\pL\pN_\-\.]{1,64}'));
897
898                 $m->connect(':nickname/rsd.xml',
899                             array('action' => 'rsd'),
900                             array('nickname' => Nickname::DISPLAY_FMT));
901
902                 $m->connect(':nickname',
903                             array('action' => 'showstream'),
904                             array('nickname' => Nickname::DISPLAY_FMT));
905             }
906
907             // AtomPub API
908
909             $m->connect('api/statusnet/app/service/:id.xml',
910                         array('action' => 'ApiAtomService'),
911                         array('id' => Nickname::DISPLAY_FMT));
912
913             $m->connect('api/statusnet/app/service.xml',
914                         array('action' => 'ApiAtomService'));
915
916             $m->connect('api/statusnet/app/subscriptions/:subscriber/:subscribed.atom',
917                         array('action' => 'AtomPubShowSubscription'),
918                         array('subscriber' => '[0-9]+',
919                               'subscribed' => '[0-9]+'));
920
921             $m->connect('api/statusnet/app/subscriptions/:subscriber.atom',
922                         array('action' => 'AtomPubSubscriptionFeed'),
923                         array('subscriber' => '[0-9]+'));
924
925             $m->connect('api/statusnet/app/favorites/:profile/:notice.atom',
926                         array('action' => 'AtomPubShowFavorite'),
927                         array('profile' => '[0-9]+',
928                               'notice' => '[0-9]+'));
929
930             $m->connect('api/statusnet/app/favorites/:profile.atom',
931                         array('action' => 'AtomPubFavoriteFeed'),
932                         array('profile' => '[0-9]+'));
933
934             $m->connect('api/statusnet/app/memberships/:profile/:group.atom',
935                         array('action' => 'AtomPubShowMembership'),
936                         array('profile' => '[0-9]+',
937                               'group' => '[0-9]+'));
938
939             $m->connect('api/statusnet/app/memberships/:profile.atom',
940                         array('action' => 'AtomPubMembershipFeed'),
941                         array('profile' => '[0-9]+'));
942
943             // user stuff
944
945             Event::handle('RouterInitialized', array($m));
946         }
947
948         return $m;
949     }
950
951     function map($path)
952     {
953         try {
954             $match = $this->m->match($path);
955         } catch (Net_URL_Mapper_InvalidException $e) {
956             common_log(LOG_ERR, "Problem getting route for $path - " .
957                        $e->getMessage());
958             // TRANS: Client error on action trying to visit a non-existing page.
959             $cac = new ClientErrorAction(_('Page not found.'), 404);
960             $cac->showPage();
961         }
962
963         return $match;
964     }
965
966     function build($action, $args=null, $params=null, $fragment=null)
967     {
968         $action_arg = array('action' => $action);
969
970         if ($args) {
971             $args = array_merge($action_arg, $args);
972         } else {
973             $args = $action_arg;
974         }
975
976         $url = $this->m->generate($args, $params, $fragment);
977
978         // Due to a bug in the Net_URL_Mapper code, the returned URL may
979         // contain a malformed query of the form ?p1=v1?p2=v2?p3=v3. We
980         // repair that here rather than modifying the upstream code...
981
982         $qpos = strpos($url, '?');
983         if ($qpos !== false) {
984             $url = substr($url, 0, $qpos+1) .
985                 str_replace('?', '&', substr($url, $qpos+1));
986
987             // @fixme this is a hacky workaround for http_build_query in the
988             // lower-level code and bad configs that set the default separator
989             // to &amp; instead of &. Encoded &s in parameters will not be
990             // affected.
991             $url = substr($url, 0, $qpos+1) .
992                 str_replace('&amp;', '&', substr($url, $qpos+1));
993
994         }
995
996         return $url;
997     }
998 }