]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/YammerImport/lib/yammerrunner.php
Merge ActivitySpam plugin
[quix0rs-gnu-social.git] / plugins / YammerImport / lib / yammerrunner.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 if (!defined('STATUSNET')) {
21     exit(1);
22 }
23
24 /**
25  * State machine for running through Yammer import.
26  *
27  * @package YammerImportPlugin
28  * @author Brion Vibber <brion@status.net>
29  */
30 class YammerRunner
31 {
32     private $state;
33     private $client;
34     private $importer;
35
36     /**
37      * Normalize our singleton state and give us a YammerRunner object to play with!
38      *
39      * @return YammerRunner
40      */
41     public static function init()
42     {
43         $state = Yammer_state::staticGet('id', 1);
44         if (!$state) {
45             $state = self::initState();
46         }
47         return new YammerRunner($state);
48     }
49
50     private static function initState()
51     {
52         $state = new Yammer_state();
53         $state->id = 1;
54         $state->state = 'init';
55         $state->created = common_sql_now();
56         $state->modified = common_sql_now();
57         $state->insert();
58         return $state;
59     }
60
61     private function __construct($state)
62     {
63         $this->state = $state;
64
65         $this->client = new SN_YammerClient(
66             common_config('yammer', 'consumer_key'),
67             common_config('yammer', 'consumer_secret'),
68             $this->state->oauth_token,
69             $this->state->oauth_secret);
70
71         $this->importer = new YammerImporter($this->client);
72     }
73
74     /**
75      * Check which state we're in
76      *
77      * @return string
78      */
79     public function state()
80     {
81         return $this->state->state;
82     }
83
84     /**
85      * Is the import done, finished, complete, finito?
86      *
87      * @return boolean
88      */
89     public function isDone()
90     {
91         $workStates = array('import-users', 'import-groups', 'fetch-messages', 'save-messages');
92         return ($this->state() == 'done');
93     }
94
95     /**
96      * Check if we have work to do in iterate().
97      *
98      * @return boolean
99      */
100     public function hasWork()
101     {
102         $workStates = array('import-users', 'import-groups', 'fetch-messages', 'save-messages');
103         return in_array($this->state(), $workStates);
104     }
105
106     /**
107      * Blow away any current state!
108      */
109     public function reset()
110     {
111         $this->state->delete();
112         $this->state = self::initState();
113     }
114
115     /**
116      * Start the authentication process! If all goes well, we'll get back a URL.
117      * Have the user visit that URL, log in on Yammer and verify the importer's
118      * permissions. They'll get back a verification code, which needs to be passed
119      * on to saveAuthToken().
120      *
121      * @return string URL
122      */
123     public function requestAuth()
124     {
125         if ($this->state->state != 'init') {
126             // TRANS: Server exception thrown if a Yammer authentication request is already present.
127             throw new ServerException(_m('Cannot request Yammer auth; already there!'));
128         }
129
130         $data = $this->client->requestToken();
131
132         $old = clone($this->state);
133         $this->state->state = 'requesting-auth';
134         $this->state->oauth_token = $data['oauth_token'];
135         $this->state->oauth_secret = $data['oauth_token_secret'];
136         $this->state->modified = common_sql_now();
137         $this->state->update($old);
138
139         return $this->getAuthUrl();
140     }
141
142     /**
143      * When already in requesting-auth state, grab the URL to send the user to
144      * to complete OAuth setup.
145      *
146      * @return string URL
147      */
148     function getAuthUrl()
149     {
150         if ($this->state() == 'requesting-auth') {
151             return $this->client->authorizeUrl($this->state->oauth_token);
152         } else {
153             // TRANS: Server exception thrown when requesting a Yammer authentication URL while in an incorrect state.
154             throw new ServerException(_m('Cannot get Yammer auth URL when not in requesting-auth state!'));
155         }
156     }
157
158     /**
159      * Now that the user's given us this verification code from Yammer, we can
160      * request a final OAuth token/secret pair which we can use to access the
161      * API.
162      *
163      * After success here, we'll be ready to move on and run through iterate()
164      * until the import is complete.
165      *
166      * @param string $verifier
167      * @return boolean success
168      */
169     public function saveAuthToken($verifier)
170     {
171         if ($this->state->state != 'requesting-auth') {
172             // TRANS: Server exception thrown if a Yammer authentication token could not be saved in a certain import state.
173             // TRANS: %s is the import state in the which the error occurred.
174             throw new ServerException(_m('Cannot save auth token in Yammer import state %s.',$this->state->state));
175         }
176
177         $data = $this->client->accessToken($verifier);
178
179         $old = clone($this->state);
180         $this->state->state = 'import-users';
181         $this->state->oauth_token = $data['oauth_token'];
182         $this->state->oauth_secret = $data['oauth_token_secret'];
183         $this->state->modified = common_sql_now();
184         $this->state->update($old);
185
186         return true;
187     }
188
189     /**
190      * Once authentication is complete, we need to call iterate() a bunch of times
191      * until state() returns 'done'.
192      *
193      * @return boolean success
194      */
195     public function iterate()
196     {
197         switch($this->state())
198         {
199             case 'init':
200             case 'requesting-auth':
201                 // Neither of these should reach our background state!
202                 common_log(LOG_ERR, "Non-background YammerImport state '$state->state' during import run!");
203                 return false;
204             case 'import-users':
205                 return $this->iterateUsers();
206             case 'import-groups':
207                 return $this->iterateGroups();
208             case 'fetch-messages':
209                 return $this->iterateFetchMessages();
210             case 'save-messages':
211                 return $this->iterateSaveMessages();
212             default:
213                 common_log(LOG_ERR, "Invalid YammerImport state '$state->state' during import run!");
214                 return false;
215         }
216     }
217
218     /**
219      * Trundle through one 'page' return of up to 50 user accounts retrieved
220      * from the Yammer API, importing them as we go.
221      *
222      * When we run out of users, move on to groups.
223      *
224      * @return boolean success
225      */
226     private function iterateUsers()
227     {
228         $old = clone($this->state);
229
230         $page = intval($this->state->users_page) + 1;
231         $data = $this->client->users(array('page' => $page));
232
233         if (count($data) == 0) {
234             common_log(LOG_INFO, "Finished importing Yammer users; moving on to groups.");
235             $this->state->state = 'import-groups';
236         } else {
237             foreach ($data as $item) {
238                 $user = $this->importer->importUser($item);
239                 common_log(LOG_INFO, "Imported Yammer user " . $item['id'] . " as $user->nickname ($user->id)");
240             }
241             $this->state->users_page = $page;
242         }
243         $this->state->modified = common_sql_now();
244         $this->state->update($old);
245         return true;
246     }
247
248     /**
249      * Trundle through one 'page' return of up to 20 user groups retrieved
250      * from the Yammer API, importing them as we go.
251      *
252      * When we run out of groups, move on to messages.
253      *
254      * @return boolean success
255      */
256     private function iterateGroups()
257     {
258         $old = clone($this->state);
259
260         $page = intval($this->state->groups_page) + 1;
261         $data = $this->client->groups(array('page' => $page));
262
263         if (count($data) == 0) {
264             common_log(LOG_INFO, "Finished importing Yammer groups; moving on to messages.");
265             $this->state->state = 'fetch-messages';
266         } else {
267             foreach ($data as $item) {
268                 $group = $this->importer->importGroup($item);
269                 common_log(LOG_INFO, "Imported Yammer group " . $item['id'] . " as $group->nickname ($group->id)");
270             }
271             $this->state->groups_page = $page;
272         }
273         $this->state->modified = common_sql_now();
274         $this->state->update($old);
275         return true;
276     }
277
278     /**
279      * Trundle through one 'page' return of up to 20 public messages retrieved
280      * from the Yammer API, saving them to our stub table for future import in
281      * correct chronological order.
282      *
283      * When we run out of messages to fetch, move on to saving the messages.
284      *
285      * @return boolean success
286      */
287     private function iterateFetchMessages()
288     {
289         $old = clone($this->state);
290
291         $oldest = intval($this->state->messages_oldest);
292         if ($oldest) {
293             $params = array('older_than' => $oldest);
294         } else {
295             $params = array();
296         }
297         $data = $this->client->messages($params);
298         $messages = $data['messages'];
299
300         if (count($messages) == 0) {
301             common_log(LOG_INFO, "Finished fetching Yammer messages; moving on to save messages.");
302             $this->state->state = 'save-messages';
303         } else {
304             foreach ($messages as $item) {
305                 $stub = Yammer_notice_stub::staticGet($item['id']);
306                 if (!$stub) {
307                     Yammer_notice_stub::record($item['id'], $item);
308                 }
309                 $oldest = $item['id'];
310             }
311             $this->state->messages_oldest = $oldest;
312         }
313         $this->state->modified = common_sql_now();
314         $this->state->update($old);
315         return true;
316     }
317
318     private function iterateSaveMessages()
319     {
320         $old = clone($this->state);
321
322         $newest = intval($this->state->messages_newest);
323
324         $stub = new Yammer_notice_stub();
325         if ($newest) {
326             $stub->whereAdd('id > ' . $newest);
327         }
328         $stub->limit(20);
329         $stub->orderBy('id');
330         $stub->find();
331
332         if ($stub->N == 0) {
333             common_log(LOG_INFO, "Finished saving Yammer messages; import complete!");
334             $this->state->state = 'done';
335         } else {
336             while ($stub->fetch()) {
337                 $item = $stub->getData();
338                 $notice = $this->importer->importNotice($item);
339                 common_log(LOG_INFO, "Imported Yammer notice " . $item['id'] . " as $notice->id");
340                 $newest = $item['id'];
341             }
342             $this->state->messages_newest = $newest;
343         }
344         $this->state->modified = common_sql_now();
345         $this->state->update($old);
346         return true;
347     }
348
349     /**
350      * Count the number of Yammer users we've mapped into our system!
351      *
352      * @return int
353      */
354     public function countUsers()
355     {
356         $map = new Yammer_user();
357         return $map->count();
358     }
359
360     /**
361      * Count the number of Yammer groups we've mapped into our system!
362      *
363      * @return int
364      */
365     public function countGroups()
366     {
367         $map = new Yammer_group();
368         return $map->count();
369     }
370
371     /**
372      * Count the number of Yammer notices we've pulled down for pending import...
373      *
374      * @return int
375      */
376     public function countFetchedNotices()
377     {
378         $map = new Yammer_notice_stub();
379         return $map->count();
380     }
381
382     /**
383      * Count the number of Yammer notices we've mapped into our system!
384      *
385      * @return int
386      */
387     public function countSavedNotices()
388     {
389         $map = new Yammer_notice();
390         return $map->count();
391     }
392
393     /**
394      * Start running import work in the background queues...
395      */
396     public function startBackgroundImport()
397     {
398         $qm = QueueManager::get();
399         $qm->enqueue('YammerImport', 'yammer');
400     }
401
402     /**
403      * Record an error condition from a background run, which we should
404      * display in progress state for the admin.
405      *
406      * @param string $msg
407      */
408     public function recordError($msg)
409     {
410         // HACK HACK HACK
411         try {
412             $temp = new Yammer_state();
413             $temp->query('ROLLBACK');
414         } catch (Exception $e) {
415             common_log(LOG_ERR, 'Exception while confirming rollback while recording error: ' . $e->getMessage());
416         }
417         $old = clone($this->state);
418         $this->state->last_error = $msg;
419         $this->state->update($old);
420     }
421
422     /**
423      * Clear the error state.
424      */
425     public function clearError()
426     {
427         $this->recordError('');
428     }
429
430     /**
431      * Get the last recorded background error message, if any.
432      *
433      * @return string
434      */
435     public function lastError()
436     {
437         return $this->state->last_error;
438     }
439 }