Monday, 2 December 2013

Facebook Page Posts from PHP, the real deal

So I had this need to post to a page (timeline) from a server-side app.
You know, to let the world know something happened somewhere, without user intervention.
It had to post as the page, not as an admin user.

One thing I learnt from this experience is that the Facebook API is really terribly documented with very little actual, usable examples.  The bits of information available on the internet is also quite sparse, outdated (deprecated methods) or irrelevant (Javascript SDK, user dialog etc).

The steps that finally worked for me (not all steps are steps, some are just notes or FYI):

1. If you don't already have a page, you can create one (https://www.facebook.com/pages/create.php)

2. If you already have a page and it has a trailing number, that is your {page id}, e.g. for https://www.facebook.com/pages/yourpagename/123133537809857 the {page id} is 123133537809857

3. Page IDs are unique throughout the Facebook system

4. If you have a page and it looks pretty without a trailing number, one way to get it is to follows the steps from here: http://inlinevision.com/apps/how-to-find-your-facebook-page-id/   To summarize, just right-click and open your page profile picture in a new tab/window.  The URL will look like this:
https://www.facebook.com/photo.php?fbid=xxxxxxxxxxxxxxx&set=a.123456789012345.1234567890.123456789009876&type=1&source=11
The portion in bold is your {page id}

5. Create an app: https://www.facebook.com/developers/createapp.php
If you are asked to sign up as a developer, do so - you have to.
If you don't wish to get stuck like I did, follow the next instructions as closely as possible, verbatim wherever you can.
Give your app a nice display name and a namespace (this ends up as the app URL, i.e .https://apps.facebook.com/{namespace} )
Tick the checkbox to host with heroku.  Go through the sign up, receive an email, set up a password and you're all set with a cloud backend which you never need to look at again (unless you want to).
Save the app settings (button near bottom)

6. Go back to https://developers.facebook.com/apps/. You want to take note of the App ID and App Secret.  Then, click on "Edit Settings".

7. In the "Select how your app integrates with Facebook" section, click once on "App on Facebook" to create a Canvas app (iFrame of external site displaying within FB).  Copy the hosting url (heroku if you followed the step above) and paste it into both Canvas URL and Secure Canvas URL.  Change the Canvas URL from "https://" to "http://".
Save Changes.

8. Browse to this URL from your browser (don't include curly braces):
https://www.facebook.com/dialog/oauth?client_id={your App ID}&redirect_uri={canvas url}&scope=manage_pages,offline_access,publish_stream
You will get three prompts - matching the three permissions you have asked for.  Click OK/Yes to all three.
You have been redirected to:
{canvas url}?code={access token}
Note: The access token is just a very long block of text.
If you botched it, don't worry, you can return to step 8 which will give you a new token, even if you don't have to give permissions anymore.

9. Make up another URL and browse to it:
https://graph.facebook.com/oauth/access_token?client_id={APP ID}&redirect_uri={canvas url}&client_secret={APP SECRET}&code={access token}
Note: {access token} is what you get from the redirected url from the last step
If all goes to plan, you will get a response that looks like this:
access_token={new token}&expires=5183370
Yes, {new token} is also very long.

2013-12-02 Update: Step 10 doesn't seem to work any more. It shows the access, however the token retrieved from step 9 **is** the page access token - just use it.  The response for step 9 also seems to have lost the "expires" parameter.

10. You get the theme, we're big on making up URLs, so here's another one:
https://graph.facebook.com/{your name or id}/accounts/?access_token={new token}
The easiest way to get your facebook "unique name" is to click on your name on the top toolbar. You'll be looking at your "home" page, which looks something like
https://www.facebook.com/{someone.something}
Note: there may be some trailing cruft after the name, e.g.
https://www.facebook.com/SpongeBob?ref=tn_tnmn
You can ignore it. "SpongeBob" is the name we are after for that URL.

11. Browsing to the URL in step 10, you will see one or more blocks of

         "category": ".....",
         "name": "{name of a page you administer}",
         "access_token": "{page access token}",
         "id": "{page id}",
         "perms": [
            "ADMINISTER",
            "EDIT_PROFILE",
            "CREATE_CONTENT",
            "MODERATE_CONTENT",
            "CREATE_ADS",
            "BASIC_ADMIN"
         ]

The most important bits from this are the {page id}, {page access token} and the fact that it has the "CREATE_CONTENT" permission.

(hah - now you know steps 2-4 are in vain. oh well)

You can actually see what Facebook stores in the {page access token} by pasting it in this page:
https://developers.facebook.com/tools/debug/access_token
What you want to verify is that it states "Expires: Never".  Pretty.

12. You now have all the information you need to post as the app to the page, as the page.  Here are some examples depending on what tool you use.

curl command line:
curl -F access_token="{page access token}" \-F message="hello world" \https://graph.facebook.com/{page id}/feed
PHP (error checking excepted):
$ch = curl_init("https://graph.facebook.com/$page_id/feed");curl_setopt($ch, CURLOPT_POST, 1);curl_setopt($ch, CURLOPT_POSTFIELDS, array(        'message' => $message,        'access_token' => $page_access_token));curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);curl_exec($ch);curl_close($ch);

In retrospect, I just didn't follow this post (http://www.apreche.net/tutorial-programatically-post-a-status-update-to-your-facebook-page/) correctly, but there you go. Now you have two interpretations to bank on.

Richard, over and out.

Wednesday, 18 September 2013

Obscure ASP.Net MVC4 WebAPI XML POST parameter

So I had this problem with getting simple XML to be recognized by a Web API controller.

The type that has no namespaces, the type that people cook up in a couple of minutes in Javascript then try to post to a RESTful API.

I started with switching to the XmlSerializer... http://stackoverflow.com/a/11804373/573261. Result is as follows:
System.InvalidOperationException: No MediaTypeFormatter is available to read an object of type 'Job' from content with media type 'application/xml'
I also tried changing the XML (body content) on the fly... http://stackoverflow.com/q/14365953/573261

In between those two attempts were others that you would rather not want to know about, since they obviously led nowhere.

In the end, it turns out that the XML Serializer cannot handle interfaced properties.

        public IList<Item> Items { get; set; }    // fails
        public List<Item> Items { get; set; }    // works

Now you know.