Monday, August 16, 2010

Group photo sharing through yogile.com

Here's a nearly universal truth: if a picture of a kid exists, that kid's parent wants a copy. Really self-controlled parents might go ahead and delete pictures that are blurry or where their kid is clearly not the subject of the picture. Maybe. But they want to make that choice themselves, and that means that any time you get a group of kids together in this age of ubiquitous digital cameras, there's going to be about 250 pictures per half-hour that every parent is going to want a copy of, from every other parent.

If said parents don't want to trust that everyone else will remember to include them in the list when sending out pictures (which may or may not happen), the solution has historically been for the event-planner to tell everyone to e-mail all the pictures to them, at which point they will make copies of all the pictures and get them back out to all the attendees.

Here's my question for you: has that ever worked out for you? In my experience, the absolute best scenario is that most of the parents will get copies of their pictures to the planner. But then the planners get to figure out when to draw the line of, "everyone who's going to send pictures, has sent them". And not only is it very easy to keep procrastinating, "just in case more come in", they really have no motivation to get those picture dispersed back out. After all, said planners now have all the pictures, so now they're happy, and their attention quickly turns to something else. And if they're really ambitious and actually decide to really get it done, they'll always end up being shocked at how many kids are in the pictures, and therefore how many copies they'll have to make and re-disperse.

Then along came services like Facebook, Flickr, Snapfish, Photobucket, etc. These well-meaning services have the right idea, which is to allow everyone to contribute photos into a single album, then provide everyone access to the pictures when they want to check in on their own. But they all require you to have an account to contribute and/or to view the album. And that's really a pain, as you're asking all these frazzled, non-techie parents to try and maintain all these different accounts, many of which they'll only use once. What happens is that if they register, they only end up spreading their invariably re-used credentials across the digital world, then forgetting that they even have that account. (Of course, I know that you use a unique log-on and password for every site for which you have an account, right?) And then they wonder why they get all this spam in their e-mail for the next twelve years.

Well, this morning I heard about a service that really makes a lot of sense. Yogile.com allows you to create a photo album for an event and optionally password-protect it. You get to pick a unique web address and e-mail address for this event. Then when you ask people to send pictures, they can either e-mail them as attachments or upload them at that site. If it's not a protected album, they can also view the photos from the event at the address you picked. And the best part is, anyone can contribute. Nobody needs to register if all they want to do is contribute pictures and view public albums. And yogile also makes sure that any pictures added to your album are sent to you (the album creator), so you don't even have to remember to log on and check for new additions.

It looks like it's free for up to 100mb, and $24.95 per year if you want unlimited storage with an ad-free interface. Here's the summary of their service from their page:

•Lets multiple people contribute with ease.
•Add photos as e-mail attachments or uploads through the site.
•Customizable URL and e-mail address per album, to share with anyone who wants to add photos.
•No need for these users to register, keeping the process simple

So I haven't used it yet, but this is just the type of thing I've wished for in the past, so you can be sure that I will before too long. And I just wanted to share (and post it so I can remind myself of it when I do need it).

Labels: , ,

Wednesday, August 11, 2010

Using PlanningCenterOnline.com's API with PHP

It's been a bumpy road, but thanks to Scotty at Planning Center Online, I've finally got their API working through PHP. In my case, I've got it returning info to render a custom Facebook Application for the church's Page, listing all the songs we sang during the previous weekend's services, and giving various links using the song names (search iTunes, Amazon, Zune or Pandora, etc). So I wanted to document the steps here, both in case I have to go back later, and for anyone else trying to do something similar.

First, a few requirements.
1) You will need a web server running PHP5
2) I assume that you have an understanding of XML and HTML- writing an app is not something that should be for first foray into the world of online programming.
3) If you need a database (more on that in a minute), you need to know how to access it and configure it yourself.
4) Knowing PHP is going to help you (quite a bit, probably), but for the record, I am one of those who doesn't know PHP, so it's entirely possible to do this, if you have other programming experience. If this is you, let me give you two tips that turned on a light bulb for me: the command -> in PHP is somewhat akin to a dot in other languages like Javascript...$str->doThis() would be more or less the same as $str.doThis() in other languages; it takes the variable and applies a function to it. Also, a dot concatenates strings instead of a plus sign or ampersand in other languages.

