Custom flickr sidebar via wget, cron & PHP

Wednesday, July 29th, 2009

The new site launched with a sidebar that shows two random photos from our flickr account, using their javascript widget. This was a great way to get things going, but now we’ve developed slightly more involved needs and I’ve had to come up with a custom solution.

Getting the list of photos

You need a flickr API key, which is quick & easy to get. Then wget & cron to get ‘em: wget --quiet 'http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=YOUR_API_KEY&user_id=YOUR_USER_ID&tags=website&per_page=500' -O photos.xml

Note that this includes a tags argument. The thing that pushed me to switch the workflow was the desire to be able to upload photos to our flickr account that don’t necessarily fit into the sidebar format, such as panoramics. To handle this, everything that belongs on the website gets the tag website, and we only fetch those ones. We’ve also talked about just getting landscape oriented photos, but haven’t implemented that.

I’m running this daily, which is plenty often to update the available photo list. I believe this gets the newest 500, which seems more than adequate, particularly since we don’t have close to 500 photos yet.

Parsing the list & generating the HTML

PHP5’s SimpleXML is pretty nice — here’s what we’re doing:

try {
  $xml = new SimpleXMLElement(file_get_contents('photos.xml'));
    $number_of_photos = count($xml->photos->photo);
    $displayed_photos = array();
    array_push(
      $displayed_photos,
      $xml->photos->photo[rand(0, $number_of_photos - 1)]);
    array_push(
      $displayed_photos,
      $xml->photos->photo[rand(0, $number_of_photos - 1)]);
    foreach ($displayed_photos as $photo) { ?>
  <div>
<?php
      print "<a href=\"http://www.flickr.com/photos/8562013@N07/" .
        $photo['id'] . "\"><img src=\"http://farm" . $photo['farm'] .
        ".static.flickr.com/" . $photo['server'] . "/" . $photo['id'] . "_" .
        $photo['secret'] .  "_m.jpg\" alt=\"" . $photo['title'] . "\" /></a>";
?>
  </div>
<?php
 }
} catch (Exception $e) {
  error_log("flickr badge had some troubles: " .
    $e->getMessage());
}

This snippet takes my laptop less than 1/20th of a second to run from the command line, which suits me fine. The actual code sits in page.tpl.php.

Flickr’s API docs, in particular the API Explorer, were awful handy in figuring this all out.

Joe’s hacky approach to getting arbitrary behavior out of Drupal

Tuesday, July 28th, 2009

Wherein I disqualify myself for work as a “drupal developer”

Self indulgent history part

Way back in 1999 when I started out writing web apps, Embperl seemed like the logical tool to use — so I’m used to having direct access to every step in the request-to-response process. In years since, I’ve progressed through a bunch of different tools and approaches, which have included writing a few rudimentary content management systems and using a bunch of prexisting ones.

Over the last few years I’ve become a big fan of ruby on rails, so if a site does much of anything (simple, recent example: The Food Project’s online summer program application), that’d be my starting point.

so, why use Drupal?

Even so, when a site is basically about some content that’s written and edited on an ongoing basis by other folks, Drupal starts looking like a reasonable tool. It’s pretty easy to get it to generate semantic output with reasonable URLs, and the editor-facing UI does a fair job of offerring a large degree of control and customizability without being overwhelmingly complex.

Despite having done some custom work on a few live Drupal sites, I still get confused about the best way to do some of the arbitrary web interaction tasks that were so straightforward back in the Embperl days. There are a billion modules in the Drupal ecosystem, but I often find that it takes more time to figure out if there is an appropriate one for my task & if it works with the current version of Drupal than it would to code up a solution on my own. Also, while I’ve made several attempts to solve programming tasks in what I understand to be the Drupal way, wrapping my head around all the relevant APIs (which can totally change every 6-12 months, with each Drupal release) similarly tends to take more time than bypassing Drupal’s functionality & handling things in Plain Old PHP.

