]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/useractivitystream.php
Add a registration activity to the end of every backup
[quix0rs-gnu-social.git] / lib / useractivitystream.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 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 /**
21  * Class for activity streams
22  *
23  * Includes faves, notices, and subscriptions.
24  *
25  * We extend atomusernoticefeed since it does some nice setup for us.
26  *
27  */
28 class UserActivityStream extends AtomUserNoticeFeed
29 {
30     public $activities = array();
31
32     const OUTPUT_STRING = 1;
33     const OUTPUT_RAW = 2;
34     public $outputMode = self::OUTPUT_STRING;
35
36     /**
37      *
38      * @param User $user
39      * @param boolean $indent
40      * @param boolean $outputMode: UserActivityStream::OUTPUT_STRING to return a string,
41      *                           or UserActivityStream::OUTPUT_RAW to go to raw output.
42      *                           Raw output mode will attempt to stream, keeping less
43      *                           data in memory but will leave $this->activities incomplete.
44      */
45     function __construct($user, $indent = true, $outputMode = UserActivityStream::OUTPUT_STRING)
46     {
47         parent::__construct($user, null, $indent);
48
49         $this->outputMode = $outputMode;
50         if ($this->outputMode == self::OUTPUT_STRING) {
51             // String buffering? Grab all the notices now.
52             $notices = $this->getNotices();
53         } elseif ($this->outputMode == self::OUTPUT_RAW) {
54             // Raw output... need to restructure from the stringer init.
55             $this->xw = new XMLWriter();
56             $this->xw->openURI('php://output');
57             if(is_null($indent)) {
58                 $indent = common_config('site', 'indent');
59             }
60             $this->xw->setIndent($indent);
61
62             // We'll fetch notices later.
63             $notices = array();
64         } else {
65             throw new Exception('Invalid outputMode provided to ' . __METHOD__);
66         }
67
68         // Assume that everything but notices is feasible
69         // to pull at once and work with in memory...
70
71         $subscriptions = $this->getSubscriptions();
72         $subscribers   = $this->getSubscribers();
73         $groups        = $this->getGroups();
74         $faves         = $this->getFaves();
75
76         $objs = array_merge($subscriptions, $subscribers, $groups, $faves, $notices);
77
78         $subscriptions = null;
79         $subscribers   = null;
80         $groups        = null;
81         $faves         = null;
82
83         unset($subscriptions);
84         unset($subscribers);
85         unset($groups);
86         unset($faves);
87
88         // Sort by create date
89
90         usort($objs, 'UserActivityStream::compareObject');
91
92         // We'll keep these around for later, and interleave them into
93         // the output stream with the user's notices.
94
95         $this->objs = $objs;
96     }
97
98     /**
99      * Interleave the pre-sorted subs/groups/faves with the user's
100      * notices, all in reverse chron order.
101      */
102     function renderEntries($format=Feed::ATOM, $handle=null)
103     {
104         $haveOne = false;
105
106         $end = time() + 1;
107         foreach ($this->objs as $obj) {
108             try {
109                 $act = $obj->asActivity();
110             } catch (Exception $e) {
111                 common_log(LOG_ERR, $e->getMessage());
112                 continue;
113             }
114
115             $start = $act->time;
116
117             if ($this->outputMode == self::OUTPUT_RAW && $start != $end) {
118                 // In raw mode, we haven't pre-fetched notices.
119                 // Grab the chunks of notices between other activities.
120                 try {
121                     $notices = $this->getNoticesBetween($start, $end);
122                     foreach ($notices as $noticeAct) {
123                         try {
124                             $nact = $noticeAct->asActivity();
125                             if ($format == Feed::ATOM) {
126                                 $nact->outputTo($this, false, false);
127                             } else {
128                                 if ($haveOne) {
129                                     fwrite($handle, ",");
130                                 }
131                                 fwrite($handle, json_encode($nact->asArray()));
132                                 $haveOne = true;
133                             }
134                         } catch (Exception $e) {
135                             common_log(LOG_ERR, $e->getMessage());
136                             continue;
137                         }
138                         $nact = null;
139                         unset($nact);
140                     }
141                 } catch (Exception $e) {
142                     common_log(LOG_ERR, $e->getMessage());
143                 }
144             }
145
146             $notices = null;
147             unset($notices);
148
149             try {
150                 if ($format == Feed::ATOM) {
151                     // Only show the author sub-element if it's different from default user
152                     $act->outputTo($this, false, ($act->actor->id != $this->user->uri));
153                 } else {
154                     if ($haveOne) {
155                         fwrite($handle, ",");
156                     }
157                     fwrite($handle, json_encode($act->asArray()));
158                     $haveOne = true;
159                 }
160             } catch (Exception $e) {
161                 common_log(LOG_ERR, $e->getMessage());
162             }
163
164             $act = null;
165             unset($act);
166
167             $end = $start;
168         }
169
170         if ($this->outputMode == self::OUTPUT_RAW) {
171             // Grab anything after the last pre-sorted activity.
172             try {
173                 $notices = $this->getNoticesBetween(0, $end);
174                 foreach ($notices as $noticeAct) {
175                     try {
176                         $nact = $noticeAct->asActivity();
177                         if ($format == Feed::ATOM) {
178                             $nact->outputTo($this, false, false);
179                         } else {
180                             if ($haveOne) {
181                                 fwrite($handle, ",");
182                             }
183                             fwrite($handle, json_encode($nact->asArray()));
184                             $haveOne = true;
185                         }
186                     } catch (Exception $e) {
187                         common_log(LOG_ERR, $e->getMessage());
188                         continue;
189                     }
190                 }
191             } catch (Exception $e) {
192                 common_log(LOG_ERR, $e->getMessage());
193             }
194         }
195
196         // We always add the registration activity at the end, even if
197         // they have older activities (from restored backups) in their stream.
198
199         try {
200             $ract = $this->user->registrationActivity();
201             if ($format == Feed::ATOM) {
202                 $ract->outputTo($this, false, false);
203             } else {
204                 if ($haveOne) {
205                     fwrite($handle, ",");
206                 }
207                 fwrite($handle, json_encode($ract->asArray()));
208                 $haveOne = true;
209             }
210         } catch (Exception $e) {
211             common_log(LOG_ERR, $e->getMessage());
212             continue;
213         }
214     }
215
216     function compareObject($a, $b)
217     {
218         $ac = strtotime((empty($a->created)) ? $a->modified : $a->created);
219         $bc = strtotime((empty($b->created)) ? $b->modified : $b->created);
220
221         return (($ac == $bc) ? 0 : (($ac < $bc) ? 1 : -1));
222     }
223
224     function getSubscriptions()
225     {
226         $subs = array();
227
228         $sub = new Subscription();
229
230         $sub->subscriber = $this->user->id;
231
232         if ($sub->find()) {
233             while ($sub->fetch()) {
234                 if ($sub->subscribed != $this->user->id) {
235                     $subs[] = clone($sub);
236                 }
237             }
238         }
239
240         return $subs;
241     }
242
243     function getSubscribers()
244     {
245         $subs = array();
246
247         $sub = new Subscription();
248
249         $sub->subscribed = $this->user->id;
250
251         if ($sub->find()) {
252             while ($sub->fetch()) {
253                 if ($sub->subscriber != $this->user->id) {
254                     $subs[] = clone($sub);
255                 }
256             }
257         }
258
259         return $subs;
260     }
261
262     function getFaves()
263     {
264         $faves = array();
265
266         $fave = new Fave();
267
268         $fave->user_id = $this->user->id;
269
270         if ($fave->find()) {
271             while ($fave->fetch()) {
272                 $faves[] = clone($fave);
273             }
274         }
275
276         return $faves;
277     }
278
279     /**
280      *
281      * @param int $start unix timestamp for earliest
282      * @param int $end unix timestamp for latest
283      * @return array of Notice objects
284      */
285     function getNoticesBetween($start=0, $end=0)
286     {
287         $notices = array();
288
289         $notice = new Notice();
290
291         $notice->profile_id = $this->user->id;
292
293         if ($start) {
294             $tsstart = common_sql_date($start);
295             $notice->whereAdd("created >= '$tsstart'");
296         }
297         if ($end) {
298             $tsend = common_sql_date($end);
299             $notice->whereAdd("created < '$tsend'");
300         }
301
302         $notice->orderBy('created DESC');
303
304         if ($notice->find()) {
305             while ($notice->fetch()) {
306                 $notices[] = clone($notice);
307             }
308         }
309
310         return $notices;
311     }
312
313     function getNotices()
314     {
315         return $this->getNoticesBetween();
316     }
317
318     function getGroups()
319     {
320         $groups = array();
321
322         $gm = new Group_member();
323
324         $gm->profile_id = $this->user->id;
325
326         if ($gm->find()) {
327             while ($gm->fetch()) {
328                 $groups[] = clone($gm);
329             }
330         }
331
332         return $groups;
333     }
334
335     function writeJSON($handle)
336     {
337         require_once INSTALLDIR.'/lib/activitystreamjsondocument.php';
338         fwrite($handle, '{"items": [');
339         $this->renderEntries(Feed::JSON, $handle);
340         fwrite($handle, ']}');
341     }
342 }