␊ |
class Picasaweb {␊ |
␊ |
public static $render_image = true;␊ |
public static $render_photo = true;␊ |
␊ |
public static $render_album = true;␊ |
␊ |
const IMAGE_REGEX = '#https://picasaweb.google.com/\S*/photo/\S*#';␊ |
const ALBUM_REGEX = '#https://picasaweb.google.com/\S*#';␊ |
␊ |
private static function renderImage($url) {␊ |
$viewer = new SSViewer('Picasaweb_image');␊ |
/**␊ |
* Get a SimpleXMLElement from the wild.␊ |
*␊ |
* Fetches the resource found at $url (supposedly an XML file)␊ |
* and convert it to the SimpleXMLElement representation of the␊ |
* whole tree.␊ |
*␊ |
* @param String $url The URL of the resource.␊ |
* @return SimpleXMLElement The DOM of the resource.␊ |
*/␊ |
private static function xmlFetch($url) {␊ |
$xml = file_get_contents($url);␊ |
if (empty($xml))␊ |
return null;␊ |
␊ |
// I have only one life...␊ |
$xml = str_replace(' xmlns=', ' fakens=', $xml);␊ |
␊ |
return simplexml_load_string($xml);␊ |
}␊ |
␊ |
/**␊ |
* Pick a specific set of fields from a SimpleXMLElement.␊ |
*␊ |
* Gets the values of a set of fields and returns the result in an␊ |
* ArrayData. The values are collected from a child of $element or␊ |
* from an attribute of $element with the requested field name.␊ |
*␊ |
* The field set is specified in $fields. If it is a simple array␊ |
* value ('value') this value is used as either as the key to look␊ |
* for in $element and as key to store the result in. If it is a␊ |
* key-value pair ('key' => 'value') 'key' is used as index for␊ |
* accessing $element and 'value' will be the key in the result.␊ |
*␊ |
* @param SimpleXMLElement $element The source element.␊ |
* @param Array $fields The fields to retrieve.␊ |
* @return ArrayData The result.␊ |
*/␊ |
private static function compoundData(SimpleXMLElement $element, Array $fields) {␊ |
$data = new ArrayData(array());␊ |
foreach ($fields as $src => $dst) {␊ |
// Do not overwrite yet defined fields␊ |
if ($data->hasField($dst))␊ |
continue;␊ |
␊ |
// Try to get the value from the children and fallback␊ |
// to the attributes if not found there␊ |
$key = is_string($src) ? $src : $dst;␊ |
$node = isset($element->$key) ? $element->$key : null;␊ |
if (is_null($node) && isset($element->attributes()->$key))␊ |
$node = $element->attributes()->$key;␊ |
␊ |
// Set in the data, if found␊ |
isset($node) and $data->setField($dst, (string) $node);␊ |
}␊ |
return $data;␊ |
}␊ |
␊ |
/**␊ |
* Return the metadata of an image.␊ |
*␊ |
* Convenient shortcut to return typical image fields,␊ |
* such as src, width and height, from an XML element.␊ |
* The data is collected with self::compoundData(), so␊ |
* either children and attributes are considered.␊ |
*␊ |
* @param SimpleXMLElement $element The source element.␊ |
* @return ArrayData Image data.␊ |
*/␊ |
private static function imageMetadata(SimpleXMLElement $element) {␊ |
$fields = array('src', 'url' => 'src', 'width', 'height', 'size');␊ |
return self::compoundData($element, $fields);␊ |
}␊ |
␊ |
/**␊ |
* Return the first match of an XPath pattern.␊ |
*␊ |
* Given a specific XPath, returns the first value or␊ |
* an empty string if the XPath does not match anything.␊ |
*␊ |
* @param SimpleXMLElement $element The source element.␊ |
* @param String An xpath pattern.␊ |
* @return String The first match.␊ |
*/␊ |
private static function xquery(SimpleXMLElement $element, $xpath) {␊ |
$result = $element->xpath($xpath);␊ |
if (! is_array($result))␊ |
return '';␊ |
␊ |
return (string) reset($result);␊ |
}␊ |
␊ |
/**␊ |
* Return the metadata of a photo.␊ |
*␊ |
* Gets some metadata from a photo. The XML element is␊ |
* expected to be an XML photo entry as returned by the␊ |
* PicasaWeb API version 2.␊ |
*␊ |
* @param SimpleXMLElement $element The source element.␊ |
* @return ArrayData Photo metadata.␊ |
*/␊ |
private static function photoMetadata(SimpleXMLElement $element) {␊ |
if (empty($element))␊ |
return null;␊ |
␊ |
$gphoto = $element->children('http://schemas.google.com/photos/2007');␊ |
$media = $element->children('http://search.yahoo.com/mrss/')->group;␊ |
␊ |
$data = new ArrayData(array(␊ |
'url' => $url␊ |
'self' => self::xquery($element, 'link[@rel="self"]/@href'),␊ |
'canonical' => self::xquery($element, 'link[contains(@rel,"#canonical")]/@href'),␊ |
'photoid' => (string) $gphoto->id,␊ |
'albumid' => (string) $gphoto->albumid,␊ |
'published' => (string) $element->published,␊ |
'title' => (string) $element->title,␊ |
'summary' => (string) $element->summary,␊ |
'credit' => (string) $media->credit,␊ |
'keywords' => (string) $media->keywords,␊ |
'timestamp' => (string) $gphoto->timestamp,␊ |
␊ |
// src is usually not defined in org (I suppose for copyright␊ |
// issues) but it is included here nonetheless␊ |
'org' => self::imageMetadata($gphoto),␊ |
'normal' => self::imageMetadata($media->content)␊ |
));␊ |
return $data->renderWith($viewer);␊ |
␊ |
// Add any thumbnail element found, naming the items "thumbnail$n"␊ |
// with $n starting from 1␊ |
$n = 1;␊ |
foreach ($media->thumbnail as $element) {␊ |
$data->setField("thumbnail$n", self::imageMetadata($element));␊ |
++$n;␊ |
}␊ |
␊ |
return $data;␊ |
}␊ |
␊ |
private static function renderAlbum($url) {␊ |
return 'Rendering the album';␊ |
/**␊ |
* Return the metadata of an album.␊ |
*␊ |
* Gets some metadata from an album. The XML element is␊ |
* expected to be an XML album entry as returned by the␊ |
* PicasaWeb API version 2.␊ |
*␊ |
* @param SimpleXMLElement $element The source element.␊ |
* @return ArrayData Album data.␊ |
*/␊ |
static function albumMetadata(SimpleXMLElement $element) {␊ |
if (empty($element))␊ |
return null;␊ |
␊ |
$gphoto = $element->children('http://schemas.google.com/photos/2007');␊ |
␊ |
return new ArrayData(array(␊ |
'self' => self::xquery($element, 'link[@rel="self"]/@href'),␊ |
'userid' => (string) $gphoto->user,␊ |
'icon' => (string) $element->icon,␊ |
'title' => (string) $element->title,␊ |
'subtitle' => (string) $element->subtitle,␊ |
'author_name' => (string) $element->author->name,␊ |
'author_uri' => (string) $element->author->uri,␊ |
'name' => (string) $gphoto->name,␊ |
'location' => (string) $gphoto->location,␊ |
'timestamp' => (string) $gphoto->timestamp,␊ |
'numphotos' => (string) $gphoto->numphotos,␊ |
'nickname' => (string) $gphoto->nickname␊ |
));␊ |
}␊ |
␊ |
/**␊ |
* Renders the given URL as a Picasaweb image or album.␊ |
* Check if a string is an ID.␊ |
*␊ |
* @param url␊ |
* The string returned is captured from print_r(). It is␊ |
* intended to be used for debugging purpose.␊ |
* Helper function to check if the given $str is a valid Picasaweb␊ |
* ID. A string is considered an ID if it contains only digits.␊ |
*␊ |
* @param String $str The string to check.␊ |
* @return Boolean true if $str contains only digits.␊ |
*/␊ |
private static function isId($str) {␊ |
return strlen($str) == strspn($str, '0123456789');␊ |
}␊ |
␊ |
public static function photoUrl($user, $photo, $album = null) {␊ |
// At least user and photo are required to access a photo entry␊ |
if (! isset($user, $photo))␊ |
return null;␊ |
␊ |
$user = "user/$user";␊ |
$photo = self::isId($photo) ? "photoid/$photo" : "photo/$photo";␊ |
$url = 'http://picasaweb.google.com/data/entry/api';␊ |
␊ |
// The album is optional␊ |
if (isset($album)) {␊ |
$album = self::isId($album) ? "albumid/$album" : "album/$album";␊ |
$url = "$url/$user/$album/$photo";␊ |
} else {␊ |
$url = "$url/$user/$photo";␊ |
}␊ |
␊ |
return $url;␊ |
}␊ |
␊ |
public static function albumUrl($user, $album) {␊ |
// Both user and album are required to access an album␊ |
if (! isset($user, $album))␊ |
return null;␊ |
␊ |
$user = "user/$user";␊ |
$album = self::isId($album) ? "albumid/$album" : "album/$album";␊ |
␊ |
return "http://picasaweb.google.com/data/feed/api/$user/$album";␊ |
}␊ |
␊ |
public static function photo($url) {␊ |
$tree = self::xmlFetch($url);␊ |
if (empty($tree))␊ |
return null;␊ |
␊ |
return self::photoMetadata($tree);␊ |
}␊ |
␊ |
public static function album($url) {␊ |
$tree = self::xmlFetch($url);␊ |
if (empty($tree))␊ |
return null;␊ |
␊ |
$data = self::albumMetadata($tree);␊ |
if (empty($data))␊ |
return null;␊ |
␊ |
// Add the photo list␊ |
$items = new ArrayList();␊ |
foreach ($tree->entry as $element)␊ |
$items->push(self::photoMetadata($element));␊ |
$data->setField('items', $items);␊ |
␊ |
return $data;␊ |
}␊ |
␊ |
/**␊ |
* Render Picasaweb photo or album.␊ |
*␊ |
* Given a Picasaweb URL, renders it as an HTML chunk that␊ |
* represents the album or a specific photo. The type of element␊ |
* depends on the format of the URL: if it has a fragment identifier␊ |
* it is a photo, otherwise it is an album.␊ |
*␊ |
* In case of error (the Picasaweb server is down, the API has ␊ |
* changed or $url is invalid), null is returned.␊ |
*␊ |
* @param String $url The URL to render.␊ |
* @returns A ready to use HTML chunk or null on errors.␊ |
* @returns String A ready to use HTML chunk or null on errors.␊ |
*/␊ |
public static function render($url) {␊ |
if (self::$render_image && preg_match(self::IMAGE_REGEX, $url))␊ |
return self::renderImage($url);␊ |
// Try to clean up a bit the Picasaweb URL mess␊ |
$url = preg_replace('"\?[^#]*"', '', $url);␊ |
␊ |
$match = array();␊ |
if (preg_match('"^https?://picasaweb.google.com/(\S*?)/(\S*?)(#(\S*?))?$"', $url, $match) != 1)␊ |
return null;␊ |
␊ |
if (self::$render_album && preg_match(self::ALBUM_REGEX, $url))␊ |
return self::renderAlbum($url);␊ |
$user = @$match[1];␊ |
$album = @$match[2];␊ |
$photo = @$match[4];␊ |
␊ |
return null;␊ |
if (empty($user) || empty($album)) {␊ |
// Both user and album are required␊ |
return null;␊ |
} elseif (empty($photo)) {␊ |
$viewer = new SSViewer('Picasaweb_album');␊ |
$data = self::album(self::albumUrl($user, $album));␊ |
} else {␊ |
$viewer = new SSViewer('Picasaweb_photo');␊ |
$data = self::photo(self::photoUrl($user, $photo, $album));␊ |
}␊ |
␊ |
if (is_null($data))␊ |
return null;␊ |
␊ |
// $url cannot be embedded as is: if included in a template it␊ |
// will be expanded recursively. This is the rationale behind␊ |
// the following scheme stripping.␊ |
$data->setField('url', preg_replace('"https?://"', '', $url));␊ |
␊ |
return $data->renderWith($viewer);␊ |
}␊ |
␊ |
public static function shortcode($arguments, $url = '') {␊ |
public static function shortcode($arguments, $url) {␊ |
$result = self::render($url);␊ |
if (is_null($result)) {␊ |
$url = htmlentities($url, ENT_COMPAT, 'UTF-8');␊ |
|
function onBeforeWrite() {␊ |
parent::onBeforeWrite();␊ |
␊ |
if (empty($this->owner->Content))␊ |
$content = $this->owner->Content;␊ |
if (empty($content))␊ |
return;␊ |
␊ |
// Pass every link to the callback: self::renderer() will␊ |
// modify only PicasaWeb links␊ |
$this->owner->Content = preg_replace_callback('#https?://\S*#',␊ |
// modify only valid PicasaWeb links␊ |
$this->owner->Content = preg_replace_callback('"https?://[-A-Za-z0-9+&@#/%?=~_()|!:,.;]*"',␊ |
'self::renderer',␊ |
$this->owner->Content);␊ |
$content);␊ |
}␊ |
};␊ |