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