]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Event/RSVP.php
Update translator documentation.
[quix0rs-gnu-social.git] / plugins / Event / RSVP.php
1 <?php
2 /**
3  * Data class for event RSVPs
4  *
5  * PHP version 5
6  *
7  * @category Data
8  * @package  StatusNet
9  * @author   Evan Prodromou <evan@status.net>
10  * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
11  * @link     http://status.net/
12  *
13  * StatusNet - the distributed open-source microblogging tool
14  * Copyright (C) 2011, StatusNet, Inc.
15  *
16  * This program is free software: you can redistribute it and/or modify
17  * it under the terms of the GNU Affero General Public License as published by
18  * the Free Software Foundation, either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
24  * GNU Affero General Public License for more details.
25  *
26  * You should have received a copy of the GNU Affero General Public License
27  * along with this program. If not, see <http://www.gnu.org/licenses/>.
28  */
29
30 if (!defined('STATUSNET')) {
31     exit(1);
32 }
33
34 /**
35  * Data class for event RSVPs
36  *
37  * @category Event
38  * @package  StatusNet
39  * @author   Evan Prodromou <evan@status.net>
40  * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
41  * @link     http://status.net/
42  *
43  * @see      Managed_DataObject
44  */
45 class RSVP extends Managed_DataObject
46 {
47     const POSITIVE = 'http://activitystrea.ms/schema/1.0/rsvp-yes';
48     const POSSIBLE = 'http://activitystrea.ms/schema/1.0/rsvp-maybe';
49     const NEGATIVE = 'http://activitystrea.ms/schema/1.0/rsvp-no';
50
51     public $__table = 'rsvp'; // table name
52     public $id;                // varchar(36) UUID
53     public $uri;               // varchar(255)
54     public $profile_id;        // int
55     public $event_id;          // varchar(36) UUID
56     public $response;            // tinyint
57     public $created;           // datetime
58
59     /**
60      * Get an instance by key
61      *
62      * @param string $k Key to use to lookup (usually 'id' for this class)
63      * @param mixed  $v Value to lookup
64      *
65      * @return RSVP object found, or null for no hits
66      */
67     function staticGet($k, $v=null)
68     {
69         return Memcached_DataObject::staticGet('RSVP', $k, $v);
70     }
71
72     /**
73      * Get an instance by compound key
74      *
75      * @param array $kv array of key-value mappings
76      *
77      * @return Bookmark object found, or null for no hits
78      */
79
80     function pkeyGet($kv)
81     {
82         return Memcached_DataObject::pkeyGet('RSVP', $kv);
83     }
84
85     /**
86      * Add the compound profile_id/event_id index to our cache keys
87      * since the DB_DataObject stuff doesn't understand compound keys
88      * except for the primary.
89      *
90      * @return array
91      */
92     function _allCacheKeys() {
93         $keys = parent::_allCacheKeys();
94         $keys[] = self::multicacheKey('RSVP', array('profile_id' => $this->profile_id,
95                                                     'event_id' => $this->event_id));
96         return $keys;
97     }
98
99     /**
100      * The One True Thingy that must be defined and declared.
101      */
102     public static function schemaDef()
103     {
104         return array(
105             'description' => 'Plan to attend event',
106             'fields' => array(
107                 'id' => array('type' => 'char',
108                               'length' => 36,
109                               'not null' => true,
110                               'description' => 'UUID'),
111                 'uri' => array('type' => 'varchar',
112                                'length' => 255,
113                                'not null' => true),
114                 'profile_id' => array('type' => 'int'),
115                 'event_id' => array('type' => 'char',
116                               'length' => 36,
117                               'not null' => true,
118                               'description' => 'UUID'),
119                 'response' => array('type' => 'char',
120                                   'length' => '1',
121                                   'description' => 'Y, N, or ? for three-state yes, no, maybe'),
122                 'created' => array('type' => 'datetime',
123                                    'not null' => true),
124             ),
125             'primary key' => array('id'),
126             'unique keys' => array(
127                 'rsvp_uri_key' => array('uri'),
128                 'rsvp_profile_event_key' => array('profile_id', 'event_id'),
129             ),
130             'foreign keys' => array('rsvp_event_id_key' => array('event', array('event_id' => 'id')),
131                                     'rsvp_profile_id__key' => array('profile', array('profile_id' => 'id'))),
132             'indexes' => array('rsvp_created_idx' => array('created')),
133         );
134     }
135
136     function saveNew($profile, $event, $verb, $options=array())
137     {
138         if (array_key_exists('uri', $options)) {
139             $other = RSVP::staticGet('uri', $options['uri']);
140             if (!empty($other)) {
141                 // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
142                 throw new ClientException(_m('RSVP already exists.'));
143             }
144         }
145
146         $other = RSVP::pkeyGet(array('profile_id' => $profile->id,
147                                      'event_id' => $event->id));
148
149         if (!empty($other)) {
150             // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
151             throw new ClientException(_m('RSVP already exists.'));
152         }
153
154         $rsvp = new RSVP();
155
156         $rsvp->id          = UUID::gen();
157         $rsvp->profile_id  = $profile->id;
158         $rsvp->event_id    = $event->id;
159         $rsvp->response      = self::codeFor($verb);
160
161         if (array_key_exists('created', $options)) {
162             $rsvp->created = $options['created'];
163         } else {
164             $rsvp->created = common_sql_now();
165         }
166
167         if (array_key_exists('uri', $options)) {
168             $rsvp->uri = $options['uri'];
169         } else {
170             $rsvp->uri = common_local_url('showrsvp',
171                                         array('id' => $rsvp->id));
172         }
173
174         $rsvp->insert();
175
176         self::blow('rsvp:for-event:%s', $event->id);
177
178         // XXX: come up with something sexier
179
180         $content = $rsvp->asString();
181
182         $rendered = $rsvp->asHTML();
183
184         $options = array_merge(array('object_type' => $verb),
185                                $options);
186
187         if (!array_key_exists('uri', $options)) {
188             $options['uri'] = $rsvp->uri;
189         }
190
191         $eventNotice = $event->getNotice();
192
193         if (!empty($eventNotice)) {
194             $options['reply_to'] = $eventNotice->id;
195         }
196
197         $saved = Notice::saveNew($profile->id,
198                                  $content,
199                                  array_key_exists('source', $options) ?
200                                  $options['source'] : 'web',
201                                  $options);
202
203         return $saved;
204     }
205
206     function codeFor($verb)
207     {
208         switch ($verb) {
209         case RSVP::POSITIVE:
210             return 'Y';
211             break;
212         case RSVP::NEGATIVE:
213             return 'N';
214             break;
215         case RSVP::POSSIBLE:
216             return '?';
217             break;
218         default:
219             // TRANS: Exception thrown when requesting an undefined verb for RSVP.
220             throw new Exception(sprintf(_m('Unknown verb "%s".'),$verb));
221         }
222     }
223
224     static function verbFor($code)
225     {
226         switch ($code) {
227         case 'Y':
228             return RSVP::POSITIVE;
229             break;
230         case 'N':
231             return RSVP::NEGATIVE;
232             break;
233         case '?':
234             return RSVP::POSSIBLE;
235             break;
236         default:
237             // TRANS: Exception thrown when requesting an undefined code for RSVP.
238             throw new Exception(sprintf(_m('Unknown code "%s".'),$code));
239         }
240     }
241
242     function getNotice()
243     {
244         $notice = Notice::staticGet('uri', $this->uri);
245         if (empty($notice)) {
246             // TRANS: Server exception thrown when requesting a non-exsting notice for an RSVP ("please respond").
247             // TRANS: %s is the RSVP with the missing notice.
248             throw new ServerException(sprintf(_m('RSVP %s does not correspond to a notice in the database.'),$this->id));
249         }
250         return $notice;
251     }
252
253     static function fromNotice($notice)
254     {
255         return RSVP::staticGet('uri', $notice->uri);
256     }
257
258     static function forEvent($event)
259     {
260         $keypart = sprintf('rsvp:for-event:%s', $event->id);
261
262         $idstr = self::cacheGet($keypart);
263
264         if ($idstr !== false) {
265             $ids = explode(',', $idstr);
266         } else {
267             $ids = array();
268
269             $rsvp = new RSVP();
270
271             $rsvp->selectAdd();
272             $rsvp->selectAdd('id');
273
274             $rsvp->event_id = $event->id;
275
276             if ($rsvp->find()) {
277                 while ($rsvp->fetch()) {
278                     $ids[] = $rsvp->id;
279                 }
280             }
281             self::cacheSet($keypart, implode(',', $ids));
282         }
283
284         $rsvps = array(RSVP::POSITIVE => array(),
285                        RSVP::NEGATIVE => array(),
286                        RSVP::POSSIBLE => array());
287
288         foreach ($ids as $id) {
289             $rsvp = RSVP::staticGet('id', $id);
290             if (!empty($rsvp)) {
291                 $verb = self::verbFor($rsvp->response);
292                 $rsvps[$verb][] = $rsvp;
293             }
294         }
295
296         return $rsvps;
297     }
298
299     function getProfile()
300     {
301         $profile = Profile::staticGet('id', $this->profile_id);
302         if (empty($profile)) {
303             // TRANS: Exception thrown when requesting a non-existing profile.
304             // TRANS: %s is the ID of the non-existing profile.
305             throw new Exception(sprintf(_m('No profile with ID %s.'),$this->profile_id));
306         }
307         return $profile;
308     }
309
310     function getEvent()
311     {
312         $event = Happening::staticGet('id', $this->event_id);
313         if (empty($event)) {
314             // TRANS: Exception thrown when requesting a non-existing event.
315             // TRANS: %s is the ID of the non-existing event.
316             throw new Exception(sprintf(_m('No event with ID %s.'),$this->event_id));
317         }
318         return $event;
319     }
320
321     function asHTML()
322     {
323         $event = Happening::staticGet('id', $this->event_id);
324
325         return self::toHTML($this->getProfile(),
326                             $event,
327                             $this->response);
328     }
329
330     function asString()
331     {
332         $event = Happening::staticGet('id', $this->event_id);
333
334         return self::toString($this->getProfile(),
335                               $event,
336                               $this->response);
337     }
338
339     static function toHTML($profile, $event, $response)
340     {
341         $fmt = null;
342
343         switch ($response) {
344         case 'Y':
345             // TRANS: HTML version of an RSVP ("please respond") status for a user.
346             // TRANS: %1$s is a profile URL, %2$s a profile name,
347             // TRANS: %3$s is an event URL, %4$s an event title.
348             $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> is attending <a href='%3\$s'>%4\$s</a>.</span>");
349             break;
350         case 'N':
351             // TRANS: HTML version of an RSVP ("please respond") status for a user.
352             // TRANS: %1$s is a profile URL, %2$s a profile name,
353             // TRANS: %3$s is an event URL, %4$s an event title.
354             $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> is not attending <a href='%3\$s'>%4\$s</a>.</span>");
355             break;
356         case '?':
357             // TRANS: HTML version of an RSVP ("please respond") status for a user.
358             // TRANS: %1$s is a profile URL, %2$s a profile name,
359             // TRANS: %3$s is an event URL, %4$s an event title.
360             $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> might attend <a href='%3\$s'>%4\$s</a>.</span>");
361             break;
362         default:
363             // TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
364             // TRANS: %s is the non-existing response code.
365             throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
366             break;
367         }
368
369         if (empty($event)) {
370             $eventUrl = '#';
371             // TRANS: Used as event title when not event title is available.
372             // TRANS: Used as: Username [is [not ] attending|might attend] an unknown event.
373             $eventTitle = _m('an unknown event');
374         } else {
375             $notice = $event->getNotice();
376             $eventUrl = $notice->bestUrl();
377             $eventTitle = $event->title;
378         }
379
380         return sprintf($fmt,
381                        htmlspecialchars($profile->profileurl),
382                        htmlspecialchars($profile->getBestName()),
383                        htmlspecialchars($eventUrl),
384                        htmlspecialchars($eventTitle));
385     }
386
387     static function toString($profile, $event, $response)
388     {
389         $fmt = null;
390
391         switch ($response) {
392         case 'Y':
393             // TRANS: Plain text version of an RSVP ("please respond") status for a user.
394             // TRANS: %1$s is a profile name, %2$s is an event title.
395             $fmt = _m('%1$s is attending %2$s.');
396             break;
397         case 'N':
398             // TRANS: Plain text version of an RSVP ("please respond") status for a user.
399             // TRANS: %1$s is a profile name, %2$s is an event title.
400             $fmt = _m('%1$s is not attending %2$s.');
401             break;
402         case '?':
403             // TRANS: Plain text version of an RSVP ("please respond") status for a user.
404             // TRANS: %1$s is a profile name, %2$s is an event title.
405             $fmt = _m('%1$s might attend %2$s.');
406             break;
407         default:
408             // TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
409             // TRANS: %s is the non-existing response code.
410             throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
411             break;
412         }
413
414         if (empty($event)) {
415             // TRANS: Used as event title when not event title is available.
416             // TRANS: Used as: Username [is [not ] attending|might attend] an unknown event.
417             $eventTitle = _m('an unknown event');
418         } else {
419             $notice = $event->getNotice();
420             $eventTitle = $event->title;
421         }
422
423         return sprintf($fmt,
424                        $profile->getBestName(),
425                        $eventTitle);
426     }
427
428     function delete()
429     {
430         self::blow('rsvp:for-event:%s', $event->id);
431         parent::delete();
432     }
433 }