]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/command.php
Notice->repeat() function takes Profile as argument now
[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 FavCommand 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         $notice = $this->getNotice($this->other);
292
293         $fave            = new Fave();
294         $fave->user_id   = $this->user->id;
295         $fave->notice_id = $notice->id;
296         $fave->find();
297
298         if ($fave->fetch()) {
299             // TRANS: Error message text shown when a favorite could not be set because it has already been favorited.
300             $channel->error($this->user, _('Could not create favorite: Already favorited.'));
301             return;
302         }
303
304         $fave = Fave::addNew($this->user->getProfile(), $notice);
305
306         if (!$fave) {
307             // TRANS: Error message text shown when a favorite could not be set.
308             $channel->error($this->user, _('Could not create favorite.'));
309             return;
310         }
311
312         // @fixme favorite notification should be triggered
313         // at a lower level
314
315         $other = User::getKV('id', $notice->profile_id);
316
317         if ($other && $other->id != $this->user->id) {
318             if ($other->email && $other->emailnotifyfav) {
319                 mail_notify_fave($other, $this->user, $notice);
320             }
321         }
322
323         $this->user->blowFavesCache();
324
325         // TRANS: Text shown when a notice has been marked as favourite successfully.
326         $channel->output($this->user, _('Notice marked as fave.'));
327     }
328 }
329
330 class JoinCommand extends Command
331 {
332     var $other = null;
333
334     function __construct($user, $other)
335     {
336         parent::__construct($user);
337         $this->other = $other;
338     }
339
340     function handle($channel)
341     {
342         $group = $this->getGroup($this->other);
343         $cur   = $this->user;
344
345         if ($cur->isMember($group)) {
346             // TRANS: Error text shown a user tries to join a group they already are a member of.
347             $channel->error($cur, _('You are already a member of that group.'));
348             return;
349         }
350         if (Group_block::isBlocked($group, $cur->getProfile())) {
351             // TRANS: Error text shown when a user tries to join a group they are blocked from joining.
352           $channel->error($cur, _('You have been blocked from that group by the admin.'));
353             return;
354         }
355
356         try {
357             $cur->joinGroup($group);
358         } catch (Exception $e) {
359             // TRANS: Message given having failed to add a user to a group.
360             // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
361             $channel->error($cur, sprintf(_('Could not join user %1$s to group %2$s.'),
362                                           $cur->nickname, $group->nickname));
363             return;
364         }
365
366         // TRANS: Message given having added a user to a group.
367         // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
368         $channel->output($cur, sprintf(_('%1$s joined group %2$s.'),
369                                               $cur->nickname,
370                                               $group->nickname));
371     }
372 }
373
374 class DropCommand extends Command
375 {
376     var $other = null;
377
378     function __construct($user, $other)
379     {
380         parent::__construct($user);
381         $this->other = $other;
382     }
383
384     function handle($channel)
385     {
386         $group = $this->getGroup($this->other);
387         $cur   = $this->user;
388
389         if (!$group) {
390             // TRANS: Error text shown when trying to leave a group that does not exist.
391             $channel->error($cur, _('No such group.'));
392             return;
393         }
394
395         if (!$cur->isMember($group)) {
396             // TRANS: Error text shown when trying to leave an existing group the user is not a member of.
397             $channel->error($cur, _('You are not a member of that group.'));
398             return;
399         }
400
401         try {
402             $cur->leaveGroup($group);
403         } catch (Exception $e) {
404             // TRANS: Message given having failed to remove a user from a group.
405             // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
406             $channel->error($cur, sprintf(_('Could not remove user %1$s from group %2$s.'),
407                                           $cur->nickname, $group->nickname));
408             return;
409         }
410
411         // TRANS: Message given having removed a user from a group.
412         // TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
413         $channel->output($cur, sprintf(_('%1$s left group %2$s.'),
414                                               $cur->nickname,
415                                               $group->nickname));
416     }
417 }
418
419 class TagCommand extends Command
420 {
421     var $other = null;
422     var $tags = null;
423     function __construct($user, $other, $tags)
424     {
425         parent::__construct($user);
426         $this->other = $other;
427         $this->tags = $tags;
428     }
429
430     function handle($channel)
431     {
432         $profile = $this->getProfile($this->other);
433         $cur     = $this->user->getProfile();
434
435         if (!$profile) {
436             // TRANS: Client error displayed trying to perform an action related to a non-existing profile.
437             $channel->error($cur, _('No such profile.'));
438             return;
439         }
440         if (!$cur->canTag($profile)) {
441             // TRANS: Error displayed when trying to tag a user that cannot be tagged.
442             $channel->error($cur, _('You cannot tag this user.'));
443             return;
444         }
445
446         $privs = array();
447         $tags = preg_split('/[\s,]+/', $this->tags);
448         $clean_tags = array();
449
450         foreach ($tags as $tag) {
451             $private = @$tag[0] === '.';
452             $tag = $clean_tags[] = common_canonical_tag($tag);
453
454             if (!common_valid_profile_tag($tag)) {
455                 // TRANS: Error displayed if a given tag is invalid.
456                 // TRANS: %s is the invalid tag.
457                 $channel->error($cur, sprintf(_('Invalid tag: "%s".'), $tag));
458                 return;
459             }
460             $privs[$tag] = $private;
461         }
462
463         try {
464             foreach ($clean_tags as $tag) {
465                 Profile_tag::setTag($cur->id, $profile->id, $tag, null, $privs[$tag]);
466             }
467         } catch (Exception $e) {
468             // TRANS: Error displayed if tagging a user fails.
469             // TRANS: %1$s is the tagged user, %2$s is the error message (no punctuation).
470             $channel->error($cur, sprintf(_('Error tagging %1$s: %2$s'),
471                                           $profile->nickname, $e->getMessage()));
472             return;
473         }
474
475         // TRANS: Succes message displayed if tagging a user succeeds.
476         // TRANS: %1$s is the tagged user's nickname, %2$s is a list of tags.
477         // TRANS: Plural is decided based on the number of tags added (not part of message).
478         $channel->output($cur, sprintf(_m('%1$s was tagged %2$s',
479                                           '%1$s was tagged %2$s',
480                                           count($clean_tags)),
481                                        $profile->nickname,
482                                        // TRANS: Separator for list of tags.
483                                        implode(_(', '), $clean_tags)));
484     }
485 }
486
487 class UntagCommand extends TagCommand
488 {
489     function handle($channel)
490     {
491         $profile = $this->getProfile($this->other);
492         $cur     = $this->user->getProfile();
493
494         if (!$profile) {
495             // TRANS: Client error displayed trying to perform an action related to a non-existing profile.
496             $channel->error($cur, _('No such profile.'));
497             return;
498         }
499         if (!$cur->canTag($profile)) {
500             // TRANS: Error displayed when trying to tag a user that cannot be tagged.
501             $channel->error($cur, _('You cannot tag this user.'));
502             return;
503         }
504
505         $tags = array_map('common_canonical_tag', preg_split('/[\s,]+/', $this->tags));
506
507         foreach ($tags as $tag) {
508             if (!common_valid_profile_tag($tag)) {
509                 // TRANS: Error displayed if a given tag is invalid.
510                 // TRANS: %s is the invalid tag.
511                 $channel->error($cur, sprintf(_('Invalid tag: "%s"'), $tag));
512                 return;
513             }
514         }
515
516         try {
517             foreach ($tags as $tag) {
518                 Profile_tag::unTag($cur->id, $profile->id, $tag);
519             }
520         } catch (Exception $e) {
521             // TRANS: Error displayed if untagging a user fails.
522             // TRANS: %1$s is the untagged user, %2$s is the error message (no punctuation).
523             $channel->error($cur, sprintf(_('Error untagging %1$s: %2$s'),
524                                           $profile->nickname, $e->getMessage()));
525             return;
526         }
527
528         // TRANS: Succes message displayed if untagging a user succeeds.
529         // TRANS: %1$s is the untagged user's nickname, %2$s is a list of tags.
530         // TRANS: Plural is decided based on the number of tags removed (not part of message).
531         $channel->output($cur, sprintf(_m('The following tag was removed from user %1$s: %2$s.',
532                                          'The following tags were removed from user %1$s: %2$s.',
533                                          count($tags)),
534                                        $profile->nickname,
535                                        // TRANS: Separator for list of tags.
536                                        implode(_(', '), $tags)));
537     }
538 }
539
540 class WhoisCommand extends Command
541 {
542     var $other = null;
543     function __construct($user, $other)
544     {
545         parent::__construct($user);
546         $this->other = $other;
547     }
548
549     function handle($channel)
550     {
551         $recipient = $this->getProfile($this->other);
552
553         // TRANS: Whois output.
554         // TRANS: %1$s nickname of the queried user, %2$s is their profile URL.
555         $whois = sprintf(_m('WHOIS',"%1\$s (%2\$s)"), $recipient->nickname,
556                          $recipient->profileurl);
557         if ($recipient->fullname) {
558             // TRANS: Whois output. %s is the full name of the queried user.
559             $whois .= "\n" . sprintf(_('Fullname: %s'), $recipient->fullname);
560         }
561         if ($recipient->location) {
562             // TRANS: Whois output. %s is the location of the queried user.
563             $whois .= "\n" . sprintf(_('Location: %s'), $recipient->location);
564         }
565         if ($recipient->homepage) {
566             // TRANS: Whois output. %s is the homepage of the queried user.
567             $whois .= "\n" . sprintf(_('Homepage: %s'), $recipient->homepage);
568         }
569         if ($recipient->bio) {
570             // TRANS: Whois output. %s is the bio information of the queried user.
571             $whois .= "\n" . sprintf(_('About: %s'), $recipient->bio);
572         }
573         $channel->output($this->user, $whois);
574     }
575 }
576
577 class MessageCommand extends Command
578 {
579     var $other = null;
580     var $text = null;
581     function __construct($user, $other, $text)
582     {
583         parent::__construct($user);
584         $this->other = $other;
585         $this->text = $text;
586     }
587
588     function handle($channel)
589     {
590         try {
591             $other = $this->getUser($this->other)->getProfile();
592         } catch (CommandException $e) {
593             try {
594                 $profile = $this->getProfile($this->other);
595             } catch (CommandException $f) {
596                 throw $e;
597             }
598             // TRANS: Command exception text shown when trying to send a direct message to a remote user (a user not registered at the current server).
599             // TRANS: %s is a remote profile.
600             throw new CommandException(sprintf(_('%s is a remote profile; you can only send direct messages to users on the same server.'), $this->other));
601         }
602
603         $len = mb_strlen($this->text);
604
605         if ($len == 0) {
606             // TRANS: Command exception text shown when trying to send a direct message to another user without content.
607             $channel->error($this->user, _('No content!'));
608             return;
609         }
610
611         $this->text = $this->user->shortenLinks($this->text);
612
613         if (Message::contentTooLong($this->text)) {
614             // XXX: i18n. Needs plural support.
615             // TRANS: Message given if content is too long. %1$sd is used for plural.
616             // TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
617             $channel->error($this->user, sprintf(_m('Message too long - maximum is %1$d character, you sent %2$d.',
618                                                     'Message too long - maximum is %1$d characters, you sent %2$d.',
619                                                     Message::maxContent()),
620                                                  Message::maxContent(), mb_strlen($this->text)));
621             return;
622         }
623
624         if (!$other instanceof Profile) {
625             // TRANS: Error text shown when trying to send a direct message to a user that does not exist.
626             $channel->error($this->user, _('No such user.'));
627             return;
628         } else if (!$this->user->mutuallySubscribed($other)) {
629             // TRANS: Error text shown when trying to send a direct message to a user without a mutual subscription (each user must be subscribed to the other).
630             $channel->error($this->user, _('You can\'t send a message to this user.'));
631             return;
632         } else if ($this->user->id == $other->id) {
633             // TRANS: Error text shown when trying to send a direct message to self.
634             $channel->error($this->user, _('Do not send a message to yourself; just say it to yourself quietly instead.'));
635             return;
636         }
637         try {
638             $message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source());
639             $message->notify();
640             // TRANS: Message given have sent a direct message to another user.
641             // TRANS: %s is the name of the other user.
642             $channel->output($this->user, sprintf(_('Direct message to %s sent.'), $this->other));
643         } catch (Exception $e) {
644             // TRANS: Error text shown sending a direct message fails with an unknown reason.
645             $channel->error($this->user, $e->getMessage());
646         }
647     }
648 }
649
650 class RepeatCommand extends Command
651 {
652     var $other = null;
653     function __construct($user, $other)
654     {
655         parent::__construct($user);
656         $this->other = $other;
657     }
658
659     function handle($channel)
660     {
661         $notice = $this->getNotice($this->other);
662
663         try {
664             $repeat = $notice->repeat($this->scoped->id, $channel->source());
665             $recipient = $notice->getProfile();
666
667             // TRANS: Message given having repeated a notice from another user.
668             // TRANS: %s is the name of the user for which the notice was repeated.
669             $channel->output($this->user, sprintf(_('Notice from %s repeated.'), $recipient->nickname));
670         } catch (Exception $e) {
671             $channel->error($this->user, $e->getMessage());
672         }
673     }
674 }
675
676 class ReplyCommand extends Command
677 {
678     var $other = null;
679     var $text = null;
680     function __construct($user, $other, $text)
681     {
682         parent::__construct($user);
683         $this->other = $other;
684         $this->text = $text;
685     }
686
687     function handle($channel)
688     {
689         $notice = $this->getNotice($this->other);
690         $recipient = $notice->getProfile();
691
692         $len = mb_strlen($this->text);
693
694         if ($len == 0) {
695             // TRANS: Command exception text shown when trying to reply to a notice without providing content for the reply.
696             $channel->error($this->user, _('No content!'));
697             return;
698         }
699
700         $this->text = $this->user->shortenLinks($this->text);
701
702         if (Notice::contentTooLong($this->text)) {
703             // XXX: i18n. Needs plural support.
704             // TRANS: Message given if content of a notice for a reply is too long. %1$d is used for plural.
705             // TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
706             $channel->error($this->user, sprintf(_m('Notice too long - maximum is %1$d character, you sent %2$d.',
707                                                     'Notice too long - maximum is %1$d characters, you sent %2$d.',
708                                                     Notice::maxContent()),
709                                                  Notice::maxContent(), mb_strlen($this->text)));
710             return;
711         }
712
713         $notice = Notice::saveNew($this->user->id, $this->text, $channel->source(),
714                                   array('reply_to' => $notice->id));
715
716         if ($notice) {
717             // TRANS: Text shown having sent a reply to a notice successfully.
718             // TRANS: %s is the nickname of the user of the notice the reply was sent to.
719             $channel->output($this->user, sprintf(_('Reply to %s sent.'), $recipient->nickname));
720         } else {
721             // TRANS: Error text shown when a reply to a notice fails with an unknown reason.
722             $channel->error($this->user, _('Error saving notice.'));
723         }
724
725     }
726 }
727
728 class GetCommand extends Command
729 {
730     var $other = null;
731
732     function __construct($user, $other)
733     {
734         parent::__construct($user);
735         $this->other = $other;
736     }
737
738     function handle($channel)
739     {
740         $target = $this->getProfile($this->other);
741
742         $notice = $target->getCurrentNotice();
743         if (!$notice) {
744             // TRANS: Error text shown when a last user notice is requested and it does not exist.
745             $channel->error($this->user, _('User has no last notice.'));
746             return;
747         }
748         $notice_content = $notice->content;
749
750         $channel->output($this->user, $target->nickname . ": " . $notice_content);
751     }
752 }
753
754 class SubCommand extends Command
755 {
756     var $other = null;
757
758     function __construct($user, $other)
759     {
760         parent::__construct($user);
761         $this->other = $other;
762     }
763
764     function handle($channel)
765     {
766
767         if (!$this->other) {
768             // TRANS: Error text shown when no username was provided when issuing a subscribe command.
769             $channel->error($this->user, _('Specify the name of the user to subscribe to.'));
770             return;
771         }
772
773         $target = $this->getProfile($this->other);
774
775         try {
776             Subscription::start($this->user->getProfile(), $target);
777             // TRANS: Text shown after having subscribed to another user successfully.
778             // TRANS: %s is the name of the user the subscription was requested for.
779             $channel->output($this->user, sprintf(_('Subscribed to %s.'), $this->other));
780         } catch (Exception $e) {
781             $channel->error($this->user, $e->getMessage());
782         }
783     }
784 }
785
786 class UnsubCommand extends Command
787 {
788     var $other = null;
789
790     function __construct($user, $other)
791     {
792         parent::__construct($user);
793         $this->other = $other;
794     }
795
796     function handle($channel)
797     {
798         if(!$this->other) {
799             // TRANS: Error text shown when no username was provided when issuing an unsubscribe command.
800             $channel->error($this->user, _('Specify the name of the user to unsubscribe from.'));
801             return;
802         }
803
804         $target = $this->getProfile($this->other);
805
806         try {
807             Subscription::cancel($this->user->getProfile(), $target);
808             // TRANS: Text shown after having unsubscribed from another user successfully.
809             // TRANS: %s is the name of the user the unsubscription was requested for.
810             $channel->output($this->user, sprintf(_('Unsubscribed from %s.'), $this->other));
811         } catch (Exception $e) {
812             $channel->error($this->user, $e->getMessage());
813         }
814     }
815 }
816
817 class OffCommand extends Command
818 {
819     var $other = null;
820
821     function __construct($user, $other=null)
822     {
823         parent::__construct($user);
824         $this->other = $other;
825     }
826     function handle($channel)
827     {
828         if ($this->other) {
829             // TRANS: Error text shown when issuing the command "off" with a setting which has not yet been implemented.
830             $channel->error($this->user, _("Command not yet implemented."));
831         } else {
832             if ($channel->off($this->user)) {
833                 // TRANS: Text shown when issuing the command "off" successfully.
834                 $channel->output($this->user, _('Notification off.'));
835             } else {
836                 // TRANS: Error text shown when the command "off" fails for an unknown reason.
837                 $channel->error($this->user, _('Can\'t turn off notification.'));
838             }
839         }
840     }
841 }
842
843 class OnCommand extends Command
844 {
845     var $other = null;
846     function __construct($user, $other=null)
847     {
848         parent::__construct($user);
849         $this->other = $other;
850     }
851
852     function handle($channel)
853     {
854         if ($this->other) {
855             // TRANS: Error text shown when issuing the command "on" with a setting which has not yet been implemented.
856             $channel->error($this->user, _("Command not yet implemented."));
857         } else {
858             if ($channel->on($this->user)) {
859                 // TRANS: Text shown when issuing the command "on" successfully.
860                 $channel->output($this->user, _('Notification on.'));
861             } else {
862                 // TRANS: Error text shown when the command "on" fails for an unknown reason.
863                 $channel->error($this->user, _('Can\'t turn on notification.'));
864             }
865         }
866     }
867 }
868
869 class LoginCommand extends Command
870 {
871     function handle($channel)
872     {
873         $disabled = common_config('logincommand','disabled');
874         $disabled = isset($disabled) && $disabled;
875         if($disabled) {
876             // TRANS: Error text shown when issuing the login command while login is disabled.
877             $channel->error($this->user, _('Login command is disabled.'));
878             return;
879         }
880
881         try {
882             $login_token = Login_token::makeNew($this->user);
883         } catch (Exception $e) {
884             $channel->error($this->user, $e->getMessage());
885         }
886
887         $channel->output($this->user,
888             // TRANS: Text shown after issuing the login command successfully.
889             // TRANS: %s is a logon link..
890             sprintf(_('This link is useable only once and is valid for only 2 minutes: %s.'),
891                     common_local_url('otp',
892                         array('user_id' => $login_token->user_id, 'token' => $login_token->token))));
893     }
894 }
895
896 class LoseCommand extends Command
897 {
898     var $other = null;
899
900     function __construct($user, $other)
901     {
902         parent::__construct($user);
903         $this->other = $other;
904     }
905
906     function execute($channel)
907     {
908         if(!$this->other) {
909             // TRANS: Error text shown when no username was provided when issuing the command.
910             $channel->error($this->user, _('Specify the name of the user to unsubscribe from.'));
911             return;
912         }
913
914         $result = Subscription::cancel($this->getProfile($this->other), $this->user->getProfile());
915
916         if ($result) {
917             // TRANS: Text shown after issuing the lose command successfully (stop another user from following the current user).
918             // TRANS: %s is the name of the user the unsubscription was requested for.
919             $channel->output($this->user, sprintf(_('Unsubscribed %s.'), $this->other));
920         } else {
921             $channel->error($this->user, $result);
922         }
923     }
924 }
925
926 class SubscriptionsCommand extends Command
927 {
928     function handle($channel)
929     {
930         $profile = $this->user->getSubscribed(0);
931         $nicknames=array();
932         while ($profile->fetch()) {
933             $nicknames[]=$profile->nickname;
934         }
935         if(count($nicknames)==0){
936             // TRANS: Text shown after requesting other users a user is subscribed to without having any subscriptions.
937             $out=_('You are not subscribed to anyone.');
938         }else{
939             // TRANS: Text shown after requesting other users a user is subscribed to.
940             // TRANS: This message supports plural forms. This message is followed by a
941             // TRANS: hard coded space and a comma separated list of subscribed users.
942             $out = _m('You are subscribed to this person:',
943                 'You are subscribed to these people:',
944                 count($nicknames));
945             $out .= ' ';
946             $out .= implode(', ',$nicknames);
947         }
948         $channel->output($this->user,$out);
949     }
950 }
951
952 class SubscribersCommand extends Command
953 {
954     function handle($channel)
955     {
956         $profile = $this->user->getSubscribers();
957         $nicknames=array();
958         while ($profile->fetch()) {
959             $nicknames[]=$profile->nickname;
960         }
961         if(count($nicknames)==0){
962             // TRANS: Text shown after requesting other users that are subscribed to a user
963             // TRANS: (followers) without having any subscribers.
964             $out=_('No one is subscribed to you.');
965         }else{
966             // TRANS: Text shown after requesting other users that are subscribed to a user (followers).
967             // TRANS: This message supports plural forms. This message is followed by a
968             // TRANS: hard coded space and a comma separated list of subscribing users.
969             $out = _m('This person is subscribed to you:',
970                 'These people are subscribed to you:',
971                 count($nicknames));
972             $out .= ' ';
973             $out .= implode(', ',$nicknames);
974         }
975         $channel->output($this->user,$out);
976     }
977 }
978
979 class GroupsCommand extends Command
980 {
981     function handle($channel)
982     {
983         $group = $this->user->getGroups();
984         $groups=array();
985         while ($group instanceof User_group && $group->fetch()) {
986             $groups[]=$group->nickname;
987         }
988         if(count($groups)==0){
989             // TRANS: Text shown after requesting groups a user is subscribed to without having
990             // TRANS: any group subscriptions.
991             $out=_('You are not a member of any groups.');
992         }else{
993             // TRANS: Text shown after requesting groups a user is subscribed to.
994             // TRANS: This message supports plural forms. This message is followed by a
995             // TRANS: hard coded space and a comma separated list of subscribed groups.
996             $out = _m('You are a member of this group:',
997                 'You are a member of these groups:',
998                 count($nicknames));
999             $out.=implode(', ',$groups);
1000         }
1001         $channel->output($this->user,$out);
1002     }
1003 }
1004
1005 class HelpCommand extends Command
1006 {
1007     function handle($channel)
1008     {
1009         // TRANS: Header line of help text for commands.
1010         $out = array(_m('COMMANDHELP', "Commands:"));
1011         $commands = array(// TRANS: Help message for IM/SMS command "on".
1012                           "on" => _m('COMMANDHELP', "turn on notifications"),
1013                           // TRANS: Help message for IM/SMS command "off".
1014                           "off" => _m('COMMANDHELP', "turn off notifications"),
1015                           // TRANS: Help message for IM/SMS command "help".
1016                           "help" => _m('COMMANDHELP', "show this help"),
1017                           // TRANS: Help message for IM/SMS command "follow <nickname>".
1018                           "follow <nickname>" => _m('COMMANDHELP', "subscribe to user"),
1019                           // TRANS: Help message for IM/SMS command "groups".
1020                           "groups" => _m('COMMANDHELP', "lists the groups you have joined"),
1021                           // TRANS: Help message for IM/SMS command "tag".
1022                           "tag <nickname> <tags>" => _m('COMMANDHELP',"tag a user"),
1023                           // TRANS: Help message for IM/SMS command "untag".
1024                           "untag <nickname> <tags>" => _m('COMMANDHELP',"untag a user"),
1025                           // TRANS: Help message for IM/SMS command "subscriptions".
1026                           "subscriptions" => _m('COMMANDHELP', "list the people you follow"),
1027                           // TRANS: Help message for IM/SMS command "subscribers".
1028                           "subscribers" => _m('COMMANDHELP', "list the people that follow you"),
1029                           // TRANS: Help message for IM/SMS command "leave <nickname>".
1030                           "leave <nickname>" => _m('COMMANDHELP', "unsubscribe from user"),
1031                           // TRANS: Help message for IM/SMS command "d <nickname> <text>".
1032                           "d <nickname> <text>" => _m('COMMANDHELP', "direct message to user"),
1033                           // TRANS: Help message for IM/SMS command "get <nickname>".
1034                           "get <nickname>" => _m('COMMANDHELP', "get last notice from user"),
1035                           // TRANS: Help message for IM/SMS command "whois <nickname>".
1036                           "whois <nickname>" => _m('COMMANDHELP', "get profile info on user"),
1037                           // TRANS: Help message for IM/SMS command "lose <nickname>".
1038                           "lose <nickname>" => _m('COMMANDHELP', "force user to stop following you"),
1039                           // TRANS: Help message for IM/SMS command "fav <nickname>".
1040                           "fav <nickname>" => _m('COMMANDHELP', "add user's last notice as a 'fave'"),
1041                           // TRANS: Help message for IM/SMS command "fav #<notice_id>".
1042                           "fav #<notice_id>" => _m('COMMANDHELP', "add notice with the given id as a 'fave'"),
1043                           // TRANS: Help message for IM/SMS command "repeat #<notice_id>".
1044                           "repeat #<notice_id>" => _m('COMMANDHELP', "repeat a notice with a given id"),
1045                           // TRANS: Help message for IM/SMS command "repeat <nickname>".
1046                           "repeat <nickname>" => _m('COMMANDHELP', "repeat the last notice from user"),
1047                           // TRANS: Help message for IM/SMS command "reply #<notice_id>".
1048                           "reply #<notice_id>" => _m('COMMANDHELP', "reply to notice with a given id"),
1049                           // TRANS: Help message for IM/SMS command "reply <nickname>".
1050                           "reply <nickname>" => _m('COMMANDHELP', "reply to the last notice from user"),
1051                           // TRANS: Help message for IM/SMS command "join <group>".
1052                           "join <group>" => _m('COMMANDHELP', "join group"),
1053                           // TRANS: Help message for IM/SMS command "login".
1054                           "login" => _m('COMMANDHELP', "Get a link to login to the web interface"),
1055                           // TRANS: Help message for IM/SMS command "drop <group>".
1056                           "drop <group>" => _m('COMMANDHELP', "leave group"),
1057                           // TRANS: Help message for IM/SMS command "stats".
1058                           "stats" => _m('COMMANDHELP', "get your stats"),
1059                           // TRANS: Help message for IM/SMS command "stop".
1060                           "stop" => _m('COMMANDHELP', "same as 'off'"),
1061                           // TRANS: Help message for IM/SMS command "quit".
1062                           "quit" => _m('COMMANDHELP', "same as 'off'"),
1063                           // TRANS: Help message for IM/SMS command "sub <nickname>".
1064                           "sub <nickname>" => _m('COMMANDHELP', "same as 'follow'"),
1065                           // TRANS: Help message for IM/SMS command "unsub <nickname>".
1066                           "unsub <nickname>" => _m('COMMANDHELP', "same as 'leave'"),
1067                           // TRANS: Help message for IM/SMS command "last <nickname>".
1068                           "last <nickname>" => _m('COMMANDHELP', "same as 'get'"),
1069                           // TRANS: Help message for IM/SMS command "on <nickname>".
1070                           "on <nickname>" => _m('COMMANDHELP', "not yet implemented."),
1071                           // TRANS: Help message for IM/SMS command "off <nickname>".
1072                           "off <nickname>" => _m('COMMANDHELP', "not yet implemented."),
1073                           // TRANS: Help message for IM/SMS command "nudge <nickname>".
1074                           "nudge <nickname>" => _m('COMMANDHELP', "remind a user to update."),
1075                           // TRANS: Help message for IM/SMS command "invite <phone number>".
1076                           "invite <phone number>" => _m('COMMANDHELP', "not yet implemented."),
1077                           // TRANS: Help message for IM/SMS command "track <word>".
1078                           "track <word>" => _m('COMMANDHELP', "not yet implemented."),
1079                           // TRANS: Help message for IM/SMS command "untrack <word>".
1080                           "untrack <word>" => _m('COMMANDHELP', "not yet implemented."),
1081                           // TRANS: Help message for IM/SMS command "track off".
1082                           "track off" => _m('COMMANDHELP', "not yet implemented."),
1083                           // TRANS: Help message for IM/SMS command "untrack all".
1084                           "untrack all" => _m('COMMANDHELP', "not yet implemented."),
1085                           // TRANS: Help message for IM/SMS command "tracks".
1086                           "tracks" => _m('COMMANDHELP', "not yet implemented."),
1087                           // TRANS: Help message for IM/SMS command "tracking".
1088                           "tracking" => _m('COMMANDHELP', "not yet implemented."));
1089
1090         // Give plugins a chance to add or override...
1091         Event::handle('HelpCommandMessages', array($this, &$commands));
1092         foreach ($commands as $command => $help) {
1093             $out[] = "$command - $help";
1094         }
1095         $channel->output($this->user, implode("\n", $out));
1096     }
1097 }