Monthly Archives: Dezember 2011

Google Picker API für GWT

Google Picker API in action

Vor einigen Wochen wurde ich mit der Implementierung eines GWT Wrappers für die Google Picker API beauftragt.

Mit dieser API ist es als Benutzer möglich über eine Weboberfläche auf Google Inhalte zuzugreifen. Ob es YouTube Videos, eigene Dokumente, die Google Bilder Suche oder sogar Google Maps sind… die API liefert Vieles.

Die Rückgabewerte sind Dokumente mit Infos über den ausgewählten Inhalt. Darunter fallen eindeutige ID’s, Verweise auf Thumbnails in verschiedenen Größen und einige Meta-Informationen.

Da es diese API (noch) nicht als GWT Modul gab, war ein Warpper um die JavaScript Bibliothek à la Google API Libraries for GWT sinnvoll.

Aber das Beste an dem Projekt ist, dass die floreysoft GmbH den Quellcode freigegeben und unter der Apache 2.0 Lizenz veröffentlicht hat. Ziemlich cool also! Und hier geht’s zum Projekt.

Mit Grails nach Fotos bei Flickr suchen und durchblättern

Die gestrige Aufgabe hat echt Spass gemacht: Ich habe It’s nice in… um eine Funktion erweitert, mit der ich zu jedem Ort aus der Datenbank Bilder von Flickr betrachten und blättern kann. Eine Demo ist hier zu sehen. Einfach auf einen Marker klicken und den „Bilder“ Reiter öffnen.

Mit Ini Fotos vom Great Barrier Reef zeigen lassen

Wie das mit Grails zu realisieren ist, möchte ich nachfolgend erläutern…

Die Flickr API ist einfach zu benutzen. Die Funktion flickr.photos.search kann ich für allerlei Arten von Bildersuche verwenden. Volltextsuche, Suche nach Tags, Geo-Suche, usw.

Ich benötige lediglich die folgenden Komponenten:

  1. Einen API-Schlüssel (Hier zu bekommen)
  2. Eine View
  3. Einen Controller
  4. Ein Template

Die View

Für den Einstieg habe ich mir eine View erzeugt, die alle Stylesheets beherbergt und – ganz wichtig – einen Container beinhaltet, in dem die Fotos später per Ajax paginiert werden:

...
<div id="photo-container">
  <g:include controller="flickr" action="photos" params="${[text: query]}" />
</div>
...

Hier ist auch zu sehen, dass ich kein Template (wie sonst üblich) einbinde, sondern einen Controller-Aufruf initiiere, der den Inhalt des Containers rendert. Ich hätte es über eine TagLib lösen können. Aber den Controller benötige ich für die Ajax-Paginierung sowieso und so kann er gleich die Logik für den API-Zugriff beherbergen.

Der FlickrController

import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON
 
class FlickrController {
  def grailsApplication
 
  /**
   * Uses the Flickr API to find photos for the given parameters
   */
  def photos = {
    def json = findPhotosBy(params)
    render(template: "photos", model: [photos: json.photos?.photo ?: [], pagination: json.photos, text: params.text])
  }
 
  private def findPhotosBy(attrs = [:]) {
    def query = [
            method: 'flickr.photos.search',
            api_key: config.flickr.key,
            sort: 'relevance',
            per_page: attrs.perPage ?: 14,
            page: attrs.page ?: 1,
            format: 'json',
            text: attrs.text,
            nojsoncallback: 1
    ]
 
    def http = new HTTPBuilder('http://api.flickr.com')
    http.request(GET, JSON) {
      uri.path = '/services/rest/'
      uri.query = query
 
      response.success = { resp, json ->
        json.photos?.photo?.each { map ->
          map.images = [
                  s: createUrlToImage(map + [size: 's']),
                  b: createUrlToImage(map + [size: 'b']),
                  m: createUrlToImage(map + [size: 'm'])]
          map.extern = "http://www.flickr.com/photos/${map.owner}/${map.id}".toString()
          map
        }
        return json
      }
 
      response.failure = { resp ->
        throw new RuntimeException("Error [${resp.statusLine.statusCode}]: ${resp.statusLine.reasonPhrase}")
      }
    }
  }
 
  /**
   * Creates an url to a flickr photo.
   *
   * @param params A map with photo details
   * @return An absolute url
   */
  private def createUrlToImage(params) {
    "http://farm${params.farm}.static.flickr.com/${params.server}/${params.id}_${params.secret}_${params.size}.jpg".toString()
  }
 
  /**
   * @return Application config
   */
  private ConfigObject getConfig() {
    grailsApplication.config
  }
}

Was passiert im Controller?

Schritt 1

Nun, zunächst einmal will ich die Fotos finden. Dazu definiere ich mir einige Parameter für die Suche: Suchtext, Paginierung, Sortierung und der Rückgabewert, den ich gern im JSON-Format hätte.

Schritt 2

Der HTTPBuilder macht den API-Aufruf zu einem Klacks! Dazu übergebe ich den Host, die Parameter und sorge im Fehlerfall für eine Exception. Beachte die statischen Importe!

Schritt 3

Im Erfolgsfall erweitere ich das JSON-Ergebnis, um einige URL’s, die auf die Bilder zeigen. Dafür definiert Flickr einige URL-Pattern für den direkten Zugriff auf die Bilder und andere Quellen. Auf diese Url’s greife ich später im Template zu.

Schritt 4

Rendern des Templates. Es werden die Fotos, die Paginierung und der Suchbegriff übergeben. Das ist wichtig, da ich für die Ajax-Paginierung diese Parameter benötige.

Das Template

Nun wollen die Photos noch auf die Leinwand gebracht werden, und das passiert mit diesem Template:

<%@ page contentType="text/html;charset=UTF-8" %>
<g:setProvider library="jquery" />
 
<div class="pagination-wrapper">
  <a href="http://www.flickr.com" target="_blank">
    <small>Powered by</small>
    <img src="<g:resource dir="images/layout" file="flickr.png" />" alt="powered by Flickr" />
  </a>
 
  <div class="pagination">
    <g:if test="${pagination.page > 1}">
      <div class="box">
        <g:remoteLink class="prev" controller="flickr" action="photos" params="${[text: text, page: pagination.page - 1]}" update="photo-container"><<</g:remoteLink>
      </div>
    </g:if>
    <div class="box">${pagination.page}</div>
    <g:if test="${pagination.page < pagination.pages}">
      <div class="box">
        <g:remoteLink class="next" controller="flickr" action="photos" params="${[text: text, page: pagination.page + 1]}" update="photo-container">>></g:remoteLink>
      </div>
    </g:if>
  </div>
</div>
 
<ul class="hoverbox">
  <g:each var="photo" in="${photos}" status="index">
    <li>
      <a href="${photo.extern}" target="_blank" title="Photo in Flickr öffnen">
        <img src="${photo.images.m}" alt="${photo.title}" class="preview" />
        <img src="${photo.images.s}" title="${photo.title}" class="thumb" alt="${photo.title}" />
      </a>
    </li>
  </g:each>
</ul>
<div class="clear"></div>

Fertig ist die Flickr-Foto-Paginierung 🙂