]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/command.php
Bug tracker link updated.
[quix0rs-gnu-social.git] / lib / command.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2008, 2009, 2010 StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
21
22 require_once(INSTALLDIR.'/lib/channel.php');
23
24 class Command
25 {
26     protected $scoped = null;   // The Profile of the user performing the command
27     var $user = null;
28
29     function __construct($user=null)
30     {
31         $this->scoped = $user->getProfile();
32         $this->user = $user;
33     }
34
35     /**
36      * Execute the command and send success or error results
37      * back via the given communications channel.
38      *
39      * @param Channel
40      */
41     public function execute($channel)
42     {
43         try {
44             $this->handle($channel);
45         } catch (CommandException $e) {
46             $channel->error($this->user, $e->getMessage());
47         } catch (Exception $e) {
48             common_log(LOG_ERR, "Error handling " . get_class($this) . ": " . $e->getMessage());
49             $channel->error($this->user, $e->getMessage());
50         }
51     }
52
53     /**
54      * Override this with the meat!
55      *
56      * An error to send back to the user may be sent by throwing
57      * a CommandException with a formatted message.
58      *
59      * @param Channel
60      * @throws CommandException
61      */
62     function handle($channel)
63     {
64         return false;
65     }
66
67     /**
68      * Look up a notice from an argument, by poster's name to get last post
69      * or notice_id prefixed with #.
70      *
71      * @return Notice
72      * @throws CommandException
73      */
74     function getNotice($arg)
75     {
76         $notice = null;
77         if (Event::handle('StartCommandGetNotice', array($this, $arg, &$notice))) {
78             if(substr($this->other,0,1)=='#'){
79                 // A specific notice_id #123
80
81                 $notice = Notice::getKV(substr($arg,1));
82                 if (!$notice) {
83                     // TRANS: Command exception text shown when a notice ID is requested that does not exist.
84                     throw new CommandException(_('Notice with that id does not exist.'));
85                 }
86             }
87
88             if (Validate::uri($this->other)) {
89                 // A specific notice by URI lookup
90                 $notice = Notice::getKV('uri', $arg);
91             }
92
93             if (!$notice) {
94                 // Local or remote profile name to get their last notice.
95                 // May throw an exception and report 'no such user'
96                 $recipient = $this->getProfile($arg);
97
98                 $notice = $recipient->getCurrentNotice();
99                 if (!$notice) {
100                     // TRANS: Command exception text shown when a last user notice is requested and it does not exist.
101                     throw new CommandException(_('User has no last notice.'));
102                 }
103             }
104         }
105         Event::handle('EndCommandGetNotice', array($this, $arg, &$notice));
106         if (!$notice) {
107             // TRANS: Command exception text shown when a notice ID is requested that does not exist.
108             throw new CommandException(_('Notice with that id does not exist.'));
109         }
110         return $notice;
111     }
112
113     /**
114      * Look up a local or remote profile by nickname.
115      *
116      * @return Profile
117      * @throws CommandException
118      */
119     function getProfile($arg)
120     {
121         $profile = null;
122         if (Event::handle('StartCommandGetProfile', array($this, $arg, &$profile))) {
123             $profile =
124               common_relative_profile($this->user, common_canonical_nickname($arg));
125         }
126         Event::handle('EndCommandGetProfile', array($this, $arg, &$profile));
127         if (!$profile) {
128             // TRANS: Message given requesting a profile for a non-existing user.
129             // TRANS: %s is the nickname of the user for which the profile could not be found.
130             throw new CommandException(sprintf(_('Could not find a user with nickname %s.'), $arg));
131         }
132         return $profile;
133     }
134
135     /**
136      * Get a local user by name
137      * @return User
138      * @throws CommandException
139      */
140     function getUser($arg)
141     {
142         $user = null;
143         if (Event::handle('StartCommandGetUser', array($this, $arg, &$user))) {
144             $user = User::getKV('nickname', Nickname::normalize($arg));
145         }
146         Event::handle('EndCommandGetUser', array($this, $arg, &$user));
147         if (!$user){
148             // TRANS: Message given getting a non-existing user.
149             // TRANS: %s is the nickname of the user that could not be found.
150             throw new CommandException(sprintf(_('Could not find a local user with nickname %s.'),
151                                $arg));
152         }
153         return $user;
154     }
155
156     /**
157      * Get a local or remote group by name.
158      * @return User_group
159      * @throws CommandException
160      */
161     function getGroup($arg)
162     {
163         $group = null;
164         if (Event::handle('StartCommandGetGroup', array($this, $arg, &$group))) {
165             $group = User_group::getForNickname($arg, $this->user->getProfile());
166         }
167         Event::handle('EndCommandGetGroup', array($this, $arg, &$group));
168         if (!$group) {
169             // TRANS: Command exception text shown when a group is requested that does not exist.
170             throw new CommandException(_('No such group.'));
171         }
172         return $group;
173     }
174 }
175
176 class CommandException extends Exception
177 {
178 }
179
180 class UnimplementedCommand extends Command
181 {
182     function handle($channel)
183     {
184         // TRANS: Error text shown when an unimplemented command is given.
185         $channel->error($this->user, _('Sorry, this command is not yet implemented.'));
186     }
187 }
188
189 class TrackingCommand extends UnimplementedCommand
190 {
191 }
192
193 class TrackOffCommand extends UnimplementedCommand
194 {
195 }
196
197 class TrackCommand extends UnimplementedCommand
198 {
199     var $word = null;
200     function __construct($user, $word)
201     {
202         parent::__construct($user);
203         $this->word = $word;
204     }
205 }
206
207 class UntrackCommand extends UnimplementedCommand
208 {
209     var $word = null;
210     function __construct($user, $word)
211     {
212         parent::__construct($user);
213         $this->word = $word;
214     }
215 }
216
217 class NudgeCommand extends Command
218 {
219     var $other = null;
220     function __construct($user, $other)
221     {
222         parent::__construct($user);
223         $this->other = $other;
224     }
225
226     function handle($channel)
227     {
228         $recipient = $this->getUser($this->other);
229         if ($recipient->id == $this->user->id) {
230             // TRANS: Command exception text shown when a user tries to nudge themselves.
231             throw new CommandException(_('It does not make a lot of sense to nudge yourself!'));
232         } else {
233             if ($recipient->email && $recipient->emailnotifynudge) {
234                 mail_notify_nudge($this->user, $recipient);
235             }
236             // XXX: notify by IM
237             // XXX: notify by SMS
238             // TRANS: Message given having nudged another user.
239             // TRANS: %s is the nickname of the user that was nudged.
240             $channel->output($this->user, sprintf(_('Nudge sent to %s.'),
241                            $recipient->nickname));
242         }
243     }
244 }
245
246 class InviteCommand extends UnimplementedCommand
247 {
248     var $other = null;
249     function __construct($user, $other)
250     {
251         parent::__construct($user);
252         $this->other = $other;
253     }
254 }
255
256 class StatsCommand extends Command
257 {
258     function handle($channel)
259     {
260         $profile = $this->user->getProfile();
261
262         $subs_count   = $profile->subscriptionCount();
263         $subbed_count = $profile->subscriberCount();
264         $notice_count = $profile->noticeCount();
265
266         // TRANS: User statistics text.
267         // TRANS: %1$s is the number of other user the user is subscribed to.
268         // TRANS: %2$s is the number of users that are subscribed to the user.
269         // TRANS: %3$s is the number of notices the user has sent.
270         $channel->output($this->user, sprintf(_("Subscriptions: %1\$s\n".
271                                    "Subscribers: %2\$s\n".
272                                    "Notices: %3\$s"),
273                                  $subs_count,
274                                  $subbed_count,
275                                  $notice_count));
276     }
277 }
278
279 class JoinCommand extends Command
280 {
281     var $other = null;
282
283     function __construct($user, $other)
284     {
285         parent::__construct($user);
286         $this->other = $other;
287     }
288
289     function handle($channel)
290     {
291         $group = $this->getGroup($this->other);
292         $cur   = $this->user;
293
294         if ($cur->isMember($group)) {
295             // TRANS: Error text shown a user tries to join a group they already are a member of.
296             $channel->error($cur, _('You are already a member of that group.'));
297             return;
298         }
299         if (Group_block::isBlocked($group, $cur->getProfile())) {
300             // TRANS: Error text shown when a user tries to join a group they are blocked from joining.
301           $channel->error($cur, _('You have been blocked from that group by the admin.'));
302             return;
303         }
304
305         try {
306             $cur->joinGroup($group);
307         } catch (Exception $e) {
308             // TRANS: Message given having failed to add a user to a group.
309             // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
310             $channel->error($cur, sprintf(_('Could not join user %1$s to group %2$s.'),
311                                           $cur->nickname, $group->nickname));
312             return;
313         }
314
315         // TRANS: Message given having added a user to a group.
316         // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
317         $channel->output($cur, sprintf(_('%1$s joined group %2$s.'),
318                                               $cur->nickname,
319                                               $group->nickname));
320     }
321 }
322
323 class DropCommand extends Command
324 {
325     var $other = null;
326
327     function __construct($user, $other)
328     {
329         parent::__construct($user);
330         $this->other = $other;
331     }
332
333     function handle($channel)
334     {
335         $group = $this->getGroup($this->other);
336         $cur   = $this->user;
337
338         if (!$group) {
339             // TRANS: Error text shown when trying to leave a group that does not exist.
340             $channel->error($cur, _('No such group.'));
341             return;
342         }
343
344         if (!$cur->isMember($group)) {
345             // TRANS: Error text shown when trying to leave an existing group the user is not a member of.
346             $channel->error($cur, _('You are not a member of that group.'));
347             return;
348         }
349
350         try {
351             $cur->leaveGroup($group);
352         } catch (Exception $e) {
353             // TRANS: Message given having failed to remove a user from a group.
354             // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
355             $channel->error($cur, sprintf(_('Could not remove user %1$s from group %2$s.'),
356                                           $cur->nickname, $group->nickname));
357             return;
358         }
359
360         // TRANS: Message given having removed a user from a group.
361         // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
362         $channel->output($cur, sprintf(_('%1$s left group %2$s.'),
363                                               $cur->nickname,
364                                               $group->nickname));
365     }
366 }
367
368 class TagCommand extends Command
369 {
370     var $other = null;
371     var $tags = null;
372     function __construct($user, $other, $tags)
373     {
374         parent::__construct($user);
375         $this->other = $other;
376         $this->tags = $tags;
377     }
378
379     function handle($channel)
380     {
381         $profile = $this->getProfile($this->other);
382         $cur     = $this->user->getProfile();
383
384         if (!$profile) {
385             // TRANS: Client error displayed trying to perform an action related to a non-existing profile.
386             $channel->error($cur, _('No such profile.'));
387             return;
388         }
389         if (!$cur->canTag($profile)) {
390             // TRANS: Error displayed when trying to tag a user that cannot be tagged.
391             $channel->error($cur, _('You cannot tag this user.'));
392             return;
393         }
394
395         $privs = array();
396         $tags = preg_split('/[\s,]+/', $this->tags);
397         $clean_tags = array();
398
399         foreach ($tags as $tag) {
400             $private = @$tag[0] === '.';
401             $tag = $clean_tags[] = common_canonical_tag($tag);
402
403             if (!common_valid_profile_tag($tag)) {
404                 // TRANS: Error displayed if a given tag is invalid.
405                 // TRANS: %s is the invalid tag.
406                 $channel->error($cur, sprintf(_('Invalid tag: "%s".'), $tag));
407                 return;
408             }
409             $privs[$tag] = $private;
410         }
411
412         try {
413             foreach ($clean_tags as $tag) {
414                 Profile_tag::setTag($cur->id, $profile->id, $tag, null, $privs[$tag]);
415             }
416         } catch (Exception $e) {
417             // TRANS: Error displayed if tagging a user fails.
418             // TRANS: %1$s is the tagged user, %2$s is the error message (no punctuation).
419             $channel->error($cur, sprintf(_('Error tagging %1$s: %2$s'),
420                                           $profile->nickname, $e->getMessage()));
421             return;
422         }
423
424         // TRANS: Succes message displayed if tagging a user succeeds.
425         // TRANS: %1$s is the tagged user's nickname, %2$s is a list of tags.
426         // TRANS: Plural is decided based on the number of tags added (not part of message).
427         $channel->output($cur, sprintf(_m('%1$s was tagged %2$s',
428                                           '%1$s was tagged %2$s',
429                                           count($clean_tags)),
430                                        $profile->nickname,
431                                        // TRANS: Separator for list of tags.
432                                        implode(_(', '), $clean_tags)));
433     }
434 }
435
436 class UntagCommand extends TagCommand
437 {
438     function handle($channel)
439     {
440         $profile = $this->getProfile($this->other);
441         $cur     = $this->user->getProfile();
442
443         if (!$profile) {
444             // TRANS: Client error displayed trying to perform an action related to a non-existing profile.
445             $channel->error($cur, _('No such profile.'));
446             return;
447         }
448         if (!$cur->canTag($profile)) {
449             // TRANS: Error displayed when trying to tag a user that cannot be tagged.
450             $channel->error($cur, _('You cannot tag this user.'));
451             return;
452         }
453
454         $tags = array_map('common_canonical_tag', preg_split('/[\s,]+/', $this->tags));
455
456         foreach ($tags as $tag) {
457             if (!common_valid_profile_tag($tag)) {
458                 // TRANS: Error displayed if a given tag is invalid.
459                 // TRANS: %s is the invalid tag.
460                 $channel->error($cur, sprintf(_('Invalid tag: "%s"'), $tag));
461                 return;
462             }
463         }
464
465         try {
466             foreach ($tags as $tag) {
467                 Profile_tag::unTag($cur->id, $profile->id, $tag);
468             }
469         } catch (Exception $e) {
470             // TRANS: Error displayed if untagging a user fails.
471             // TRANS: %1$s is the untagged user, %2$s is the error message (no punctuation).
472             $channel->error($cur, sprintf(_('Error untagging %1$s: %2$s'),
473                                           $profile->nickname, $e->getMessage()));
474             return;
475         }
476
477         // TRANS: Succes message displayed if untagging a user succeeds.
478         // TRANS: %1$s is the untagged user's nickname, %2$s is a list of tags.
479         // TRANS: Plural is decided based on the number of tags removed (not part of message).
480         $channel->output($cur, sprintf(_m('The following tag was removed from user %1$s: %2$s.',
481                                          'The following tags were removed from user %1$s: %2$s.',
482                                          count($tags)),
483                                        $profile->nickname,
484                                        // TRANS: Separator for list of tags.
485                                        implode(_(', '), $tags)));
486     }
487 }
488
489 class WhoisCommand extends Command
490 {
491     var $other = null;
492     function __construct($user, $other)
493     {
494         parent::__construct($user);
495         $this->other = $other;
496     }
497
498     function handle($channel)
499     {
500         $recipient = $this->getProfile($this->other);
501
502         // TRANS: Whois output.
503         // TRANS: %1$s nickname of the queried user, %2$s is their profile URL.
504         $whois = sprintf(_m('WHOIS',"%1\$s (%2\$s)"), $recipient->nickname,
505                          $recipient->profileurl);
506         if ($recipient->fullname) {
507             // TRANS: Whois output. %s is the full name of the queried user.
508             $whois .= "\n" . sprintf(_('Fullname: %s'), $recipient->fullname);
509         }
510         if ($recipient->location) {
511             // TRANS: Whois output. %s is the location of the queried user.
512             $whois .= "\n" . sprintf(_('Location: %s'), $recipient->location);
513         }
514         if ($recipient->homepage) {
515             // TRANS: Whois output. %s is the homepage of the queried user.
516             $whois .= "\n" . sprintf(_('Homepage: %s'), $recipient->homepage);
517         }
518         if ($recipient->bio) {
519             // TRANS: Whois output. %s is the bio information of the queried user.
520             $whois .= "\n" . sprintf(_('About: %s'), $recipient->bio);
521         }
522         $channel->output($this->user, $whois);
523     }
524 }
525
526 class RepeatCommand extends Command
527 {
528     var $other = null;
529     function __construct($user, $other)
530     {
531         parent::__construct($user);
532         $this->other = $other;
533     }
534
535     function handle($channel)
536     {
537         $notice = $this->getNotice($this->other);
538
539         try {
540             $repeat = $notice->repeat($this->scoped->id, $channel->source());
541             $recipient = $notice->getProfile();
542
543             // TRANS: Message given having repeated a notice from another user.
544             // TRANS: %s is the name of the user for which the notice was repeated.
545             $channel->output($this->user, sprintf(_('Notice from %s repeated.'), $recipient->nickname));
546         } catch (Exception $e) {
547             $channel->error($this->user, $e->getMessage());
548         }
549     }
550 }
551
552 class ReplyCommand extends Command
553 {
554     var $other = null;
555     var $text = null;
556     function __construct($user, $other, $text)
557     {
558         parent::__construct($user);
559         $this->other = $other;
560         $this->text = $text;
561     }
562
563     function handle($channel)
564     {
565         $notice = $this->getNotice($this->other);
566         $recipient = $notice->getProfile();
567
568         $len = mb_strlen($this->text);
569
570         if ($len == 0) {
571             // TRANS: Command exception text shown when trying to reply to a notice without providing content for the reply.
572             $channel->error($this->user, _('No content!'));
573             return;
574         }
575
576         $this->text = $this->user->shortenLinks($this->text);
577
578         if (Notice::contentTooLong($this->text)) {
579             // XXX: i18n. Needs plural support.
580             // TRANS: Message given if content of a notice for a reply is too long. %1$d is used for plural.
581             // TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
582             $channel->error($this->user, sprintf(_m('Notice too long - maximum is %1$d character, you sent %2$d.',
583                                                     'Notice too long - maximum is %1$d characters, you sent %2$d.',
584                                                     Notice::maxContent()),
585                                                  Notice::maxContent(), mb_strlen($this->text)));
586             return;
587         }
588
589         $notice = Notice::saveNew($this->user->id, $this->text, $channel->source(),
590                                   array('reply_to' => $notice->id));
591
592         if ($notice) {
593             // TRANS: Text shown having sent a reply to a notice successfully.
594             // TRANS: %s is the nickname of the user of the notice the reply was sent to.
595             $channel->output($this->user, sprintf(_('Reply to %s sent.'), $recipient->nickname));
596         } else {
597             // TRANS: Error text shown when a reply to a notice fails with an unknown reason.
598             $channel->error($this->user, _('Error saving notice.'));
599         }
600
601     }
602 }
603
604 class GetCommand extends Command
605 {
606     var $other = null;
607
608     function __construct($user, $other)
609     {
610         parent::__construct($user);
611         $this->other = $other;
612     }
613
614     function handle($channel)
615     {
616         $target = $this->getProfile($this->other);
617
618         $notice = $target->getCurrentNotice();
619         if (!$notice) {
620             // TRANS: Error text shown when a last user notice is requested and it does not exist.
621             $channel->error($this->user, _('User has no last notice.'));
622             return;
623         }
624         $notice_content = $notice->content;
625
626         $channel->output($this->user, $target->nickname . ": " . $notice_content);
627     }
628 }
629
630 class SubCommand extends Command
631 {
632     var $other = null;
633
634     function __construct($user, $other)
635     {
636         parent::__construct($user);
637         $this->other = $other;
638     }
639
640     function handle($channel)
641     {
642
643         if (!$this->other) {
644             // TRANS: Error text shown when no username was provided when issuing a subscribe command.
645             $channel->error($this->user, _('Specify the name of the user to subscribe to.'));
646             return;
647         }
648
649         $target = $this->getProfile($this->other);
650
651         try {
652             Subscription::start($this->user->getProfile(), $target);
653             // TRANS: Text shown after having subscribed to another user successfully.
654             // TRANS: %s is the name of the user the subscription was requested for.
655             $channel->output($this->user, sprintf(_('Subscribed to %s.'), $this->other));
656         } catch (Exception $e) {
657             $channel->error($this->user, $e->getMessage());
658         }
659     }
660 }
661
662 class UnsubCommand extends Command
663 {
664     var $other = null;
665
666     function __construct($user, $other)
667     {
668         parent::__construct($user);
669         $this->other = $other;
670     }
671
672     function handle($channel)
673     {
674         if(!$this->other) {
675             // TRANS: Error text shown when no username was provided when issuing an unsubscribe command.
676             $channel->error($this->user, _('Specify the name of the user to unsubscribe from.'));
677             return;
678         }
679
680         $target = $this->getProfile($this->other);
681
682         try {
683             Subscription::cancel($this->user->getProfile(), $target);
684             // TRANS: Text shown after having unsubscribed from another user successfully.
685             // TRANS: %s is the name of the user the unsubscription was requested for.
686             $channel->output($this->user, sprintf(_('Unsubscribed from %s.'), $this->other));
687         } catch (Exception $e) {
688             $channel->error($this->user, $e->getMessage());
689         }
690     }
691 }
692
693 class OffCommand extends Command
694 {
695     var $other = null;
696
697     function __construct($user, $other=null)
698     {
699         parent::__construct($user);
700         $this->other = $other;
701     }
702     function handle($channel)
703     {
704         if ($this->other) {
705             // TRANS: Error text shown when issuing the command "off" with a setting which has not yet been implemented.
706             $channel->error($this->user, _("Command not yet implemented."));
707         } else {
708             if ($channel->off($this->user)) {
709                 // TRANS: Text shown when issuing the command "off" successfully.
710                 $channel->output($this->user, _('Notification off.'));
711             } else {
712                 // TRANS: Error text shown when the command "off" fails for an unknown reason.
713                 $channel->error($this->user, _('Can\'t turn off notification.'));
714             }
715         }
716     }
717 }
718
719 class OnCommand extends Command
720 {
721     var $other = null;
722     function __construct($user, $other=null)
723     {
724         parent::__construct($user);
725         $this->other = $other;
726     }
727
728     function handle($channel)
729     {
730         if ($this->other) {
731             // TRANS: Error text shown when issuing the command "on" with a setting which has not yet been implemented.
732             $channel->error($this->user, _("Command not yet implemented."));
733         } else {
734             if ($channel->on($this->user)) {
735                 // TRANS: Text shown when issuing the command "on" successfully.
736                 $channel->output($this->user, _('Notification on.'));
737             } else {
738                 // TRANS: Error text shown when the command "on" fails for an unknown reason.
739                 $channel->error($this->user, _('Can\'t turn on notification.'));
740             }
741         }
742     }
743 }
744
745 class LoginCommand extends Command
746 {
747     function handle($channel)
748     {
749         $disabled = common_config('logincommand','disabled');
750         $disabled = isset($disabled) && $disabled;
751         if($disabled) {
752             // TRANS: Error text shown when issuing the login command while login is disabled.
753             $channel->error($this->user, _('Login command is disabled.'));
754             return;
755         }
756
757         try {
758             $login_token = Login_token::makeNew($this->user);
759         } catch (Exception $e) {
760             $channel->error($this->user, $e->getMessage());
761         }
762
763         $channel->output($this->user,
764             // TRANS: Text shown after issuing the login command successfully.
765             // TRANS: %s is a logon link..
766             sprintf(_('This link is useable only once and is valid for only 2 minutes: %s.'),
767                     common_local_url('otp',
768                         array('user_id' => $login_token->user_id, 'token' => $login_token->token))));
769     }
770 }
771
772 class LoseCommand extends Command
773 {
774     var $other = null;
775
776     function __construct($user, $other)
777     {
778         parent::__construct($user);
779         $this->other = $other;
780     }
781
782     function execute($channel)
783     {
784         if(!$this->other) {
785             // TRANS: Error text shown when no username was provided when issuing the command.
786             $channel->error($this->user, _('Specify the name of the user to unsubscribe from.'));
787             return;
788         }
789
790         $result = Subscription::cancel($this->getProfile($this->other), $this->user->getProfile());
791
792         if ($result) {
793             // TRANS: Text shown after issuing the lose command successfully (stop another user from following the current user).
794             // TRANS: %s is the name of the user the unsubscription was requested for.
795             $channel->output($this->user, sprintf(_('Unsubscribed %s.'), $this->other));
796         } else {
797             $channel->error($this->user, $result);
798         }
799     }
800 }
801
802 class SubscriptionsCommand extends Command
803 {
804     function handle($channel)
805     {
806         $profile = $this->user->getSubscribed(0);
807         $nicknames=array();
808         while ($profile->fetch()) {
809             $nicknames[]=$profile->nickname;
810         }
811         if(count($nicknames)==0){
812             // TRANS: Text shown after requesting other users a user is subscribed to without having any subscriptions.
813             $out=_('You are not subscribed to anyone.');
814         }else{
815             // TRANS: Text shown after requesting other users a user is subscribed to.
816             // TRANS: This message supports plural forms. This message is followed by a
817             // TRANS: hard coded space and a comma separated list of subscribed users.
818             $out = _m('You are subscribed to this person:',
819                 'You are subscribed to these people:',
820                 count($nicknames));
821             $out .= ' ';
822             $out .= implode(', ',$nicknames);
823         }
824         $channel->output($this->user,$out);
825     }
826 }
827
828 class SubscribersCommand extends Command
829 {
830     function handle($channel)
831     {
832         $profile = $this->user->getSubscribers();
833         $nicknames=array();
834         while ($profile->fetch()) {
835             $nicknames[]=$profile->nickname;
836         }
837         if(count($nicknames)==0){
838             // TRANS: Text shown after requesting other users that are subscribed to a user
839             // TRANS: (followers) without having any subscribers.
840             $out=_('No one is subscribed to you.');
841         }else{
842             // TRANS: Text shown after requesting other users that are subscribed to a user (followers).
843             // TRANS: This message supports plural forms. This message is followed by a
844             // TRANS: hard coded space and a comma separated list of subscribing users.
845             $out = _m('This person is subscribed to you:',
846                 'These people are subscribed to you:',
847                 count($nicknames));
848             $out .= ' ';
849             $out .= implode(', ',$nicknames);
850         }
851         $channel->output($this->user,$out);
852     }
853 }
854
855 class GroupsCommand extends Command
856 {
857     function handle($channel)
858     {
859         $group = $this->user->getGroups();
860         $groups=array();
861         while ($group instanceof User_group && $group->fetch()) {
862             $groups[]=$group->nickname;
863         }
864         if(count($groups)==0){
865             // TRANS: Text shown after requesting groups a user is subscribed to without having
866             // TRANS: any group subscriptions.
867             $out=_('You are not a member of any groups.');
868         }else{
869             // TRANS: Text shown after requesting groups a user is subscribed to.
870             // TRANS: This message supports plural forms. This message is followed by a
871             // TRANS: hard coded space and a comma separated list of subscribed groups.
872             $out = _m('You are a member of this group:',
873                 'You are a member of these groups:',
874                 count($nicknames));
875             $out.=implode(', ',$groups);
876         }
877         $channel->output($this->user,$out);
878     }
879 }
880
881 class HelpCommand extends Command
882 {
883     function handle($channel)
884     {
885         // TRANS: Header line of help text for commands.
886         $out = array(_m('COMMANDHELP', "Commands:"));
887         $commands = array(// TRANS: Help message for IM/SMS command "on".
888                           "on" => _m('COMMANDHELP', "turn on notifications"),
889                           // TRANS: Help message for IM/SMS command "off".
890                           "off" => _m('COMMANDHELP', "turn off notifications"),
891                           // TRANS: Help message for IM/SMS command "help".
892                           "help" => _m('COMMANDHELP', "show this help"),
893                           // TRANS: Help message for IM/SMS command "follow <nickname>".
894                           "follow <nickname>" => _m('COMMANDHELP', "subscribe to user"),
895                           // TRANS: Help message for IM/SMS command "groups".
896                           "groups" => _m('COMMANDHELP', "lists the groups you have joined"),
897                           // TRANS: Help message for IM/SMS command "tag".
898                           "tag <nickname> <tags>" => _m('COMMANDHELP',"tag a user"),
899                           // TRANS: Help message for IM/SMS command "untag".
900                           "untag <nickname> <tags>" => _m('COMMANDHELP',"untag a user"),
901                           // TRANS: Help message for IM/SMS command "subscriptions".
902                           "subscriptions" => _m('COMMANDHELP', "list the people you follow"),
903                           // TRANS: Help message for IM/SMS command "subscribers".
904                           "subscribers" => _m('COMMANDHELP', "list the people that follow you"),
905                           // TRANS: Help message for IM/SMS command "leave <nickname>".
906                           "leave <nickname>" => _m('COMMANDHELP', "unsubscribe from user"),
907                           // TRANS: Help message for IM/SMS command "d <nickname> <text>".
908                           "d <nickname> <text>" => _m('COMMANDHELP', "direct message to user"),
909                           // TRANS: Help message for IM/SMS command "get <nickname>".
910                           "get <nickname>" => _m('COMMANDHELP', "get last notice from user"),
911                           // TRANS: Help message for IM/SMS command "whois <nickname>".
912                           "whois <nickname>" => _m('COMMANDHELP', "get profile info on user"),
913                           // TRANS: Help message for IM/SMS command "lose <nickname>".
914                           "lose <nickname>" => _m('COMMANDHELP', "force user to stop following you"),
915                           // TRANS: Help message for IM/SMS command "repeat #<notice_id>".
916                           "repeat #<notice_id>" => _m('COMMANDHELP', "repeat a notice with a given id"),
917                           // TRANS: Help message for IM/SMS command "repeat <nickname>".
918                           "repeat <nickname>" => _m('COMMANDHELP', "repeat the last notice from user"),
919                           // TRANS: Help message for IM/SMS command "reply #<notice_id>".
920                           "reply #<notice_id>" => _m('COMMANDHELP', "reply to notice with a given id"),
921                           // TRANS: Help message for IM/SMS command "reply <nickname>".
922                           "reply <nickname>" => _m('COMMANDHELP', "reply to the last notice from user"),
923                           // TRANS: Help message for IM/SMS command "join <group>".
924                           "join <group>" => _m('COMMANDHELP', "join group"),
925                           // TRANS: Help message for IM/SMS command "login".
926                           "login" => _m('COMMANDHELP', "Get a link to login to the web interface"),
927                           // TRANS: Help message for IM/SMS command "drop <group>".
928                           "drop <group>" => _m('COMMANDHELP', "leave group"),
929                           // TRANS: Help message for IM/SMS command "stats".
930                           "stats" => _m('COMMANDHELP', "get your stats"),
931                           // TRANS: Help message for IM/SMS command "stop".
932                           "stop" => _m('COMMANDHELP', "same as 'off'"),
933                           // TRANS: Help message for IM/SMS command "quit".
934                           "quit" => _m('COMMANDHELP', "same as 'off'"),
935                           // TRANS: Help message for IM/SMS command "sub <nickname>".
936                           "sub <nickname>" => _m('COMMANDHELP', "same as 'follow'"),
937                           // TRANS: Help message for IM/SMS command "unsub <nickname>".
938                           "unsub <nickname>" => _m('COMMANDHELP', "same as 'leave'"),
939                           // TRANS: Help message for IM/SMS command "last <nickname>".
940                           "last <nickname>" => _m('COMMANDHELP', "same as 'get'"),
941                           // TRANS: Help message for IM/SMS command "on <nickname>".
942                           "on <nickname>" => _m('COMMANDHELP', "not yet implemented."),
943                           // TRANS: Help message for IM/SMS command "off <nickname>".
944                           "off <nickname>" => _m('COMMANDHELP', "not yet implemented."),
945                           // TRANS: Help message for IM/SMS command "nudge <nickname>".
946                           "nudge <nickname>" => _m('COMMANDHELP', "remind a user to update."),
947                           // TRANS: Help message for IM/SMS command "invite <phone number>".
948                           "invite <phone number>" => _m('COMMANDHELP', "not yet implemented."),
949                           // TRANS: Help message for IM/SMS command "track <word>".
950                           "track <word>" => _m('COMMANDHELP', "not yet implemented."),
951                           // TRANS: Help message for IM/SMS command "untrack <word>".
952                           "untrack <word>" => _m('COMMANDHELP', "not yet implemented."),
953                           // TRANS: Help message for IM/SMS command "track off".
954                           "track off" => _m('COMMANDHELP', "not yet implemented."),
955                           // TRANS: Help message for IM/SMS command "untrack all".
956                           "untrack all" => _m('COMMANDHELP', "not yet implemented."),
957                           // TRANS: Help message for IM/SMS command "tracks".
958                           "tracks" => _m('COMMANDHELP', "not yet implemented."),
959                           // TRANS: Help message for IM/SMS command "tracking".
960                           "tracking" => _m('COMMANDHELP', "not yet implemented."));
961
962         // Give plugins a chance to add or override...
963         Event::handle('HelpCommandMessages', array($this, &$commands));
964
965         sort($commands);
966         foreach ($commands as $command => $help) {
967             $out[] = "$command - $help";
968         }
969         $channel->output($this->user, implode("\n", $out));
970     }
971 }