Facebook.php 7.8 KB
<?php
/**
 * http://wiki.gotenzing.com/groups/tenzing/wiki/aa35a/FB_Connect_in_WP.html
 *
 * This needs to go in the <html tag
 * xmlns:fb="http://www.facebook.com/2008/fbml"
 * If you want to use FBML AND pass W3C validation
 *
 * Graph API Reference:
 * http://developers.facebook.com/docs/reference/api/user
 */

namespace Tz\WordPress\Tools\Auth\Facebook;

use Tz\WordPress\Tools;
use Tz\WordPress\Tools\Auth;

use FB;
use WP_User;

use Exception, InvalidArgumentException;

const VERSION = 0.2;
const COOKIE_LOGOUT = 'wpfb_logout';
const COOKIE_DENY   = 'wpfb_stay_logged_out';
const OPTION_NAME   = 'tz_auth_fb';

    call_user_func(function() {
        Vars::$options = new Tools\WP_Option(OPTION_NAME, Array('button_title' => 'Login', 'ext_perms' => Array('email' => 1)));

        Tools\add_actions(__NAMESPACE__ . '\Actions');
        Tools\add_filters(__NAMESPACE__ . '\Filters');
        Tools\add_shortcodes(__NAMESPACE__ . '\ShortCodes');

        if (is_admin()) {
            require_once(__DIR__ . DIRECTORY_SEPARATOR . 'Settings.php');
        }
    });

/**
 * Generates markup for a Facebook Login button
 * @param {Boolean} $echo TRUE to echo the button, false to return a string
 * @returns NULL|String
 */
function drawLoginButton($echo = true) {
    $title = Vars::$options['button_title'] ?: 'Login';
    $btn = '<a id="TzFB" class="fb_button fb_button_medium"><span class="fb_button_text">' . $title . '</span></a>';
    // $btn = '<fb:login-button></fb:login-button>';

    if (!$echo) {
        return $btn;
    }

    echo $btn;
}

// This might need some work...what happens if the object fails?
function getSDK() {
    static $instance = false;
    if (false === $instance) {
        require_once(__DIR__ . DIRECTORY_SEPARATOR . 'facebook-sdk.php');
        $instance = new FB\Facebook(Array(
            'appId'  => Vars::$options['application_id']
          , 'secret' => Vars::$options['application_secret']
          , 'cookie' => true
          , 'domain' => Vars::$options['domain_name']
        ));
    }

    return $instance;
}

/**
 * Like WordPress' get_user_by() function but for FB
 * @global $wpdb
 */
function get_user_by_fbuid($fbuid) {
    global $wpdb;

    $fbuid = mysql_real_escape_string($fbuid);
    if (null === ($user  = $wpdb->get_row("SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = 'fbuid' AND meta_value = '{$fbuid}'"))) {
        return false;
    }

    return new WP_User($user->user_id);
}

function load() {
?>
<div id="fb-root"></div>
<script>
  window.fbAsyncInit = function() {
    FB.init({appId: '<?php echo Vars::$options['application_id']; ?>', status: true, cookie: true, xfbml: true});
    FB.Event.subscribe('auth.login', function(response) { window.location.reload(); });
  };

  (function() {
    var e   = document.createElement('script');
    e.async = true;
    e.src   = document.location.protocol + '//connect.facebook.net/en_US/all.js';
    document.getElementById('fb-root').appendChild(e);
  }());
</script>
<?php
}