Here’s a common approach I’ll take, then. I have no interest in extensive coding through a web browser (i.e. putting a bunch of PHP in a node or block), so I’ll set up a Drupal module to hold my application’s arbitrary php functions. Then I’ll create a page at the desired URL and enter a line of PHP to route the GET and/or POST to my code, as appropriate.

Example

I’m working on an email list signup that needs to communicate with a FileMaker database. An HTML form POSTs to http://thefoodproject.org/mailing_list_signup, which is just a Drupal page using the PHP input filter (which you now need to turn on via admin settings) containing <?php tfp_process_list_signup($_POST); The code for that function lives in sites/all/modules/tfp/tfp.module, and does the normal PHP things to pull information out of the POST & hand that info off to the FileMaker database. Here’s a bit of it:

function tfp_process_list_signup($_POST) {
  $reply = '';
  $sent_plausible_address = FALSE;
  if ($_POST['email']) {
    $supplied_email = $_POST['email'];
    if (preg_match(PLAUSIBLE_EMAIL, $supplied_email)) {
      $sent_plausible_address = TRUE;
      $reply = "Thanks! We'll add your to our mailing list.";
    } else {
      $reply = "'$supplied_email' doesn't look like an email address to me. ";
    }
    //. . .
  }
  return $reply;
}

On the upside, this is quick & easy. The downside with this approach vs. writing a module the Drupal way is that I’m losing out on Drupal’s form API and its associated validation, antispoofing, etc. services. On the upside, last time I checked the linked documentation for the form API, it said

Warning - this page has only been partially updated for the Drupal 6.x API
Until it has been fully updated, reference this page as well: Drupal 5.x to 6.x FormAPI changes

so I’m quite happy to not have to wade through all of that! (more…)

FileMaker error 100

Friday, November 21st, 2008

or, Why To Use Dedicated Layouts When Connecting To FileMaker Via PHP

I’d read that it’s a good practice to always use a dedicated layout for any PHP scripts you have that are talking to a FileMaker database. While I’d seen reasons of efficiency and reliability, today I learned another reason that’s true: it can eliminate otherwise hard-to-debug problems.

At first when working on my current FileMaker <-> PHP project, I was attempting to reuse an existing layout that had all the info I needed. While my permissions seemed to be fine for the data file and layout I was attempting to access, actually running the script kept resulting in “Error 100: File is missing” coming back at me as soon as I added any criteria to my search. FileMaker doesn’t bother putting anything useful in its server logs, either, so it wouldn’t have been much fun picking through the layout & figuring what linkage(s) were to blame.

However, by simply creating a dedicated layout, everything started working as planned. A practice I’ll be following in the future.

Sending email from FileMaker via PHP, revisited

Wednesday, October 22nd, 2008

We last looked at this a few months ago, but have been revisiting it to come up with something a little more robust. Notes:

  • there’s a good overview of using PHP with FM at the sixfriedrice blog.
  • the API for FileMaker’s PHP interface is available at http://YOURSERVER.URL:16000/docs/PHP%20API%20Documentation/index.html
  • I’d missed this last time around, but accounts & permissions are a little funky. fmphp needs to be added to the Extended Privileges of the database you’re trying to get to, and must have the same privilege set as the account you’re connecting as.
  • The solution we settled on is a CLI PHP script running hourly, checking for mail to send. Launchd would be the logical way to do the scheduling, but always drives me nuts. Fortunately the server in question has cron set up (so much simpler!)

The code we’re more or less using:


#!/usr/bin/php
<?php
set_include_path(get_include_path() . PATH_SEPARATOR .
  '/Library/FileMaker Server/Web Publishing/publishing-engine/php/lib/php/');
require_once('FileMaker.php');

echo "PHP email-sending-script, running at " .
  date('m/d/Y H:i') . "\n";
$layout = 'Outgoing_Email';
$fm = new FileMaker('Layout Name');
$fm->setProperty('username', 'your filemaker username');
$fm->setProperty('password', 'your filemaker password');

