Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

I had a really hard time implementing Facebook with Wicket, and mostly that was because the examples were out dated. This example is for using Facebook connect with wicket and the facebook-java-api found here: http://code.google.com/p/facebook-java-api/ The docs were not much help, so I feel obligated to put what I did to get things working.

For starters, I added the dependency to maven:

Code Block
titlepom.xml
borderStylesolid
<dependency>
    <groupId>com.google.code.facebookapi</groupId>
    <artifactId>facebook-java-api-schema</artifactId>
    <version>2.1.1</version>
</dependency>

We use a Panel to display login information.

Code Block
titleSimplePanel.html
borderStylesolid
<html xmlns:wicket>
<body>
  <wicket:panel>
    <!-- facebook api as of 8/11/09 -->
  	<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php" type="text/javascript"></script>
    
    <!-- function for facebook to execute on login-->
  	<script type="text/javascript" wicket:id="fbcallback">
    function callWicket() { 
     var wcall = wicketAjaxGet('$url$' + '$args$', function() { }, function() { }); 
    }
    </script>
	
	<div id="loginform">
		<div wicket:id="fbloginDiv" style="display:none;">
		    <span wicket:id="fblogin">
            
            <!-- facebool login button -->
            <fb:login-button onlogin='callWicket();'></fb:login-button>
            </span>
            
            <!-- facebook api -->
	        <script type="text/javascript">
	        FB.init("your-api-key", "/xd_receiver.htm");
	        </script>
	    </div>
	</div>

  </wicket:panel>
</body>
</html>

The panel caused me a slight issue when setting up the AbstractDefaultAjaxBehavior. I found that I had to add my Panel to my Page before I could get the callbackUrl from the behavior. Once I did that, everything was ok. Here is a simple panel example that illustrates one way to add the <fb:login to your page and handle a callback from Facebook.

Code Block
titleSimplePanel.java
borderStylesolid

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.context.SecurityContext;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.context.SecurityContextImpl;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;

import com.google.code.facebookapi.FacebookException;
import com.google.code.facebookapi.FacebookJsonRestClient;
import com.google.code.facebookapi.FacebookWebappHelper;
import com.google.code.facebookapi.ProfileField;
import com.pigspigot.model.User;

/**
 * An example Panel that shows one way to output a facebook login button using fbml
 * and getting a callback from Facebook. 
 * 
 * @author russellsimpkins@hotmail.com
 *
 */
public class SimplePanel extends Panel {
    private static final Log log = LogFactory.getLog(SimplePanel.class);
    private WebMarkupContainer fbloginDiv;
    private Label fblogin;
    
    public SimplePanel(String id) {
        super(id);
        // TODO Auto-generated constructor stub
    }
    
    /**
     * This method will the panel
     */
    public void createPanel() {
        fbloginDiv = new WebMarkupContainer("fbloginDiv");
        fbloginDiv.setOutputMarkupId(true).setMarkupId("fbloginDiv");
        fblogin = new Label("fblogin", "<fb:login-button onlogin='callWicket();'></fb:login-button>");
        fblogin.setEscapeModelStrings(false);
        fblogin.setOutputMarkupId(true);
        if (isAuthenticated()) {
            fbloginDiv.add(new SimpleAttributeModifier("style", "display:none;"));
        }
        fbloginDiv.add(fblogin);
        addOrReplace(fbloginDiv);
        /**
         * This will only be called after they're logged in via facebook
         */
        final AbstractDefaultAjaxBehavior behave = new AbstractDefaultAjaxBehavior() {
            protected void respond(final AjaxRequestTarget target) {
                // deal with facebook
                handleFacebookCallback(target.getPage());
                fbloginDiv.add(new SimpleAttributeModifier("style", "display:none;"));
                target.addComponent(fbloginDiv);
            }
        };
        add(behave);
        CharSequence url = behave.getCallbackUrl();
        StringBuffer sb = new StringBuffer();
        sb.append("function callWicket() { \n");
        sb.append("     var wcall = wicketAjaxGet('");
        sb.append(url);
        sb.append("', function() { }, function() { });");
        sb.append("    }");
        Label fbcallback = new Label("fbcallback", sb.toString());
        fbcallback.setOutputMarkupId(true);
        fbcallback.setEscapeModelStrings(false);
        add(fbcallback);
        
    }
    
    /**
     * All that we do to log you in from facebook. I put my fbook.key and fbook.secret in the 
     * properties file.
     * @param thePage
     */
    public void handleFacebookCallback(Page thePage) {
        
        HttpServletRequest req = ((ServletWebRequest) thePage.getRequest()).getHttpServletRequest();
        HttpServletResponse res = ((WebResponse) thePage.getResponse()).getHttpServletResponse();
        String api = getLocalizer().getString("fbook.key", this);
        String secret = getLocalizer().getString("fbook.secret", this);
        FacebookWebappHelper<Object> helper = FacebookWebappHelper.newInstanceJson(req, res, api, secret);
        
        // make sure the login worked
        if (helper.isLogin()) {
            FacebookJsonRestClient facebookClient = (FacebookJsonRestClient) helper.getFacebookRestClient();
            long id;
            try {
                // grab the logged in user's id
                id = facebookClient.users_getLoggedInUser();

                // you can bundle ajax calls...
                facebookClient.beginBatch();
                
                // i'm going to call the users.getInfo fb api call, just to make sure it works
                ArrayList<Long> ids = new ArrayList<Long>();
                ids.add(new Long(id));

                // put together a set of fields for fb to return
                HashSet<ProfileField> fields = new HashSet<ProfileField>();
                fields.add(ProfileField.FIRST_NAME);
                fields.add(ProfileField.LAST_NAME);

                // get the user data
                facebookClient.users_getInfo(ids, fields);
                
                // execute the batch (which also terminates batch mode until beginBatch is called again)
                List<? extends Object> batchResponse = facebookClient.executeBatch(false);
                JSONArray userInfo = (JSONArray) batchResponse.get(0);
                JSONObject user = userInfo.getJSONObject(0);

                // a pojo user object
                User theUser = new User();
                String username = user.getString("first_name");
                theUser.setUsername(username);
                
                // fb emails are proxy, my app needs some kind of holder
                theUser.setEmail("noreply@facebook.com");
                theUser.setUserId(new Integer(0));
                theUser.setFacebook(true);
                theUser.setFacebookId(id);
                
                // we use spring, so here we give basic access to the facebook user.
                List<GrantedAuthority> gaList = new ArrayList<GrantedAuthority>();
                gaList.add(new GrantedAuthorityImpl("STANDARD"));
                theUser.setAuthorities(gaList.toArray(new GrantedAuthority[] {}));
                GrantedAuthority[] ga = theUser.getAuthorities();
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(theUser, theUser, ga);
                SecurityContext context = new SecurityContextImpl();
                context.setAuthentication(authentication);
                SecurityContextHolder.setContext(context);
                
            } catch (FacebookException e) {
                log.error("facebook issues: " + e);
            } catch (JSONException e) {
                log.error("facebook json issues: " + e);
            }
        }
    }
    
    /**
     * Do your own kind of auth check
     * @return
     */
    public boolean isAuthenticated() {
        return SecurityContextHolder.getContext().getAuthentication() != null;
    }

}

The Panel is added to your Page like so:

Code Block
titleSimplePage.java
borderStylesolid

SimplePanel myPanel = new SimplePanel("your-wicket-id");
// make sure you add the panel first
add(myPanel);
// now you can create the panel contents
myPanel.createPanel();