The mile-high overview starts at http://www.planningcenteronline.com/home/api . As they note, they use OAuth 1.0a for authentication, so you need to e-mail them with details of your app to get a Consumer Key. The e-mail address is listed on that page, I'm not going to repeat it here 'cause I want you to go look at that page and find what info they want you to send them. After all, they may change the address or information they need in the future.

Now, as they say on that overview page, they've provided some sample PHP files- so pay those files a visit.

1) You need to download four files: OAuth.php, common.php, index.php and complete.php . OAuth.php and common.php are just include files, the only thing you need to do with these is to edit common.php to enter your app's key and secret (unchanging random strings provided to you when you e-mail them as I noted above), and to point $callback_url to complete.php.

Now I need to explain the functions of the other two files, because they aren't what you might initially expect. Complete.php is actually your app itself. Loading index.php on the other hand, actually starts the initial authorization process, and should only be loaded once per user. But at the end of that process, it will use $callback_url to send the user back to complete.php. So you might want to rename index.php to something like requestAuth.php and complete.php to the file name you actually want people to load, if you wanted to help keep it straight in your mind. That said, I'm still going to refer to them by their original names throughout this entry.

2) Now you need to upload OAuth.php and common.php to a directory on your web server where PHP will be looking for include files. On my server, this is under admin/core, but yours will probably be somewhere else. But don't assume that all four files can just be uploaded to a single directory and expect php to be able to find them.

3) Now it's time to figure out how you want your app to work. If you want it to just create a page that looks the same and has the same data no matter who loads it, you might want to create a user in your organization's PlanningCenterOnline account that has full viewing rights. That way your app has access to any info you want, but if something goes wrong, the account doesn't have the power to affect other users, change or delete data, etc. With this method, YOU will be loading index.php once, and logging in using that new account, and authorizing the app to use that account. The end users loading the page don't ever have to authorize anything.

Let me talk for a minute about apps that instead give each user different data based on their own account (showing them their calendar, listing songs only on weekends they're scheduled for, etc). For this app, you don't need to make a new user account, since each user will be authorizing the app to use their own account. You're going to need to store users and their authorization keys in a list or some kind of database. I still think this article will be valuable to you so that you have an idea of where everything goes and what it does, but I'm not going to go into specifics on how to create this type of app or working with the database you'll need. But here's a high-level overview of what you're going to need to do for this type of dynamic user-specific app:

-Your users will probably be coming in and hitting your complete.php page first, since once it's authorized that's where they'll need to be going. You need to have complete.php check your database or list to see if they have authorized the app before, and if not, redirect them to index.php.
- Once they authorize through index.php, you need to store their credentials so they don't have to authorize again. You can either do this at the bottom of index.php before they return to complete.php, or you can point $callback_url in common.php to a new php file that does nothing but store their creds, then redirect them to complete.php itself. That way you'd keep complete.php simpler- a simple, "if they're not in the database, redirect to index.php" condition.
- Everything else will be mostly the same as I outline below- instead of hard-coding the key and token into the php code, you'll just be using the stored strings in your database.

Now back to assuming that your app will display the same data to everyone:

4) Edit complete.php like so:
- Delete everything below line 51. That is, delete everything from
// 4. Set Person id for example 1 and example 2
down, but leave the ?> tag in the last line intact. Now at the end of the file (but above the ?> tag), enter these two lines:
echo $access_token['oauth_token'] . "<br />";
echo $access_token['oauth_token_secret'];
Now upload index.php and complete.php to your web server.