class Actions {
    public static function send_headers() {
        /*
        // This SHOULD work, but FB is being stupid and not passing back, so I have to use 2 cookies instead
        if (isset($_GET['nofb'])) {
            return;
        }
        */

        $sdk = getSDK();

        // User is not logged in to Facebook
        if (null === ($sess = $sdk->getSession())) {
            // In case FB enabled, but not used by this user, remove useless cookies
            setcookie(COOKIE_LOGOUT, '', time() - 3600, '/');
            setcookie(COOKIE_DENY, '', time() - 3600, '/');

            return;
        }

        // Becaues FB redirect is dumb
        if (!isset($_COOKIE[COOKIE_LOGOUT]) && isset($_COOKIE[COOKIE_DENY])) {
            setcookie(COOKIE_DENY, '', time() - 3600, '/');
            return;
        }

        // User logged out of WordPress, log them out of Facebook
        if (isset($_COOKIE[COOKIE_LOGOUT])) {
            $url = $sdk->getLogoutUrl(Array('nofb' => 1));

            setcookie(COOKIE_LOGOUT, '', time() - 3600, '/');
            $sdk->setSession();

            header('Location: ' . $url);

            die;
        }

        $fb_user = get_user_by_fbuid($sess['uid']);

        if (is_user_logged_in()) {
            global $current_user;
            get_currentuserinfo();

            // User logged in with a native WP account then logged in with FB, merge
            if (false === $fb_user) {
                update_user_meta($current_user->ID, 'fbuid', $sess['uid']);
                return;
            }

            // User has already logged into WP with his FB acct
            if ($fb_user->ID == $current_user->ID) {
                return;
            }

            // FB user exists, but the logged in user has different fbuid?
            // user created 2 accounts?
        }

        // Welcome back!
        if (false !== $fb_user) {
            $user = Auth\signin($fb_user->user_login);
        } else {
            try {
                $info = $sdk->api('/me');
            } catch (FB\FacebookApiException $e) {
                // Load up an error thingie
                return;
            }

            require_once(ABSPATH . WPINC . DIRECTORY_SEPARATOR . 'registration.php');
            $username = 'fbc' . $sess['uid'];
            // User logged in via Facebook for the first time, register/activate a linked WordPress account

            // Email address is already registered...
            if (false !== get_user_by('email', $info['email'])) {
                // Not sure if I can throw exception, this is outside the theme stuff...
                throw new Exception('email conflict');
            }

            try {
                $key  = Auth\register($username, $info['email'], _generate_password());
                $id   = Auth\activate($key);
                $user = Auth\signin($username);

                _update_user(Array(
                    'ID'            => $user->ID
                  , 'user_nicename' => $info['name']
                  , 'first_name'    => $info['first_name']
                  , 'last_name'     => $info['last_name']
                  , 'nickname'      => $info['name']
                  , 'display_name'  => $info['name']
                  , 'user_url'      => ($info['user_website'] ?: '')
                  , 'facebook'      => $info['link']
                ));

                update_user_meta($user->ID, 'fbuid', $info['id']);
            } catch (Exception $e) {
                // many types of exceptions
            }
        }
    }

    public static function wp_enqueue_scripts() {
        _enqueue_script('tz-facebook', Tools\url('tz-facebook.js', __FILE__), Array('addEvent'));
        _localize_script('tz-facebook', 'TzFBData', Array('ext_perms' => implode(',', array_keys(Vars::$options['ext_perms']))));
    }

    /**
     * Set a cookie to tell this to logout of Facebook on next pass
     */
    public static function wp_logout() {
        remove_action('send_headers', Array(__CLASS__, 'send_headers'));
        setcookie(COOKIE_LOGOUT, 1, time() + 3600, '/');
        setcookie(COOKIE_DENY, 1, time() + 3600, '/');
    }
}

class Filters {
    public static function user_contactmethods($methods) {
        $methods['facebook'] = 'Facebook';
        return $methods;
    }
}

class ShortCodes {
    public static function fb_login_button() {
        $sdk = getSDK();
        if ($sdk->getSession()) {
            ob_start();
            print_r($sdk->getSession());
            try {
                print_r($sdk->api('/me'));
            } catch (Exception $e) {
                print_r($e);
            }
            $data = '<pre>' . ob_get_contents() . '</pre>';
            ob_end_clean();

            return $data;
        } else {
            return drawLoginButton(false);
        }
    }
}

class Vars {
    /**
     * WordPress option for this module
     * @type WP_Option
     */
    public static $options;
}
?>