$findCmd =& $fm->newFindCommand($layout);
$findCmd->addFindCriterion('Sent_Flag', '< 1');
$result = $findCmd->execute();
if (FileMaker::isError($result)) {
  if ($result->code == 401) {
    exit("No emails to send.\n");
  } else {
    exit("trouble: " . $result->message . "(" . $result->code . ")");
  }
}

$records = $result->getRecords();
foreach($records as $record) {
  echo "To: " . $record->getField('Recipient') . "\n";
  echo "Subject: " . $record->getField('Subject') . "\n";
  $headers = array(
    "From: filemaker@example.com",
    "MIME-Version: 1.0",
    "Content-type: text/html"
    );

  /*
   FM helpfully encodes < and >...
   */
  $body = preg_replace('/&lt;/', '< ', $record->getField('Body'));
  $body = preg_replace('/&gt;/', '>', $body);
  $rc = mail($record->getField('Recipient'),
       $record->getField('Subject'),
       $body,
       implode("\r\n", $headers)
      );
  if ($rc) {
    $update = $fm->newEditCommand($layout, $record->getRecordId());
    $update->setField('Sent_Date', date('m/d/Y'));
    $update->setField('Sent_Time', date('H:i'));
    $update->setField('Sent_Flag', '1');
    $result = $update->execute();
    if (FileMaker::isError($result)) {
      exit("trouble updating the database after sending email: " .
        $result->message . "(" . $result->code . ")");
    }
    echo "Mailed!\n";
  } else {
    exit("Mail didn't work.\n");
  }
}

Sending server-side emails from FileMaker via PHP

Thursday, June 12th, 2008

Some context — my new gig features a big ‘ol FileMaker installation, which has a number of automated maintenance routines. Some of those routines send emails, through a convoluted process involving FileMaker calling a GUI MUA (e.g. Mail.app). There are a number of practical problems with this, such as the requirement of another computer running, always logged into an account capable of sending the emails.

We’d done a bit of research about strictly server-side alternatives, and found surprisingly little. The best resource was Graham Sprague’s page about sending emails via FileMaker’s XSLT Web Publishing tool. We gave that approach a try, but didn’t get any results, or anything useful from FileMaker’s logs to explain why things weren’t working. I’m not sure what FileMaker version Graham’s example was written for, perhaps something’s changed with version 9?

Rather than dive into FileMaker’s proprietary XSLT system to debug things, it occurred to me that this might be a job for FileMaker’s PHP API. Sure enough, after about 15 minutes of consulting the API Doc, we were sending emails based on the contents of a FileMaker record.

We’re still working on ironing out the details, but here’s the rough proof of concept PHP file. It works with the example email database from Graham’s XSLT sample, with the php permission added to the database. Plenty of missing features such as cc & bcc fields, actually checking for the ‘send’ flag, checking for errors, any kind of authorization or authentication, etc. In other words, you probably don’t want to be running this on a publicly accessible webserver, but at least it presents the basic idea in a simple form.


<?php
require_once('FileMaker.php');
$fm = new FileMaker();
$fm->setProperty('username', 'send_email');
$fm->setProperty('password', 'whatever_the_password_is');
$fm->setProperty('database', 'Email');

$findCmd =& $fm->newFindAllCommand('Utility_Email');
$result = $findCmd->execute();
$records = $result->getRecords();
foreach($records as $record) {
  $headers = array("From: " . $record->getField('from'));

  mail($record->getField('to'),
       $record->getField('subject'),
       $record->getField('message'),
       implode("\r\n", $headers)
      );
}
echo "Emails sent";

puttering around with Symfony

Wednesday, January 23rd, 2008