5) Fire up your browser, and load your index.php page. It will immediately bounce you to PlanningCenterOnline and ask you to log in (if you're not already). Log in to whatever account you want the data to get pulled from. Once you're logged in, it will ask you whether you want to allow your application access to your account. Check "Authorize access" and click "Save changes". It will redirect you to complete.php, and there should be two lines of random characters. The first line is the token proving that this account has authorized access to this app, and the second line is the secret to verify the token is legitimate. You need these strings in the next step.

6) Now edit complete.php again. This time, delete everything below the two require_once lines at the top of the file (again, leave only the ?> line at the bottom of the file intact). Now between the second require_once line and the ?> line, put in this code:
$test_consumer = new OAuthConsumer($key, $secret, NULL);
$access_consumer = new OAuthConsumer('abcdabcdabcdabcdabcd', '1234123412341234123412341234123412341234', NULL);
Now replace the "abcd..." string with the token that showed up a moment ago on complete.php (it's the top line). And replace the "1234..." string with the secret string that was below it.

(if you're storing these keys in the database, this is obviously where you want to fetch the data for the current user and replace that in, instead of hard-coding it into the php here).

7) Your app should be able to authenticate sucessfully now, and make requests to modify or read data, based on the permissions of the authenticated user in PlanningCenterOnline. To make sure it worked, add in the following lines just above the ?> tag:
// build and sign request
$request = OAuthRequest::from_consumer_and_token($test_consumer,
$access_consumer,
'GET',
'https://www.planningcenteronline.com/organization.xml',
NULL);
$request->sign_request(new OAuthSignatureMethod_HMAC_SHA1(),$test_consumer,$access_consumer);
// make request
$response = run_curl($request, 'GET');
// display request
echo $response;
Now upload complete.php again, and browse directly to it again. This time, you should see a whole lot of XML output providing you all the data about your organization. It should look similar to what you see in their example. If you do, it worked! Not too bad, huh?

8) Now for the fun part: you need to actually write your app! Fortunately, this is a lot easier than all the OAuth stuff. See how, in the lines above, we chose to, "get" the URL https://www.planningcenteronline.com/organization.xml? GET means that we want to read, not change, the specified data. The URL is documented under "Organization API" at http://www.planningcenteronline.com/home/api. So look through all the APIs (Plans, People, Songs, etc) and look at their samples to see what output you can expect, in order to extract the data you want to show in your app. Anytime the URL on one of their APIs includes the number 1, you need to understand that's actually a placeholder for an ID you need to get from one API level "up".

So for my app, which lists all the songs sung in a particular week, I have to start at the Organization API and extract the ID of the correct "service type". (I know which service type I want because each service type has a "name" tag associated with it as well, and that's the name I'm used to seeing in Planning Center). Say that ID was 1234.

Now look at the Plans API. The URL you need to GET to get a list of available plans (in my case, plans = weekends) is https://www.planningcenteronline.com/service_types/1/plans.xml, so I replace 1 with the 1234 that IDs the service type I want to look into, and build my query to GET https://www.planningcenteronline.com/service_types/1234/plans.xml instead.

That returns a list of available plans, so I need to parse the list to find the ID of the plan I want to examine. Then replace that ID into the URL for listing all the elements of a service. For my app, I then parsed to find the IDs of all elements which had a "type" of "PlanSong", and looped through the list, replacing each ID into the URL of the Songs API to extract the titles.

Now, I did find out that there is at least one argument you can put on the end of the URL to change the output- since my app deals with plans in the past and the default return from the Plans API is only future plans, they told me instead of running GET on https://www.planningcenteronline.com/service_types/1/plans.xml, I could run it on https://www.planningcenteronline.com/service_types/1/plans.xml?all=true instead. See the extra parameter there on the end? all=true tells it to return past services as well. And they told me if there were multiple parameters to use an ampersand (&) between the keys, which along with the question mark before the first key, is pretty typical. But I was never able to get an answer or find a list of all the available parameters for the different URLs, nor did brief experimentation with other logical key/value combinations yield anything else that worked. But if there's some information you need that it won't return, write 'em up and ask them- there just may be a parameter you can add that will do the trick for you! And if you learn of any others, please leave a comment here!

Though a detailed description of looping through the XML is more than I want to cover in this post, I'll at least point you in the right direction. I store the XML data in $response, as you saw in the initial Organization API example we used to make sure authentication was working. So here's how I display the date of the service whose songs I'm listing:
$xml = simplexml_load_string($response);
foreach($xml->children() as $child)
{
if($child->getName() == 'dates')
{
echo 'Songs from the ' . $child . ' services';
}
}
I hope that helps someone who wants to use the API with PHP!