by Dave Cohen: new hook after setting props, gives third party modules a chance to...
[project/fb.git] / fb_devel.module
1 <?php
2 /**
3 * @file
4 * Makes development with Drupal for Facebook much easier. Keep this
5 * module enabled until you're confident your app works perfectly.
6 *
7 * Produces warning messages and log messages
8 * when it detects something is wrong with
9 * your configuration.
10 *
11 * Runs tests for Drupal's status page.
12 *
13 */
14
15 define('FB_DEVEL_URL_LINTER', 'http://developers.facebook.com/tools/lint');
16
17 function fb_devel_menu() {
18 $items['fb/devel'] = array(
19 'page callback' => 'fb_devel_page',
20 'type' => MENU_CALLBACK,
21 'access callback' => TRUE, /* TODO: restrict access */
22 );
23
24 $items['fb/devel/fbu'] = array(
25 'page callback' => 'fb_devel_fbu_page',
26 'type' => MENU_CALLBACK,
27 'access callback' => TRUE,
28 );
29
30 $items['fb/devel/tab'] = array(
31 'page callback' => 'fb_devel_tab',
32 'type' => MENU_CALLBACK,
33 'access callback' => TRUE, /* TODO: restrict access */
34 );
35
36 // Return some info for debugging AHAH problems
37 $items['fb/devel/js'] = array(
38 'page callback' => 'fb_devel_js',
39 'type' => MENU_CALLBACK,
40 'access callback' => TRUE,
41 );
42
43 $items['fb/devel/linter'] = array(
44 'title' => 'Facebook linter',
45 'page callback' => 'drupal_not_found', // Because we alter link, below.
46 'type' => MENU_NORMAL_ITEM,
47 'access arguments' => array('access devel information'),
48 'menu_name' => 'devel',
49 'options' => array('alter' => TRUE),
50 );
51
52 return $items;
53 }
54
55 // Trick learned from devel.module, to make link to linter for the current page.
56 function fb_devel_translated_menu_link_alter(&$item) {
57 if ($item['link_path'] == 'fb/devel/linter' ||
58 $item['link_path'] == FB_DEVEL_URL_LINTER) {
59 //dpm($item, __FUNCTION__);
60 $item['href'] = FB_DEVEL_URL_LINTER; // href matters. link_path and router path do not.
61 $item['localized_options']['query'] = array(
62 'url' => url(request_path(), array('absolute' => TRUE)),
63 );
64 }
65 }
66
67 /**
68 * Implements hook_init().
69 */
70 function fb_devel_init() {
71 if (user_access('access devel information')) {
72 // Add our module's javascript.
73 drupal_add_js(drupal_get_path('module', 'fb_devel') . '/fb_devel.js');
74 drupal_add_js(array('fb_devel' => array('verbose' => fb_verbose())), 'setting');
75 }
76
77 // fb_settings.inc sanity check.
78 if (isset($GLOBALS['fb_init_no_settings'])) {
79 if (user_access('access administration pages') && (module_exists('fb_canvas') || module_exists('fb_tab'))) {
80 // fb_settings.php must be included for canvas or tab support.
81 drupal_set_message(t('!drupal_for_facebook (fb_canvas.module) has been enabled, but fb_settings.inc is not included in settings.php. Please read the !readme.',
82 array('!drupal_for_facebook' => l(t('Drupal for Facebook'), 'http://drupal.org/project/fb'),
83 // This link should work with clean URLs
84 // disabled.
85 '!readme' => '<a href=' . base_path() . drupal_get_path('module', 'fb') . '/README.txt>README.txt</a>')), 'error');
86 }
87 }
88
89
90
91 // $conf['fb_apikey'] sanity check.
92 if ($apikey = variable_get(FB_VAR_APIKEY, NULL)) {
93 $fb_app = fb_get_app(array('apikey' => $apikey));
94 $message = t('Drupal for Facebook no longer uses the \'fb_apikey\' variable. Change $conf[\'fb_apikey\'] in your settings.php. Use $conf[\'fb_id\'] instead, and make the value the app id (%app_id) instead of the apikey.', array(
95 '%app_id' => $fb_app->id,
96 ));
97 if (user_access(FB_PERM_ADMINISTER)) {
98 drupal_set_message($message, 'error');
99 }
100 watchdog('fb_devel', $message, array(), WATCHDOG_WARNING);
101 }
102
103 // $conf['fb_id'] sanity check
104 if ($id = variable_get(FB_VAR_ID, NULL)) {
105 if ($label = variable_get(FB_CONNECT_VAR_PRIMARY, NULL)) {
106 $fb_app = fb_get_app(array('label' => $label));
107 if ($fb_app && ($fb_app->id != $id)) {
108 $message = t('Drupal for Facebook has detected a problem. $conf[\'fb_id\'] (%fb_id) is not the same as the primary application %label (%fb_primary_id).',
109 array(
110 '%fb_id' => $id,
111 '%fb_primary_id' => $fb_app->id,
112 '%label' => $fb_app->label,
113 ));
114 if (user_access(FB_PERM_ADMINISTER)) {
115 drupal_set_message($message, 'error');
116 }
117 watchdog('fb_devel', $message, array(), WATCHDOG_WARNING);
118 }
119 }
120 }
121
122 // fb_user table sanity check
123 if (module_exists('fb_user')) {
124 if (db_query("SELECT count(*) FROM {authmap} WHERE module='fb_user'")->fetchField()) {
125 $message = 'fb_user data has not been migrated from authmap table. Run update.php.';
126 $args = array();
127 if (user_access('access administration pages')) {
128 drupal_set_message(t($message, $args), 'error');
129 }
130 watchdog('fb_devel', $message, $args, WATCHDOG_ERROR);
131 }
132 }
133
134 // Old url rewrite sanity check.
135 if ($id = fb_settings(FB_SETTINGS_CB)) {
136 if ($GLOBALS['_fb_app']->id != $id && $GLOBALS['_fb_app']->apikey == $id) {
137 $message = 'Facebook callback URLs have changed. They now include the app\'s ID, instead of APIKEY. Your application %label has not been updated. Either <a target=_top href=!sync_url>sync properties</a> or manually change callbacks on remote settings.';
138 $args = array(
139 '%label' => $GLOBALS['_fb_app']->title,
140 '!sync_url' => url(FB_PATH_ADMIN_APPS . '/' . $GLOBALS['_fb_app']->label . '/fb/set_props', array(
141 'fb_url_alter' => FALSE,
142 )),
143 );
144 if (user_access('access administration pages')) {
145 drupal_set_message(t($message, $args), 'warning');
146 }
147 watchdog('fb_devel', $message, $args, WATCHDOG_WARNING);
148 }
149 }
150 }
151
152 /**
153 * Implements hook_fb().
154 */
155 function fb_devel_fb($op, $data, &$return) {
156 $fb_app = isset($data['fb_app']) ? $data['fb_app'] : NULL;
157 $fb = isset($data['fb']) ? $data['fb'] : NULL;
158 $errors = 0;
159
160 if ($op == FB_OP_INITIALIZE) {
161
162 if (fb_settings(FB_SETTINGS_APIKEY) &&
163 ($fb_app->apikey != fb_settings(FB_SETTINGS_APIKEY))) {
164 $message = t('Drupal for Facebook has detected a problem. The initialized app has an apikey (%fb_app_apikey), while the settings indicates a different apikey (%fb_settings_apikey).', array(
165 '%fb_app_apikey' => $fb_app->apikey,
166 '%fb_settings_apikey' => fb_settings(FB_SETTINGS_APIKEY),
167 ));
168 drupal_set_message($message, 'error');
169 watchdog('fb_devel', $message, array(), WATCHDOG_WARNING);
170 $errors++;
171 }
172
173 // This value comes from url rewriting. Recommended on canvas pages.
174 $id = fb_settings(FB_SETTINGS_CB);
175
176 // Sanity check.
177 if ($id && $id != $fb_app->id) {
178 $message = t('fb_app id (%fb_app_id) does not equal fb settings id (%fb_settings_id). This is usually caused by the wrong callback url on your <a href="!url">facebook application settings form</a>.',
179 array('%fb_app_id' => $fb_app->id,
180 '%fb_settings_id' => $id,
181 '!url' => "http://www.facebook.com/developers/apps.php",
182 ));
183 drupal_set_message($message, 'warning');
184 watchdog('fb_devel', $message, array(), WATCHDOG_WARNING);
185 $errors++;
186 }
187
188 // Catch badly formed links ASAP.
189 if ($id && !fb_is_canvas() && !fb_is_tab()) {
190 // Skip check on callbacks from facebook.
191 if ((arg(0) != 'fb_app') || (arg(1) != 'event')) {
192 $message = t('URL starts with %prefix. This should never happen on connect pages. Did the previous page have a badly formed link?', array(
193 '%prefix' => FB_SETTINGS_CB . '/' . $id,
194 ));
195 drupal_set_message($message, 'error');
196 $errors++;
197 }
198 }
199
200 // path replacement sanity check
201 global $base_path;
202 if ($base_path == "/{$fb_app->canvas}/") {
203 $message = t('Facebook canvas page matches Drupal base_path (%base_path). This is currently not supported.',
204 array('%base_path' => $base_path));
205 drupal_set_message($message, 'error');
206 watchdog('fb_devel', $message);
207 }
208
209 // Old API Sanity check.
210 if (isset($_REQUEST['fb_sig'])) {
211 $message = t('Passed old-style fb_sig parameters. Go to !url, edit your application. Under "migrations" enable "new sdks".', array(
212 '!url' => 'http://www.facebook.com/developers/apps.php',
213 ));
214 dpm($message);
215 watchdog('fb_devel', $message);
216 }
217
218 // server URL sanity check
219 // This is an expensive test, because it invokes api_client.
220 try {
221 $props = $fb->api(array(
222 'method' => 'admin.getAppProperties',
223 'access_token' => fb_get_token($fb),
224 'properties' => array('connect_url', 'callback_url'),
225 ));
226 $props = json_decode($props, TRUE);
227 if (is_array($props)) {
228 // Strip "http(s):" to avoid warnings when the only difference is http vs https
229 $baseurl = str_replace(array('http://','https://'), '', $GLOBALS['base_url']);
230 foreach ($props as $prop => $url) {
231 if ($url && (strpos($url, $baseurl) === FALSE)) {
232 $message = t('The Facebook Application labeled %label has a suspicious %prop. The value is %value, while something starting with %url was expected. Consider editing !applink.', array(
233 '%label' => $fb_app->label,
234 '%prop' => $prop,
235 '%value' => $url,
236 '%url' => $GLOBALS['base_url'],
237 '!applink' => l($fb_app->label, FB_PATH_ADMIN_APPS . '/' . $fb_app->label),
238 ));
239
240 if (user_access('access administration pages')) {
241 drupal_set_message($message, 'warning');
242 }
243 watchdog('fb_devel', $message);
244 }
245 }
246 }
247 }
248 catch (Exception $e) {
249 dpm($e, __FUNCTION__);
250 if ($e->getCode() == 102) {
251 // Session key invalid or no longer valid 102, which we can ignore.
252 }
253 else {
254 fb_log_exception($e, t('Failed to get app properties for %name.', array('%name' => $fb_app->title)));
255 }
256 }
257
258 // App data sanity checks.
259 $fb_app_data = fb_get_app_data($fb_app);
260
261 // Account mapping format has changed!
262 if (isset($fb_app_data['fb_user']) && !is_array($fb_app_data['fb_user']['map_account'])) {
263 $message = 'The options for mapping facebook account to local accounts has changed. You must manually <a href=!url>edit your application</a>. Look for the map accounts options under facebook user settings.';
264 $args = array('!url' => url(FB_PATH_ADMIN_APPS . '/' . $fb_app->label));
265 if (user_access('access administration pages')) {
266 drupal_set_message(t($message, $args), 'error');
267 }
268 watchdog('fb_devel', $message, $args, WATCHDOG_ERROR);
269 }
270
271 }
272
273 elseif ($op == FB_APP_OP_EVENT) {
274 $type = $data['event_type'];
275 // Facebook has notified us of some event.
276 $message = t('Facebook has notified the %label application of a %type event.',
277 array('%label' => $fb_app->label,
278 '%type' => $type));
279 $message .= '<pre>' . print_r($data, 1);
280 $message .= print_r($_REQUEST, 1) . '</pre>';
281 watchdog('fb_devel', $message);
282 }
283
284 elseif ($op == FB_OP_JS) {
285 // Start debugger
286 //$return[] = "debugger; // added by fb_devel.module";
287 }
288
289 elseif ($op == FB_OP_POST_INIT) {
290 if (isset($_SESSION['fb_devel'])) {
291 // Counter helps track how long session has been around.
292 $_SESSION['fb_devel']['FB_OP_POST_INIT'] = $_SESSION['fb_devel']['FB_OP_POST_INIT'] + 1;
293 }
294 else {
295 $_SESSION['fb_devel']['FB_OP_POST_INIT'] = 1;
296 }
297
298 // Javascript to help us with sanity checks.
299 drupal_add_js(array(
300 'fb_devel' => array(
301 'session_id' => session_id(),
302 'session_name' => session_name(),
303 ),
304 ), 'setting');
305 }
306 elseif ($op == FB_OP_AJAX_EVENT) {
307 if (fb_verbose() == 'extreme' && FALSE) { // disabled to prevent console not defined error.
308 $session_id = session_id();
309 $session_name = session_name();
310 $return[] = "console.log('fb_devel.module extreme verbosity: original session_id is ' + Drupal.settings.fb_devel.session_name + ':' + Drupal.settings.fb_devel.session_id + ', session_id during FB_OP_AJAX_EVENT is $session_name:$session_id');";
311 $fbu = fb_facebook_user($data['fb']);
312 $return[] = "console.log('fb_facebook_user() during FB_OP_AJAX_EVENT is $fbu, data[event_data][fbu] is {$data[event_data][fbu]}');";
313 $return[] = "console.log('_COOKIE[fbu_{$fb_app->apikey}] is " . $_COOKIE['fbu_' . $fb_app->apikey] . "');";
314 $return[] = "FB_JS.reload();";
315 }
316 }
317 }
318
319
320 /**
321 * Implements hook_footer().
322 */
323 function fb_devel_footerXXX($is_front) {
324 $output = "<!-- fb_devel_footer() -->\n";
325 $output .= "<script type=\"text/javascript\">\n";
326 $output .= "<!--//--><![CDATA[//><!--\n";
327 $output .= " jQuery(document).bind('fb_init', FB_Devel.initHandler);\n";
328 //$output .= " FB_Devel.sanityCheck()\n";
329 $output .= "\n//--><!]]>\n";
330 $output .= "\n</script>\n";
331 return $output;
332 }
333
334
335
336 /**
337 * Provides a page with useful debug info.
338 *
339 * @TODO - clean this up and rely less on drupal_set_message() and dpm().
340 */
341 function fb_devel_page() {
342 global $_fb, $_fb_app;
343 global $user;
344
345 if (isset($_REQUEST['require_login']) && $_REQUEST['require_login'])
346 fb_require_authorization($_fb);
347
348 $items = array();
349
350 $items['fb_settings'] = fb_settings();
351
352 if ($_fb) {
353
354 $items['$GLOBALS[_fb]'] = $_fb;
355
356 // TODO: determine whether connect page or canvas.
357
358 drupal_set_message(t("session name: " . session_name()));
359 drupal_set_message(t("session id: " . session_id()));
360 drupal_set_message(t("cookie domain: " . fb_settings(FB_SETTINGS_COOKIE_DOMAIN)));
361
362 if (isset($_COOKIE['fbs_' . $_fb_app->apikey]))
363 drupal_set_message(t("fbs_" . $_fb_app->apikey . ": " . $_COOKIE["fbs_" . $_fb_app->apikey]));
364
365 drupal_set_message(t("<a href=\"!url\">processed link</a>, <a href=!url>unprocessed</a>", array('!url' => url('fb/devel'))));
366 drupal_set_message(t("getUser() returns " . $_fb->getUser()));
367 drupal_set_message(t("getAccessToken() returns " . $_fb->getAccessToken()));
368
369 drupal_set_message(t("base_url: " . $GLOBALS['base_url']));
370 drupal_set_message(t("base_path: " . $GLOBALS['base_path']));
371 drupal_set_message(t("url() returns: " . url()));
372 }
373
374 if ($fbu = fb_get_fbu($user)) {
375 $path = "fb/devel/fbu/$fbu";
376 drupal_set_message(t("Learn more about the current user at !link",
377 array('!link' => l($path, $path))));
378 }
379
380 dpm(fb_get_fbu($user), 'Facebook user via fb_get_fbu');
381 //dpm($user, "Local user " . theme('username', $user));
382
383 if (isset($GLOBALS['fb_connect_apikey'])) {
384 drupal_set_message(t("fb_connect_apikey = " . $GLOBALS['fb_connect_apikey']));
385 }
386
387 dpm(fb_settings(), 'fb_settings()');
388 dpm($_COOKIE, 'cookie');
389 dpm($_REQUEST, "Request");
390 //dpm($_fb_app, "fb_app");
391 dpm($_SESSION, "session:");
392
393 foreach ($items as $key => $val) {
394 if (is_array($val) || is_object($val)) {
395 $markup = print_r($val, 1);
396 }
397 else {
398 $markup = $val;
399 }
400 $out[$key] = array(
401 '#prefix' => "<dl><dt>$key</dt><dd><pre>",
402 '#suffix' => "</pre></dd></dl>",
403 '#markup' => $markup,
404 );
405 }
406
407 return render($out);
408 }
409
410
411 /**
412 * Provides a profile tab (FBML) with useful debug info.
413 *
414 */
415 function fb_devel_tab() {
416 global $_fb, $_fb_app;
417 global $user;
418
419 $info['session_id'] = session_id();
420 $info['session_name'] = session_name();
421 $info['cookie domain'] = fb_settings(FB_SETTINGS_COOKIE_DOMAIN);
422
423 // Tests for links
424 $link_test = url(current_path(), array('absolute' => TRUE));
425 $info['link test'] = "<a href=\"$link_test\">link test (processed)</a>";
426 $info['link test 2'] = "<a href='$link_test'>link test (not processed)</a>";
427
428 //$info['fb_app'] = $_fb_app;
429 //$info['fb'] = $_fb;
430 $info['fb_settings'] = fb_settings();
431 $info['REQUEST'] = $_REQUEST;
432 $info['SESSION'] = $_SESSION;
433 $info['COOKIE'] = $_COOKIE;
434
435 if (isset($_fb)) {
436 $info['fb->getSignedRequest()'] = $_fb->getSignedRequest();
437 $fbu = fb_facebook_user();
438 try {
439 $info["fb->api(/$fbu)"] = $_fb->api('/' . $fbu);
440 }
441 catch (Exception $e) {
442 $info["fb->api(/$fbu)"] = $e->getMessage();
443 }
444
445 if ($app_id = $_REQUEST['fb_sig_app_id']) {
446 try {
447 $info['fb->api(fb_sig_app_id)'] = $_fb->api($_REQUEST['fb_sig_app_id']);
448 }
449 catch (Exception $e) {
450 $info['fb->api(fb_sig_app_id)'] = $e->getMessage();
451 }
452 }
453 }
454
455 print '<p>fb_devel.module tab</p>';
456 foreach ($info as $key => $value) {
457 print "<p>$key:\n";
458 if (is_array($value)) {
459 print '<pre>' . check_plain(print_r($value, 1)) . '</pre>';
460 }
461 elseif (is_object($value)) {
462 print '<pre>' . check_plain(print_r($value, 1)) . '</pre>';
463 }
464 else {
465 print '<pre>' . $value . '</pre>';
466 }
467 print "\n</p>\n\n";
468 }
469
470 exit();
471 }
472
473 /**
474 * A page which tests function which work with facebook user ids
475 */
476 function fb_devel_fbu_page($fbu = NULL) {
477 global $_fb, $_fb_app;
478 if ($fbu) {
479 // Uses FQL
480 $info = fb_users_getInfo(array($fbu), $_fb);
481 $output = "<p>Debug FQL info about facebook id $fbu ({$info[0]['name']}):</p>\n";
482 $output .= "<img src=http://graph.facebook.com/$fbu/picture></img>";
483 $output .= "<pre>" . print_r($info[0], 1) . "</pre>";
484
485 // Use new graph api
486 $info = $_fb->api($fbu, array('metadata' => 1));
487 $output .= "<p>Debug info about facebook id $fbu ({$info['name']}):</p>\n";
488 $output .= "<img src=http://graph.facebook.com/$fbu/picture></img>";
489 $output .= "<pre>" . print_r($info, 1) . "</pre>";
490
491 foreach (array('statuses') as $connection) {
492 try {
493 if ($data = $_fb->api($fbu . '/' . $connection)) {
494 $output .= "<p>$connection<pre>";
495 $output .= print_r($data, 1);
496 $output .= "</pre></p>\n\n";
497 }
498 }
499 catch (FacebookApiException $e) {
500 fb_log_exception($e, t('Failed to lookup %connection', array('%connection' => $fbu . '/' . $connection)));
501 }
502 }
503 try {
504 $friends = $_fb->api($fbu . '/friends');
505 //dpm($friends, "$fbu/friends returned");
506 $items = array();
507 foreach ($friends['data'] as $data) {
508 $items[] = l($data['name'], "fb/devel/fbu/{$data['id']}");
509 }
510 if (count($items)) {
511 $output .= "\n<p>Known friends:<ul><li>";
512 $output .= implode("</li>\n <li>", $items);
513 $output .= "</li></ul></p>\n\n";
514 }
515 }
516 catch (FacebookApiException $e) {
517 fb_log_exception($e, t('Failed to lookup friends of facebook user %fbu', array('%fbu' => $fbu)));
518 }
519
520 if (module_exists('fb_user')) {
521 $local_friends = fb_user_get_local_friends($fbu);
522
523 $items = array();
524 foreach ($local_friends as $uid) {
525 $account = user_load($uid);
526 $items[] = theme('username', $account);
527 }
528 if (count($items)) {
529 $output .= "\n<p>Local friends:<ul><li>";
530 $output .= implode("</li>\n <li>", $items);
531 $output .= "</li></ul></p>\n\n";
532 }
533 }
534 }
535 else
536 drupal_set_message(t("You have to specify a facebook user id."), 'error');
537 return $output;
538 }
539
540
541 /**
542 * Provide some information for testing AHAH and AJAX scenarios.
543 */
544 function fb_devel_js() {
545 $data = '<pre>';
546
547 $data .= "session_name() = " . session_name() . "\n";
548 $data .= "session_id() = " . session_id() . "\n";
549 $data .= "REQUEST: " . dprint_r($_REQUEST, 1) . "\n";
550 $data .= "SESSION: " . dprint_r($_SESSION, 1) . "\n";
551 $data .= '</pre>';
552
553 print drupal_json_output(array('status' => TRUE, 'data' => $data));
554 exit();
555 }
556
557 function fb_devel_block_info() {
558 // Provide two copies of same block, for iframe scenarios.
559 $items[0]['info'] = t('Facebook Devel Page Info');
560 $items[1]['info'] = t('Facebook Devel Page Info 2');
561 return $items;
562 }
563
564 function fb_devel_block_view($delta = '') {
565 if (user_access('access devel information')) {
566 return array(
567 'subject' => t('Facebook Devel Info'),
568 'content' => drupal_get_form('fb_devel_info'),
569 );
570 }
571 }
572
573 function fb_devel_info() {
574 global $_fb, $_fb_app;
575 global $user;
576 global $base_url;
577 global $base_path;
578
579 $info = array();
580
581 if ($_fb) {
582 if (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CANVAS) {
583 $info['Page Status'] = t('Serving canvas page.');
584 }
585 elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_CONNECT) {
586 $info['Page Status'] = t('Connected via Facebook Connect');
587 }
588 elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PROFILE) {
589 $info['Page Status'] = t('Serving deprecated FBML profile tab');
590 }
591 elseif (fb_settings(FB_SETTINGS_TYPE) == FB_SETTINGS_TYPE_PAGE_TAB) {
592 $info['Page Status'] = t('Iframe tab on page %page', array(
593 '%page' => fb_settings(FB_SETTINGS_PAGE_ID),
594 ));
595 }
596 elseif (!fb_settings(FB_SETTINGS_TYPE)) {
597 $info['Page Status'] = t('Facebook PHP SDK initialized.'); // Either a facebook connect page, or canvas page where user has not authorized app.
598 }
599 else {
600 $info['Page Status'] = t('Global fb instance is set, but page type unknown (%type).',
601 array('%type' => fb_settings(FB_SETTINGS_TYPE),
602 ));
603 }
604 $fbu = fb_facebook_user();
605 $info['fb_facebook_user'] = $fbu;
606
607 if (!$fbu) {
608 $info['login button'] = theme('fb_login_button', array('text' => 'Connect'));
609 $info['login link'] = "<a href=# onclick='FB.login(function(response) {alert(\"FB.login() success.\");}, {perms:Drupal.settings.fb.perms}); return false;'>fb login</a>";
610 }
611
612 // Tests for link on canvas pages (iframe cookie test)
613 //$link_test = url(fb_scrub_urls($_REQUEST['q']), array('absolute' => TRUE));
614 //$info['link test'] = "<a href=\"$link_test\">link test (processed)</a>";
615 //$info['link test 2'] = "<a href='$link_test'>link test (not processed)</a>";
616
617 if ($fbu && FALSE) { // Disabled as this expensive check can cause problems.
618 if (!fb_api_check_session($_fb)) {
619 $info['fb_api_check_session()'] = t('Returned FALSE');
620 }
621 else {
622 $info['fb_api_check_session()'] = t('Returned TRUE');
623 }
624 }
625 }
626 else {
627 $info['Page Status'] = t('Not a canvas page.');
628 }
629 // Use theme_username() rather than theme('username') because we want link to local user, even when FBML is enabled
630 if ($user->uid) {
631 $name = $user->name;
632 }
633 else {
634 $name = "anonymous";
635 }
636 $info['local user'] = theme_username(array('account' => $user,
637 'attributes_array' => array(),
638 'name' => $name,
639 'extra' => ''));
640 $info['fb_get_fbu'] = fb_get_fbu($user->uid);
641 $info['base_url'] = $base_url;
642 $info['base_path'] = $base_path;
643 $info['url() returns'] = url();
644 $info['current_path'] = current_path();
645 $info['arg(0) is'] = arg(0);
646 $info['arg(1) is'] = arg(1);
647 $info['session_id'] = session_id();
648 $info['session_name'] = session_name();
649 $info['fb_settings()'] = fb_settings();
650 $info['request'] = $_REQUEST;
651 $info['user'] = $GLOBALS['user'];
652 $info['fb_app'] = $_fb_app;
653 $info['session'] = $_SESSION;
654 $info['cookies'] = $_COOKIE;
655
656
657 if ($_fb) {
658 $info['signed_request'] = $_fb->getSignedRequest();
659 try {
660 // app properties
661 $token = fb_get_token($_fb);
662 $props = $_fb->api(array(
663 'method' => 'admin.getAppProperties',
664 'properties' => 'about_url', 'application_name',
665 'access_token' => $token,
666 ));
667 $info['application properties'] = $props;
668
669 }
670 catch (FacebookApiException $e) {
671 fb_log_exception($e, "failed to get app properties");
672 }
673 // $info["fb->getSession()"] = $_fb->getSession();
674 $info["fb->getSignedRequest()"] = $_fb->getSignedRequest();
675 if ($fbu) {
676 try {
677 $info["fb->api($fbu)"] = $_fb->api($fbu, array(
678 'access_token' => fb_get_token($_fb),
679 ));
680 }
681 catch (FacebookApiException $e) {
682 if (fb_verbose() == 'extreme') {
683 // After oauth upgrade, this exception is thrown always, when using app token.
684 // So only show error when fb_verbose is extreme. Hopefully facebook will fix the issue.
685 fb_log_exception($e, "failed to look up _fb->api($fbu)."); // Dont show token, it can inlude app secret. using token " . fb_get_token($_fb));
686 }
687 }
688 }
689 }
690
691 $form = array();
692 foreach ($info as $key => $val) {
693 if (is_string($val) || is_numeric($val) || !$val) {
694 $form[$key] = array('#markup' => t($key) . ' = ' . $val,
695 '#weight' => count($form),
696 '#suffix' => '<br/>',
697 );
698
699 }
700 else {
701 $form[] = array(
702 '#type' => 'fieldset',
703 '#title' => t($key),
704 '#collapsible' => TRUE,
705 '#collapsed' => TRUE,
706 '#weight' => count($form),
707 'value' => array(
708 '#prefix' => '',
709 '#suffix' => '',
710 '#type' => 'markup',
711 '#markup' => dprint_r($val, 1)),
712 );
713 }
714 }
715 return $form;
716 }
717
718 /**
719 * Implements hook_user_view().
720 */
721 function fb_devel_user_view($account, $view_mode, $langcod) {
722 if (user_access('administer users') && user_access('access devel information')) {
723 $account->content['fb_devel'] = array(
724 '#type' => 'fieldset',
725 '#title' => t('Drupal for Facebook Devel'),
726 '#description' => t('Information from facebook API, visible only to administrators.'),
727 '#collapsible' => TRUE,
728 '#collapsed' => TRUE,
729 '#weight' => 99,
730 );
731
732 foreach (fb_get_all_apps() as $fb_app) {
733 $account->content['fb_devel'][$fb_app->label] = array(
734 '#type' => 'fieldset',
735 '#title' => $fb_app->title,
736 );
737 if ($fbu = fb_get_fbu($account, $fb_app)) {
738 $fb = fb_api_init($fb_app);
739 try {
740 // Don't use fb_api() because we don't want the caching here.
741 $info = $fb->api("/$fbu", array('access_token' => fb_get_token($fb)));
742
743 //$info = fb_users_getInfo(array($fbu), $fb, TRUE);
744 $account->content['fb_devel'][$fb_app->label]['info'] = array(
745 '#type' => 'markup',
746 '#markup' => "facebook graph for /$fbu:" . dprint_r($info, 1),
747 );
748
749 if ($token = fb_get_token($fb, $fbu)) {
750 $account->content['fb_devel'][$fb_app->label]['token'] = array(
751 '#type' => 'markup',
752 '#markup' => 'access_token: ' . fb_get_token($fb, $fbu),
753 '#prefix' => '<p>', '#suffix' => '</p>',
754 );
755
756 $account->content['fb_devel'][$fb_app->label]['graph'] = array(
757 '#type' => 'markup',
758 '#markup' => l(t('Browse graph as this user'), 'https://graph.facebook.com/' . $fbu . '?metadata=1&access_token=' . $token),
759 '#prefix' => '<p>', '#suffix' => '</p>',
760 );
761 }
762 }
763 catch (Exception $e) {
764 $account->content['fb_devel'][$fb_app->label]['#description'] = $e->getMessage();
765 $account->content['fb_devel'][$fb_app->label]['info'] = array(
766 '#type' => 'fieldset',
767 '#title' => t('Failed to get info for !app.',
768 array('!app' => $fb_app->title)),
769 '#collapsible' => TRUE,
770 '#collapsed' => TRUE,
771 'exception' => array(
772 '#type' => 'markup',
773 '#markup' => dprint_r($e, 1),
774 ),
775 );
776 }
777 }
778 else {
779 $account->content['fb_devel'][$fb_app->label]['info'] = array(
780 '#type' => 'markup',
781 '#markup' => t('No mapping to a facebook account.'),
782 );
783 }
784 }
785 }
786 }
787
788 /**
789 * Implements hook_cron().
790 *
791 * Remove obsolete data from {users} table. Not a serious problem,
792 * just cruft in the database which should never have been saved.
793 * Clean it up.
794 */
795 function fb_devel_cron() {
796 $limit = 10;
797 $result = db_query_range('SELECT * FROM {users} WHERE data LIKE :as OR data like :iau OR data like :fbu',
798 0, $limit, array(':as' => '%\"app_specific\"%',
799 ':iau' => '%\"is_app_user\"%',
800 ':fbu' => '%\"fbu\"%'));
801
802 foreach ($result as $account) {
803 $data = unserialize($account->data);
804 // Clean out the bogus data.
805 foreach (array('app_specific', 'username', 'fbu', 'info') as $key) {
806 unset($data[$key]);
807 }
808 db_update('users')
809 ->fields(array('data' => serialize($data), 'uid' => $account->uid))
810 ->execute;
811
812 /*
813 db_query("UPDATE {users} SET data=:data WHERE uid=:uid",
814 serialize($data), $account->uid);
815 */
816 if (fb_verbose()) {
817 print(t('Cleaned up data for user %name (%uid), it is now: !data',
818 array('%name' => $account->name,
819 '%uid' => $account->uid,
820 '!data' => '<pre>' . print_r($data, 1) . '</pre>',
821 )));
822 }
823 }
824 }
825
826
827 function fb_devel_fb_tab($op, $data, &$return) {
828 // debug.
829 if (fb_verbose() === 'extreme') {
830 $return['fb_devel'] = array(
831 '#type' => 'markup',
832 '#markup' => __FUNCTION__ . ':' . print_r($data, 1),
833 '#prefix' => '<pre>',
834 '#suffix' => '</pre>',
835 '#weight' => 99,
836 );
837 if ($data['profile_id'] && isset($data['fb'])) {
838 $return['fb_devel']['profile_id'] = array(
839 '#type' => 'markup',
840 '#markup' => __FUNCTION__ . ':' . print_r($data['fb']->api($data['profile_id']), 1),
841 '#prefix' => '<pre>',
842 '#suffix' => '</pre>',
843 '#weight' => 99,
844 );
845 }
846 }
847 }
848
849
850 /**
851 * Wrapper for fb_fql_query(). Useful for debugging.
852 */
853 function fb_devel_fql_query($fb, $query, $params = array()) {
854 if (!is_object($fb)) {
855 dpm(debug_backtrace(), __FUNCTION__);
856 return;
857 }
858 $url_params = $params;
859 $url_params['query'] = $query;
860 $url_params['format'] = 'JSON';
861 $url = url("https://api.facebook.com/method/fql.query", array('query' => $url_params));
862 dpm(func_get_args(), __FUNCTION__);
863 drupal_set_message(l($url, $url));
864 return fb_fql_query($fb, $query, $params);
865 }