On advice from Nate, I’m taking Symfony for a spin (using the stable version 1, not the under-development 1.1), reimplementing the aforementioned contact management application. Here’s what I’m noticing:

  • Somewhat like Rails, there’s DB-independent schema definition! Big win here. Among other things, this lets you develop in SQLite and deploy in MySQL, which is a nice pattern. Rails’ awesome migrations aren’t here, so it’s not as useful and flexible a system, but it’s better than nothing for getting up & running.
  • One big difference from Rails that’s clear right away: rather than building code dynamically based on the DB schema, thousands of lines of getters / setters / etc. are generated by the symfony command line tool. Makes sense for PHP, but it’s a clunkier development experience.
  • There’s testing built in! It’s a bit of a PITA to do things like create a test database, and all the pieces for testing your models aren’t there right away, but still better than CakePHP. The aforementioned missing piece is available as a plugin. There doesn’t seem to be a straightforward way to get the propel-insert-sql task to run on alternate environments, so one must create the db on their own. I’m using SQLite, which is great, but my local environment somehow ended up with PHP having SQLite 2.8 while my command line is version 3 (macports at fault? what a step backwards from Ubuntu…). One port install sqlite2 later, and you can do sqlite data/test.db < data/sql/lib.model.schema.sql
  • No console, which makes the tests even more essential, so it does seem to be worth the pain of getting them working.

Side note — already, on the basis of the previous post, I’m seeing a surprising amount of traffic from searches on CakePHP, Symfony, and REST. Clearly I’m not the only one looking for this kind of thing.

First thoughts on CakePHP from a Rails perspective

Friday, December 14th, 2007

I have a new project coming up that seems like a great fit for Ruby on Rails, particularly the RESTful interfaces that have gone in over the last year. However, the typical questions about using Rails apply here: I’m not sure how well the project’s hosting environment will support Rails, and collaborators aren’t as familiar with it. Thus, it seemed like it might be worth another look at options on the PHP side of things. I keep hearing that CakePHP is Rails-inspired and has many of the same advantages, so today I’ve taken the stable version (1.1.8.5850) for a spin, letting the Cake manual’s blog tutorial get me started. Here’s what I’ve noticed about CakePHP development as practiced in the tutorial — maybe there’s better ways to do things & the tutorial just isn’t mentioning them?

  • No migrations; you have to generate the DDL on your own, both initially and for any subsequent modifications. Also, no model or controller generators. It’s definitely nicer to just do script/generate scaffold post title:string body:text; rake db:migrate and be off to the races — from Rails 1.2 on, those races include the whole REST business. To be fair, there is a one off reference to ‘bake’ scripts in the CakePHP manual, but no pointers on what those are, or where they live, and the website isn’t much clearer.
  • No TDD. Woah. That’s, like, half the advantage of Rails — automated testing is right there in your face, and is firmly entrenched in the community.
  • PHP code is not quite as elegant as Ruby. For example, generating a link to delete a post looks like this: $html->link('Delete', "/posts/delete/{$post['Post'][id']}", null, 'Are you sure?' ). Can you spot the syntax error? Probably you can, but it took me a good few minutes. The Rails equivalent would be
    link_to "Delete", { :action => "delete", :id => @post.id }, :confirm => "Are you sure?", :method => :delete
  • A lack of the experiences baked into Rails. For example, after the Google Web Accelerator fiasco, it became common practice to make all links to destructive actions happen via POST, which can be done via link_to’s :method parameter. Compare to the GET-powered delete link above, straight from the Cake tutorial.
  • I miss my vim & rails integration, which makes creating and navigating the various files a breeze. (Glad I went to the trouble of making that a link, as it got me re-reading the plugin’s about page, and I saw the parts about partial extraction and migration inversion, neither of which I’d noticed before)

On the upside, using CakePHP sure beats writing a CRUD-heavy PHP application from scratch. One of my current projects, a completely custom contact management application, would be in much better shape if it were using CakePHP instead of the project-specific ORM I cobbled together — I’m going to have to give conversion of that project to CakePHP some serious thought.

But is CakePHP a DSL for the web in the sense that Rails is? Not yet.