]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Moved oEmbed stuff out to a plugin (Oembed).
authorMikael Nordfeldth <mmn@hethane.se>
Tue, 6 May 2014 21:00:30 +0000 (23:00 +0200)
committerMikael Nordfeldth <mmn@hethane.se>
Tue, 6 May 2014 21:32:13 +0000 (23:32 +0200)
25 files changed:
CONFIGURE
actions/attachment.php
actions/oembed.php [deleted file]
actions/shownotice.php
classes/File.php
classes/File_oembed.php [deleted file]
classes/File_redirection.php
db/core.php
lib/action.php
lib/attachmentlist.php
lib/default.php
lib/oembedhelper.php [deleted file]
lib/router.php
plugins/Bookmark/README [new file with mode: 0644]
plugins/LinkPreview/README [new file with mode: 0644]
plugins/Oembed/CONFIGURE [new file with mode: 0644]
plugins/Oembed/OembedPlugin.php [new file with mode: 0644]
plugins/Oembed/README [new file with mode: 0644]
plugins/Oembed/actions/oembed.php [new file with mode: 0644]
plugins/Oembed/classes/File_oembed.php [new file with mode: 0644]
plugins/Oembed/lib/oembedhelper.php [new file with mode: 0644]
plugins/Oembed/scripts/fixup_files.php [new file with mode: 0755]
plugins/Oembed/tests/oEmbedTest.php [new file with mode: 0644]
scripts/fixup_files.php [deleted file]
tests/oEmbedTest.php [deleted file]

index 141c475563386a255be6e321da67ecd1ffbddea0..bec9e90a6fec88b76007dffdac6abcae2bca8299 100644 (file)
--- a/CONFIGURE
+++ b/CONFIGURE
@@ -592,23 +592,6 @@ desclimit: maximum number of characters to allow in group descriptions.
 addtag: Whether to add a tag for the group nickname for every group post 
        (pre-1.0.x behaviour). Defaults to false.
 
-oembed
---------
-
-oEmbed endpoint for multimedia attachments (links in posts). Will also
-work as 'oohembed' for backwards compatibility.
-
-endpoint: oohembed endpoint using http://oohembed.com/ software. Defaults to
-         'http://oohembed.com/oohembed/'.
-order: Array of methods to check for OEmbed data. Methods include 'built-in'
-       (use a built-in function to simulate oEmbed for some sites),
-       'well-known' (use well-known public oEmbed endpoints),
-       'discovery' (discover using <link> headers in HTML), 'service' (use
-       a third-party service, like oohembed or embed.ly. Default is
-       array('built-in', 'well-known', 'service', 'discovery'). Note that very
-       few sites implement oEmbed; 'discovery' is going to fail 99% of the
-       time.
-
 search
 ------
 
index 30499c15f4764b71296d71745cca07101f54d9ec..d9cacb11931d560ddb662e00f365b7fd8032e5c2 100644 (file)
@@ -96,28 +96,6 @@ class AttachmentAction extends Action
         return $a->title();
     }
 
-    function extraHead()
-    {
-        $this->element('link',array('rel'=>'alternate',
-            'type'=>'application/json+oembed',
-            'href'=>common_local_url(
-                'oembed',
-                array(),
-                array('format'=>'json', 'url'=>
-                    common_local_url('attachment',
-                        array('attachment' => $this->attachment->id)))),
-            'title'=>'oEmbed'),null);
-        $this->element('link',array('rel'=>'alternate',
-            'type'=>'text/xml+oembed',
-            'href'=>common_local_url(
-                'oembed',
-                array(),
-                array('format'=>'xml','url'=>
-                    common_local_url('attachment',
-                        array('attachment' => $this->attachment->id)))),
-            'title'=>'oEmbed'),null);
-    }
-
     /**
      * Handle input
      *
diff --git a/actions/oembed.php b/actions/oembed.php
deleted file mode 100644 (file)
index da0352e..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-<?php
-/**
- * StatusNet, the distributed open-source microblogging tool
- *
- * StatusNet-only extensions to the Twitter-like API
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Twitter
- * @package   StatusNet
- * @author    Evan Prodromou <evan@status.net>
- * @copyright 2008 StatusNet, Inc.
- * @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Oembed provider implementation
- *
- * This class handles all /main/oembed(.xml|.json)/ requests.
- *
- * @category  oEmbed
- * @package   StatusNet
- * @author    Craig Andrews <candrews@integralblue.com>
- * @copyright 2008 StatusNet, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://status.net/
- */
-
-class OembedAction extends Action
-{
-
-    function handle($args)
-    {
-        common_debug("in oembed api action");
-
-        $url = $args['url'];
-        if( substr(strtolower($url),0,strlen(common_root_url())) == strtolower(common_root_url()) ){
-            $path = substr($url,strlen(common_root_url()));
-
-            $r = Router::get();
-
-            $proxy_args = $r->map($path);
-
-            if (!$proxy_args) {
-                // TRANS: Server error displayed in oEmbed action when path not found.
-                // TRANS: %s is a path.
-                $this->serverError(sprintf(_('"%s" not found.'),$path), 404);
-            }
-            $oembed=array();
-            $oembed['version']='1.0';
-            $oembed['provider_name']=common_config('site', 'name');
-            $oembed['provider_url']=common_root_url();
-            switch($proxy_args['action']){
-                case 'shownotice':
-                    $oembed['type']='link';
-                    $id = $proxy_args['notice'];
-                    $notice = Notice::getKV($id);
-                    if(empty($notice)){
-                        // TRANS: Server error displayed in oEmbed action when notice not found.
-                        // TRANS: %s is a notice.
-                        $this->serverError(sprintf(_("Notice %s not found."),$id), 404);
-                    }
-                    $profile = $notice->getProfile();
-                    if (empty($profile)) {
-                        // TRANS: Server error displayed in oEmbed action when notice has not profile.
-                        $this->serverError(_('Notice has no profile.'), 500);
-                    }
-                    $authorname = $profile->getFancyName();
-                    // TRANS: oEmbed title. %1$s is the author name, %2$s is the creation date.
-                    $oembed['title'] = sprintf(_('%1$s\'s status on %2$s'),
-                        $authorname,
-                        common_exact_date($notice->created));
-                    $oembed['author_name']=$authorname;
-                    $oembed['author_url']=$profile->profileurl;
-                    $oembed['url']=$notice->getUrl();
-                    $oembed['html']=$notice->rendered;
-                    break;
-                case 'attachment':
-                    $id = $proxy_args['attachment'];
-                    $attachment = File::getKV($id);
-                    if(empty($attachment)){
-                        // TRANS: Server error displayed in oEmbed action when attachment not found.
-                        // TRANS: %d is an attachment ID.
-                        $this->serverError(sprintf(_('Attachment %s not found.'),$id), 404);
-                    }
-                    if(empty($attachment->filename) && $file_oembed = File_oembed::getKV('file_id', $attachment->id)){
-                        // Proxy the existing oembed information
-                        $oembed['type']=$file_oembed->type;
-                        $oembed['provider']=$file_oembed->provider;
-                        $oembed['provider_url']=$file_oembed->provider_url;
-                        $oembed['width']=$file_oembed->width;
-                        $oembed['height']=$file_oembed->height;
-                        $oembed['html']=$file_oembed->html;
-                        $oembed['title']=$file_oembed->title;
-                        $oembed['author_name']=$file_oembed->author_name;
-                        $oembed['author_url']=$file_oembed->author_url;
-                        $oembed['url']=$file_oembed->getUrl();
-                    }else if(substr($attachment->mimetype,0,strlen('image/'))=='image/'){
-                        $oembed['type']='photo';
-                        if ($attachment->filename) {
-                            $filepath = File::path($attachment->filename);
-                            $gis = @getimagesize($filepath);
-                            if ($gis) {
-                                $oembed['width'] = $gis[0];
-                                $oembed['height'] = $gis[1];
-                            } else {
-                                // TODO Either throw an error or find a fallback?
-                            }
-                        }
-                        $oembed['url']=$attachment->getUrl();
-                        try {
-                            $thumb = $attachment->getThumbnail();
-                            $oembed['thumbnail_url'] = $thumb->getUrl();
-                            $oembed['thumbnail_width'] = $thumb->width;
-                            $oembed['thumbnail_height'] = $thumb->height;
-                            unset($thumb);
-                        } catch (UnsupportedMediaException $e) {
-                            // No thumbnail data available
-                        }
-                    }else{
-                        $oembed['type']='link';
-                        $oembed['url']=common_local_url('attachment',
-                            array('attachment' => $attachment->id));
-                    }
-                    if($attachment->title) $oembed['title']=$attachment->title;
-                    break;
-                default:
-                    // TRANS: Server error displayed in oEmbed request when a path is not supported.
-                    // TRANS: %s is a path.
-                    $this->serverError(sprintf(_('"%s" not supported for oembed requests.'),$path), 501);
-            }
-            switch($args['format']){
-                case 'xml':
-                    $this->init_document('xml');
-                    $this->elementStart('oembed');
-                    $this->element('version',null,$oembed['version']);
-                    $this->element('type',null,$oembed['type']);
-                    if($oembed['provider_name']) $this->element('provider_name',null,$oembed['provider_name']);
-                    if($oembed['provider_url']) $this->element('provider_url',null,$oembed['provider_url']);
-                    if($oembed['title']) $this->element('title',null,$oembed['title']);
-                    if($oembed['author_name']) $this->element('author_name',null,$oembed['author_name']);
-                    if($oembed['author_url']) $this->element('author_url',null,$oembed['author_url']);
-                    if($oembed['url']) $this->element('url',null,$oembed['url']);
-                    if($oembed['html']) $this->element('html',null,$oembed['html']);
-                    if($oembed['width']) $this->element('width',null,$oembed['width']);
-                    if($oembed['height']) $this->element('height',null,$oembed['height']);
-                    if($oembed['cache_age']) $this->element('cache_age',null,$oembed['cache_age']);
-                    if($oembed['thumbnail_url']) $this->element('thumbnail_url',null,$oembed['thumbnail_url']);
-                    if($oembed['thumbnail_width']) $this->element('thumbnail_width',null,$oembed['thumbnail_width']);
-                    if($oembed['thumbnail_height']) $this->element('thumbnail_height',null,$oembed['thumbnail_height']);
-
-                    $this->elementEnd('oembed');
-                    $this->end_document('xml');
-                    break;
-                case 'json': case '':
-                    $this->init_document('json');
-                    print(json_encode($oembed));
-                    $this->end_document('json');
-                    break;
-                default:
-                    // TRANS: Error message displaying attachments. %s is a raw MIME type (eg 'image/png')
-                    $this->serverError(sprintf(_('Content type %s not supported.'), $apidata['content-type']), 501);
-            }
-        }else{
-            // TRANS: Error message displaying attachments. %s is the site's base URL.
-            $this->serverError(sprintf(_('Only %s URLs over plain HTTP please.'), common_root_url()), 404);
-        }
-    }
-
-    function init_document($type)
-    {
-        switch ($type) {
-        case 'xml':
-            header('Content-Type: application/xml; charset=utf-8');
-            $this->startXML();
-            break;
-        case 'json':
-            header('Content-Type: application/json; charset=utf-8');
-
-            // Check for JSONP callback
-            $callback = $this->arg('callback');
-            if ($callback) {
-                print $callback . '(';
-            }
-            break;
-        default:
-            // TRANS: Server error displayed in oEmbed action when request specifies an unsupported data format.
-            $this->serverError(_('Not a supported data format.'), 501);
-            break;
-        }
-    }
-
-    function end_document($type='xml')
-    {
-        switch ($type) {
-        case 'xml':
-            $this->endXML();
-            break;
-        case 'json':
-            // Check for JSONP callback
-            $callback = $this->arg('callback');
-            if ($callback) {
-                print ')';
-            }
-            break;
-        default:
-            // TRANS: Server error displayed in oEmbed action when request specifies an unsupported data format.
-            $this->serverError(_('Not a supported data format.'), 501);
-            break;
-        }
-        return;
-    }
-
-    /**
-     * Is this action read-only?
-     *
-     * @param array $args other arguments
-     *
-     * @return boolean is read only action?
-     */
-    function isReadOnly($args)
-    {
-        return true;
-    }
-}
index ff39080eeb0cacd4c2cd1185e94cf9c83b27ac17..28cb68c121f224c9b1b93f4c3f5eb09ba1d212bd 100644 (file)
@@ -282,21 +282,6 @@ class ShownoticeAction extends Action
                                          'content' => $id->toString()));
         }
 
-        $this->element('link',array('rel'=>'alternate',
-            'type'=>'application/json+oembed',
-            'href'=>common_local_url(
-                'oembed',
-                array(),
-                array('format'=>'json','url'=>$this->notice->getUrl())),
-            'title'=>'oEmbed'),null);
-        $this->element('link',array('rel'=>'alternate',
-            'type'=>'text/xml+oembed',
-            'href'=>common_local_url(
-                'oembed',
-                array(),
-                array('format'=>'xml','url'=>$this->notice->getUrl())),
-            'title'=>'oEmbed'),null);
-
         // Extras to aid in sharing notices to Facebook
         $avatarUrl = $this->profile->avatarUrl(AVATAR_PROFILE_SIZE);
         $this->element('meta', array('property' => 'og:image',
index 29dc7b5492a841851c82923e894f70eac967e658..69aee7fcee1962c7b302dd893390e43eab469642 100644 (file)
@@ -75,62 +75,26 @@ class File extends Managed_DataObject
         // I don't know why we have to keep doing this but I'm adding this last check to avoid
         // uniqueness bugs.
 
-        $x = File::getKV('url', $given_url);
+        $file = File::getKV('url', $given_url);
         
-        if (!$x instanceof File) {
-            $x = new File;
-            $x->url = $given_url;
-            if (!empty($redir_data['protected'])) $x->protected = $redir_data['protected'];
-            if (!empty($redir_data['title'])) $x->title = $redir_data['title'];
-            if (!empty($redir_data['type'])) $x->mimetype = $redir_data['type'];
-            if (!empty($redir_data['size'])) $x->size = intval($redir_data['size']);
-            if (isset($redir_data['time']) && $redir_data['time'] > 0) $x->date = intval($redir_data['time']);
-            $file_id = $x->insert();
+        if (!$file instanceof File) {
+            $file = new File;
+            $file->url = $given_url;
+            if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected'];
+            if (!empty($redir_data['title'])) $file->title = $redir_data['title'];
+            if (!empty($redir_data['type'])) $file->mimetype = $redir_data['type'];
+            if (!empty($redir_data['size'])) $file->size = intval($redir_data['size']);
+            if (isset($redir_data['time']) && $redir_data['time'] > 0) $file->date = intval($redir_data['time']);
+            $file_id = $file->insert();
         }
 
-        $x->saveOembed($redir_data, $given_url);
-        return $x;
-    }
-
-    /**
-     * Save embedding information for this file, if applicable.
-     *
-     * Normally this won't need to be called manually, as File::saveNew()
-     * takes care of it.
-     *
-     * @param array $redir_data lookup data eg from File_redirection::where()
-     * @param string $given_url
-     * @return boolean success
-     */
-    public function saveOembed(array $redir_data, $given_url)
-    {
-        if (isset($redir_data['type'])
-                && (('text/html' === substr($redir_data['type'], 0, 9)
-                || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21)))) {
-            try {
-                $oembed_data = File_oembed::_getOembed($given_url);
-            } catch (Exception $e) {
-                return false;
-            }
-            if ($oembed_data === false) {
-                return false;
-            }
-            $fo = File_oembed::getKV('file_id', $this->id);
-
-            if ($fo instanceof File_oembed) {
-                common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file $file_id", __FILE__);
-            } else {
-                File_oembed::saveNew($oembed_data, $this->id);
-                return true;
-            }
-        }
-        return false;
+        Event::handle('EndFileSaveNew', array($file, $redir_data, $given_url));
+        return $file;
     }
 
     /**
      * Go look at a URL and possibly save data about it if it's new:
      * - follow redirect chains and store them in file_redirection
-     * - look up oEmbed data and save it in file_oembed
      * - if a thumbnail is available, save it in file_thumbnail
      * - save file record with basic info
      * - optionally save a file_to_post record
@@ -384,7 +348,7 @@ class File extends Managed_DataObject
         $enclosure->size=$this->size;
         $enclosure->mimetype=$this->mimetype;
 
-        if(! isset($this->filename)){
+        if (!isset($this->filename)) {
             $notEnclosureMimeTypes = array(null,'text/html','application/xhtml+xml');
             $mimetype = $this->mimetype;
             if($mimetype != null){
@@ -394,32 +358,8 @@ class File extends Managed_DataObject
             if($semicolon){
                 $mimetype = substr($mimetype,0,$semicolon);
             }
-            if(in_array($mimetype,$notEnclosureMimeTypes)){
-                // Never treat generic HTML links as an enclosure type!
-                // But if we have oEmbed info, we'll consider it golden.
-                $oembed = File_oembed::getKV('file_id',$this->id);
-                if($oembed && in_array($oembed->type, array('photo', 'video'))){
-                    $mimetype = strtolower($oembed->mimetype);
-                    $semicolon = strpos($mimetype,';');
-                    if($semicolon){
-                        $mimetype = substr($mimetype,0,$semicolon);
-                    }
-                    // @fixme uncertain if this is right.
-                    // we want to expose things like YouTube videos as
-                    // viewable attachments, but don't expose them as
-                    // downloadable enclosures.....?
-                    //if (in_array($mimetype, $notEnclosureMimeTypes)) {
-                    //    return false;
-                    //} else {
-                        if($oembed->mimetype) $enclosure->mimetype=$oembed->mimetype;
-                        if($oembed->url) $enclosure->url=$oembed->url;
-                        if($oembed->title) $enclosure->title=$oembed->title;
-                        if($oembed->modified) $enclosure->modified=$oembed->modified;
-                        unset($oembed->size);
-                    //}
-                } else {
-                    return false;
-                }
+            if (in_array($mimetype, $notEnclosureMimeTypes)) {
+                Event::handle('FileEnclosureMetadata', array($file, &$enclosure));
             }
         }
         return $enclosure;
diff --git a/classes/File_oembed.php b/classes/File_oembed.php
deleted file mode 100644 (file)
index cb8420e..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.     If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('GNUSOCIAL')) { exit(1); }
-
-/**
- * Table Definition for file_oembed
- */
-
-class File_oembed extends Managed_DataObject
-{
-    public $__table = 'file_oembed';                     // table name
-    public $file_id;                         // int(4)  primary_key not_null
-    public $version;                         // varchar(20)
-    public $type;                            // varchar(20)
-    public $mimetype;                        // varchar(50)
-    public $provider;                        // varchar(50)
-    public $provider_url;                    // varchar(255)
-    public $width;                           // int(4)
-    public $height;                          // int(4)
-    public $html;                            // text()
-    public $title;                           // varchar(255)
-    public $author_name;                     // varchar(50)
-    public $author_url;                      // varchar(255)
-    public $url;                             // varchar(255)
-    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
-
-    public static function schemaDef()
-    {
-        return array(
-            'fields' => array(
-                'file_id' => array('type' => 'int', 'not null' => true, 'description' => 'oEmbed for that URL/file'),
-                'version' => array('type' => 'varchar', 'length' => 20, 'description' => 'oEmbed spec. version'),
-                'type' => array('type' => 'varchar', 'length' => 20, 'description' => 'oEmbed type: photo, video, link, rich'),
-                'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'),
-                'provider' => array('type' => 'varchar', 'length' => 50, 'description' => 'name of this oEmbed provider'),
-                'provider_url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL of this oEmbed provider'),
-                'width' => array('type' => 'int', 'description' => 'width of oEmbed resource when available'),
-                'height' => array('type' => 'int', 'description' => 'height of oEmbed resource when available'),
-                'html' => array('type' => 'text', 'description' => 'html representation of this oEmbed resource when applicable'),
-                'title' => array('type' => 'varchar', 'length' => 255, 'description' => 'title of oEmbed resource when available'),
-                'author_name' => array('type' => 'varchar', 'length' => 50, 'description' => 'author name for this oEmbed resource'),
-                'author_url' => array('type' => 'varchar', 'length' => 255, 'description' => 'author URL for this oEmbed resource'),
-                'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL for this oEmbed resource when applicable (photo, link)'),
-                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
-            ),
-            'primary key' => array('file_id'),
-            'foreign keys' => array(
-                'file_oembed_file_id_fkey' => array('file', array('file_id' => 'id')),
-            ),
-        );
-    }
-
-    function _getOembed($url) {
-        $parameters = array(
-            'maxwidth' => common_config('thumbnail', 'width'),
-            'maxheight' => common_config('thumbnail', 'height'),
-        );
-        try {
-            return oEmbedHelper::getObject($url, $parameters);
-        } catch (Exception $e) {
-            common_log(LOG_INFO, "Error during oembed lookup for $url - " . $e->getMessage());
-            return false;
-        }
-    }
-
-    public function getUrl()
-    {
-        return $this->url;
-    }
-
-    /**
-     * Save embedding info for a new file.
-     *
-     * @param object $data Services_oEmbed_Object_*
-     * @param int $file_id
-     */
-    public static function saveNew($data, $file_id) {
-        $file_oembed = new File_oembed;
-        $file_oembed->file_id = $file_id;
-        if (!isset($data->version)) {
-            common_debug('DEBUGGING oEmbed: data->version undefined in variable $data: '.var_export($data, true));
-        }
-        $file_oembed->version = $data->version;
-        $file_oembed->type = $data->type;
-        if (!empty($data->provider_name)) $file_oembed->provider = $data->provider_name;
-        if (!empty($data->provider)) $file_oembed->provider = $data->provider;
-        if (!empty($data->provider_url)) $file_oembed->provider_url = $data->provider_url;
-        if (!empty($data->width)) $file_oembed->width = intval($data->width);
-        if (!empty($data->height)) $file_oembed->height = intval($data->height);
-        if (!empty($data->html)) $file_oembed->html = $data->html;
-        if (!empty($data->title)) $file_oembed->title = $data->title;
-        if (!empty($data->author_name)) $file_oembed->author_name = $data->author_name;
-        if (!empty($data->author_url)) $file_oembed->author_url = $data->author_url;
-        if (!empty($data->url)){
-            $file_oembed->url = $data->url;
-            $given_url = File_redirection::_canonUrl($file_oembed->url);
-            if (! empty($given_url)){
-                $file = File::getKV('url', $given_url);
-                if ($file instanceof File) {
-                    $file_oembed->mimetype = $file->mimetype;
-                } else {
-                    $file_redir = File_redirection::getKV('url', $given_url);
-                    if (empty($file_redir)) {
-                        $redir_data = File_redirection::where($given_url);
-                        $file_oembed->mimetype = $redir_data['type'];
-                    } else {
-                        $file_id = $file_redir->file_id;
-                    }
-                }
-            }
-        }
-        $file_oembed->insert();
-        if (!empty($data->thumbnail_url) || ($data->type == 'photo')) {
-            $ft = File_thumbnail::getKV('file_id', $file_id);
-            if ($ft instanceof File_thumbnail) {
-                common_log(LOG_WARNING, "Strangely, a File_thumbnail object exists for new file $file_id",
-                           __FILE__);
-            } else {
-                File_thumbnail::saveNew($data, $file_id);
-            }
-        }
-    }
-}
index 42021668d99684e8123394a2390ae5aaaecdcfd9..0bcccc6cffbbb52ecd8aa4b0100e2afe6cd93dda 100644 (file)
@@ -257,9 +257,6 @@ class File_redirection extends Managed_DataObject
                     // Save file and embedding data about it!
                     $file = File::saveNew($redir_data, $long_url);
                     $file_id = $file->id;
-                    if (!empty($redir_data['oembed']['json'])) {
-                        File_oembed::saveNew($redir_data['oembed']['json'], $file_id);
-                    }
                 } else if (is_string($redir_data)) {
                     // The file is a known redirect target.
                     $file = File::getKV('url', $redir_data);
index d435c03cd6e6376fb2f9e58996cc8cb99b83dfe4..61f8228cb5068292393f14229534c8e853af0ba6 100644 (file)
@@ -67,7 +67,6 @@ $classes = array('Schema_version',
                  'Group_inbox',
                  'Group_member',
                  'File',
-                 'File_oembed',
                  'File_redirection',
                  'File_thumbnail',
                  'File_to_post',
index e219847c702be29110b7a4a1e60bb30220bc578f..4d917dc69e1fe7d2581b1a604e9a1a10799cb1c8 100644 (file)
@@ -166,6 +166,11 @@ class Action extends HTMLOutputter // lawsuit
         return $this->scoped;
     }
 
+    public function getActionName()
+    {
+        return $this->action;
+    }
+
     /**
      * Show page, a template method.
      *
index 7f356f9527f24193ea41016aa2952442267a7ee0..66b03182b35bc3093b25a348d2bdcf0b19ef4ada 100644 (file)
@@ -134,8 +134,6 @@ class AttachmentListItem extends Widget
 
     var $attachment = null;
 
-    var $oembed = null;
-
     /**
      * @param File $attachment the attachment we will display
      */
@@ -143,21 +141,10 @@ class AttachmentListItem extends Widget
     {
         parent::__construct($out);
         $this->attachment  = $attachment;
-        $this->oembed = File_oembed::getKV('file_id', $this->attachment->id);
     }
 
     function title() {
-        if (empty($this->attachment->title)) {
-            if (empty($this->oembed->title)) {
-                $title = $this->attachment->filename;
-            } else {
-                $title = $this->oembed->title;
-            }
-        } else {
-            $title = $this->attachment->title;
-        }
-
-        return $title;
+        return $this->attachment->title ?: $this->attachment->filename;
     }
 
     function linkTitle() {
@@ -238,40 +225,19 @@ class AttachmentListItem extends Widget
 class Attachment extends AttachmentListItem
 {
     function showLink() {
-        $this->out->elementStart('div', array('id' => 'attachment_view',
-                                              'class' => 'hentry'));
-        $this->out->elementStart('div', 'entry-title');
-        $this->out->element('a', $this->linkAttr(), $this->linkTitle());
-        $this->out->elementEnd('div');
+        if (Event::handle('StartShowAttachmentLink', array($this->out, $this->attachment))) {
+            $this->out->elementStart('div', array('id' => 'attachment_view',
+                                                  'class' => 'hentry'));
+            $this->out->elementStart('div', 'entry-title');
+            $this->out->element('a', $this->linkAttr(), $this->linkTitle());
+            $this->out->elementEnd('div');
 
-        $this->out->elementStart('div', 'entry-content');
-        $this->showRepresentation();
-        $this->out->elementEnd('div');
-
-        if (!empty($this->oembed->author_name) || !empty($this->oembed->provider)) {
-            $this->out->elementStart('div', array('id' => 'oembed_info',
-                                                  'class' => 'entry-content'));
-            if (!empty($this->oembed->author_name)) {
-                $this->out->elementStart('div', 'fn vcard author');
-                if (empty($this->oembed->author_url)) {
-                    $this->out->text($this->oembed->author_name);
-                } else {
-                    $this->out->element('a', array('href' => $this->oembed->author_url,
-                                                   'class' => 'url'), $this->oembed->author_name);
-                }
-            }
-            if (!empty($this->oembed->provider)) {
-                $this->out->elementStart('div', 'fn vcard');
-                if (empty($this->oembed->provider_url)) {
-                    $this->out->text($this->oembed->provider);
-                } else {
-                    $this->out->element('a', array('href' => $this->oembed->provider_url,
-                                                   'class' => 'url'), $this->oembed->provider);
-                }
-            }
+            $this->out->elementStart('div', 'entry-content');
+            $this->showRepresentation();
+            $this->out->elementEnd('div');
+            Event::handle('EndShowAttachmentLink', array($this->out, $this->attachment));
             $this->out->elementEnd('div');
         }
-        $this->out->elementEnd('div');
     }
 
     function show() {
@@ -288,28 +254,7 @@ class Attachment extends AttachmentListItem
 
     function showRepresentation() {
         if (Event::handle('StartShowAttachmentRepresentation', array($this->out, $this->attachment))) {
-            if (!empty($this->oembed->type)) {
-                switch ($this->oembed->type) {
-                case 'rich':
-                case 'video':
-                case 'link':
-                    if (!empty($this->oembed->html)) {
-                        require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
-                        $config = array(
-                            'safe'=>1,
-                            'elements'=>'*+object+embed');
-                        $this->out->raw(htmLawed($this->oembed->html,$config));
-                    }
-                    break;
-
-                case 'photo':
-                    $this->out->element('img', array('src' => $this->oembed->url, 'width' => $this->oembed->width, 'height' => $this->oembed->height, 'alt' => 'alt'));
-                    break;
-
-                default:
-                    Event::handle('ShowUnsupportedAttachmentRepresentation', array($this->out, $this->attachment));
-                }
-            } elseif (!empty($this->attachment->mimetype)) {
+            if (!empty($this->attachment->mimetype)) {
                 switch ($this->attachment->mimetype) {
                 case 'image/gif':
                 case 'image/png':
index d2270fa6a562532505def19018a896b1c2a0b172..65301bbb454fd33b1a3b106c23a10d353a0e5af9 100644 (file)
@@ -303,6 +303,7 @@ $default =
                             'ClientSideShorten' => array(),
                             'EmailAuthentication' => array(),
                             'Event' => array(),
+                            'Oembed' => array(),
                             'OpenID' => array(),
                             'Poll' => array(),
                             'QnA' => array(),
diff --git a/lib/oembedhelper.php b/lib/oembedhelper.php
deleted file mode 100644 (file)
index fab1131..0000000
+++ /dev/null
@@ -1,363 +0,0 @@
-<?php
-/*
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008-2010, StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.     If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('STATUSNET')) {
-    exit(1);
-}
-
-
-/**
- * Utility class to wrap basic oEmbed lookups.
- *
- * Blacklisted hosts will use an alternate lookup method:
- *  - Twitpic
- *
- * Whitelisted hosts will use known oEmbed API endpoints:
- *  - Flickr, YFrog
- *
- * Sites that provide discovery links will use them directly; a bug
- * in use of discovery links with query strings is worked around.
- *
- * Others will fall back to oohembed (unless disabled).
- * The API endpoint can be configured or disabled through config
- * as 'oohembed'/'endpoint'.
- */
-class oEmbedHelper
-{
-    protected static $apiMap = array(
-        'flickr.com' => 'http://www.flickr.com/services/oembed/',
-        'yfrog.com' => 'http://www.yfrog.com/api/oembed',
-        'youtube.com' => 'http://www.youtube.com/oembed',
-        'viddler.com' => 'http://lab.viddler.com/services/oembed/',
-        'qik.com' => 'http://qik.com/api/oembed.json',
-        'revision3.com' => 'http://revision3.com/api/oembed/',
-        'hulu.com' => 'http://www.hulu.com/api/oembed.json',
-        'vimeo.com' => 'http://www.vimeo.com/api/oembed.json',
-        'my.opera.com' => 'http://my.opera.com/service/oembed',
-    );
-    protected static $functionMap = array(
-        'twitpic.com' => 'oEmbedHelper::twitPic',
-    );
-
-    /**
-     * Perform or fake an oEmbed lookup for the given resource.
-     *
-     * Some known hosts are whitelisted with API endpoints where we
-     * know they exist but autodiscovery data isn't available.
-     * If autodiscovery links are missing and we don't recognize the
-     * host, we'll pass it to noembed.com's public service which
-     * will either proxy or fake info on a lot of sites.
-     *
-     * A few hosts are blacklisted due to known problems with oohembed,
-     * in which case we'll look up the info another way and return
-     * equivalent data.
-     *
-     * Throws exceptions on failure.
-     *
-     * @param string $url
-     * @param array $params
-     * @return object
-     */
-    public static function getObject($url, $params=array())
-    {
-        $host = parse_url($url, PHP_URL_HOST);
-        if (substr($host, 0, 4) == 'www.') {
-            $host = substr($host, 4);
-        }
-
-        common_log(LOG_INFO, 'Checking for oEmbed data for ' . $url);
-
-        // You can fiddle with the order of discovery -- either skipping
-        // some types or re-ordering them.
-
-        $order = common_config('oembed', 'order');
-
-        foreach ($order as $method) {
-
-            switch ($method) {
-            case 'built-in':
-                common_log(LOG_INFO, 'Considering built-in oEmbed methods...');
-                // Blacklist: systems with no oEmbed API of their own, which are
-                // either missing from or broken on noembed.com's proxy.
-                // we know how to look data up in another way...
-                if (array_key_exists($host, self::$functionMap)) {
-                    common_log(LOG_INFO, 'We have a built-in method for ' . $host);
-                    $func = self::$functionMap[$host];
-                    return call_user_func($func, $url, $params);
-                }
-                break;
-            case 'well-known':
-                common_log(LOG_INFO, 'Considering well-known oEmbed endpoints...');
-                // Whitelist: known API endpoints for sites that don't provide discovery...
-                if (array_key_exists($host, self::$apiMap)) {
-                    $api = self::$apiMap[$host];
-                    common_log(LOG_INFO, 'Using well-known endpoint "' . $api . '" for "' . $host . '"');
-                    break 2;
-                }
-                break;
-            case 'discovery':
-                try {
-                    common_log(LOG_INFO, 'Trying to discover an oEmbed endpoint using link headers.');
-                    $api = self::discover($url);
-                    common_log(LOG_INFO, 'Found API endpoint ' . $api . ' for URL ' . $url);
-                    break 2;
-                } catch (Exception $e) {
-                    common_log(LOG_INFO, 'Could not find an oEmbed endpoint using link headers.');
-                    // Just ignore it!
-                }
-                break;
-            case 'service':
-                $api = common_config('oembed', 'endpoint');
-                common_log(LOG_INFO, 'Using service API endpoint ' . $api);
-                break 2;
-                break;
-            }
-        }
-
-        if (empty($api)) {
-            // TRANS: Server exception thrown in oEmbed action if no API endpoint is available.
-            throw new ServerException(_('No oEmbed API endpoint available.'));
-        }
-
-        return self::getObjectFrom($api, $url, $params);
-    }
-
-    /**
-     * Perform basic discovery.
-     * @return string
-     */
-    static function discover($url)
-    {
-        // @fixme ideally skip this for non-HTML stuff!
-        $body = self::http($url);
-        return self::discoverFromHTML($url, $body);
-    }
-
-    /**
-     * Partially ripped from OStatus' FeedDiscovery class.
-     *
-     * @param string $url source URL, used to resolve relative links
-     * @param string $body HTML body text
-     * @return mixed string with URL or false if no target found
-     */
-    static function discoverFromHTML($url, $body)
-    {
-        // DOMDocument::loadHTML may throw warnings on unrecognized elements,
-        // and notices on unrecognized namespaces.
-        $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
-        $dom = new DOMDocument();
-        $ok = $dom->loadHTML($body);
-        error_reporting($old);
-
-        if (!$ok) {
-            throw new oEmbedHelper_BadHtmlException();
-        }
-
-        // Ok... now on to the links!
-        $feeds = array(
-            'application/json+oembed' => false,
-        );
-
-        $nodes = $dom->getElementsByTagName('link');
-        for ($i = 0; $i < $nodes->length; $i++) {
-            $node = $nodes->item($i);
-            if ($node->hasAttributes()) {
-                $rel = $node->attributes->getNamedItem('rel');
-                $type = $node->attributes->getNamedItem('type');
-                $href = $node->attributes->getNamedItem('href');
-                if ($rel && $type && $href) {
-                    $rel = array_filter(explode(" ", $rel->value));
-                    $type = trim($type->value);
-                    $href = trim($href->value);
-
-                    if (in_array('alternate', $rel) && array_key_exists($type, $feeds) && empty($feeds[$type])) {
-                        // Save the first feed found of each type...
-                        $feeds[$type] = $href;
-                    }
-                }
-            }
-        }
-
-        // Return the highest-priority feed found
-        foreach ($feeds as $type => $url) {
-            if ($url) {
-                return $url;
-            }
-        }
-
-        throw new oEmbedHelper_DiscoveryException();
-    }
-
-    /**
-     * Actually do an oEmbed lookup to a particular API endpoint.
-     *
-     * @param string $api oEmbed API endpoint URL
-     * @param string $url target URL to look up info about
-     * @param array $params
-     * @return object
-     */
-    static function getObjectFrom($api, $url, $params=array())
-    {
-        $params['url'] = $url;
-        $params['format'] = 'json';
-        $key=common_config('oembed','apikey');
-        if(isset($key)) {
-            $params['key'] = common_config('oembed','apikey');
-        }
-        $data = self::json($api, $params);
-        return self::normalize($data);
-    }
-
-    /**
-     * Normalize oEmbed format.
-     *
-     * @param object $orig
-     * @return object
-     */
-    static function normalize($orig)
-    {
-        $data = clone($orig);
-
-        if (empty($data->type)) {
-            throw new Exception('Invalid oEmbed data: no type field.');
-        }
-
-        if ($data->type == 'image') {
-            // YFrog does this.
-            $data->type = 'photo';
-        }
-
-        if (isset($data->thumbnail_url)) {
-            if (!isset($data->thumbnail_width)) {
-                // !?!?!
-                $data->thumbnail_width = common_config('thumbnail', 'width');
-                $data->thumbnail_height = common_config('thumbnail', 'height');
-            }
-        }
-
-        return $data;
-    }
-
-    /**
-     * Using a local function for twitpic lookups, as oohembed's adapter
-     * doesn't return a valid result:
-     * http://code.google.com/p/oohembed/issues/detail?id=19
-     *
-     * This code fetches metadata from Twitpic's own API, and attempts
-     * to guess proper thumbnail size from the original's size.
-     *
-     * @todo respect maxwidth and maxheight params
-     *
-     * @param string $url
-     * @param array $params
-     * @return object
-     */
-    static function twitPic($url, $params=array())
-    {
-        $matches = array();
-        if (preg_match('!twitpic\.com/(\w+)!', $url, $matches)) {
-            $id = $matches[1];
-        } else {
-            throw new Exception("Invalid twitpic URL");
-        }
-
-        // Grab metadata from twitpic's API...
-        // http://dev.twitpic.com/docs/2/media_show
-        $data = self::json('http://api.twitpic.com/2/media/show.json',
-                array('id' => $id));
-        $oembed = (object)array('type' => 'photo',
-                                'url' => 'http://twitpic.com/show/full/' . $data->short_id,
-                                'width' => $data->width,
-                                'height' => $data->height);
-        if (!empty($data->message)) {
-            $oembed->title = $data->message;
-        }
-
-        // Thumbnail is cropped and scaled to 150x150 box:
-        // http://dev.twitpic.com/docs/thumbnails/
-        $thumbSize = 150;
-        $oembed->thumbnail_url = 'http://twitpic.com/show/thumb/' . $data->short_id;
-        $oembed->thumbnail_width = $thumbSize;
-        $oembed->thumbnail_height = $thumbSize;
-
-        return $oembed;
-    }
-
-    /**
-     * Fetch some URL and return JSON data.
-     *
-     * @param string $url
-     * @param array $params query-string params
-     * @return object
-     */
-    static protected function json($url, $params=array())
-    {
-        $data = self::http($url, $params);
-        return json_decode($data);
-    }
-
-    /**
-     * Hit some web API and return data on success.
-     * @param string $url
-     * @param array $params
-     * @return string
-     */
-    static protected function http($url, $params=array())
-    {
-        $client = HTTPClient::start();
-        if ($params) {
-            $query = http_build_query($params, null, '&');
-            if (strpos($url, '?') === false) {
-                $url .= '?' . $query;
-            } else {
-                $url .= '&' . $query;
-            }
-        }
-        $response = $client->get($url);
-        if ($response->isOk()) {
-            return $response->getBody();
-        } else {
-            throw new Exception('Bad HTTP response code: ' . $response->getStatus());
-        }
-    }
-}
-
-class oEmbedHelper_Exception extends Exception
-{
-    public function __construct($message = "", $code = 0, $previous = null)
-    {
-        parent::__construct($message, $code);
-    }
-}
-
-class oEmbedHelper_BadHtmlException extends oEmbedHelper_Exception
-{
-    function __construct($previous=null)
-    {
-        return parent::__construct('Bad HTML in discovery data.', 0, $previous);
-    }
-}
-
-class oEmbedHelper_DiscoveryException extends oEmbedHelper_Exception
-{
-    function __construct($previous=null)
-    {
-        return parent::__construct('No oEmbed discovery data.', 0, $previous);
-    }
-}
index 71bf4e8e82d5c9751125f912baef118e19185a24..3febbbeff44bee10f34e063215f91f03468db0a6 100644 (file)
@@ -168,9 +168,6 @@ class Router
 
             $m->connect('main/tagprofile', array('action' => 'tagprofile'));
 
-            $m->connect('main/oembed',
-                        array('action' => 'oembed'));
-
             $m->connect('main/xrds',
                         array('action' => 'publicxrds'));
 
diff --git a/plugins/Bookmark/README b/plugins/Bookmark/README
new file mode 100644 (file)
index 0000000..e853760
--- /dev/null
@@ -0,0 +1 @@
+Depends on the oEmbed plugin (Oembed).
diff --git a/plugins/LinkPreview/README b/plugins/LinkPreview/README
new file mode 100644 (file)
index 0000000..a1b1a87
--- /dev/null
@@ -0,0 +1 @@
+Depends on the oEmbed plugin (Oembed)
diff --git a/plugins/Oembed/CONFIGURE b/plugins/Oembed/CONFIGURE
new file mode 100644 (file)
index 0000000..b8d24f9
--- /dev/null
@@ -0,0 +1,18 @@
+So far this is still in $config['site']['oembed'].
+
+oembed
+--------
+
+oEmbed endpoint for multimedia attachments (links in posts). Will also
+work as 'oohembed' for backwards compatibility.
+
+endpoint: oohembed endpoint using http://oohembed.com/ software. Defaults to
+         'http://oohembed.com/oohembed/'.
+order: Array of methods to check for OEmbed data. Methods include 'built-in'
+       (use a built-in function to simulate oEmbed for some sites),
+       'well-known' (use well-known public oEmbed endpoints),
+       'discovery' (discover using <link> headers in HTML), 'service' (use
+       a third-party service, like oohembed or embed.ly. Default is
+       array('built-in', 'well-known', 'service', 'discovery'). Note that very
+       few sites implement oEmbed; 'discovery' is going to fail 99% of the
+       time.
diff --git a/plugins/Oembed/OembedPlugin.php b/plugins/Oembed/OembedPlugin.php
new file mode 100644 (file)
index 0000000..177fd6e
--- /dev/null
@@ -0,0 +1,187 @@
+<?php
+
+class OembedPlugin extends Plugin
+{
+    public function onCheckSchema()
+    {
+        $schema = Schema::get();
+        $schema->ensureTable('file_oembed', File_oembed::schemaDef());
+        return true;
+    }
+
+    public function onRouterInitialized(URLMapper $m)
+    {
+        $m->connect('main/oembed', array('action' => 'oembed'));
+    }
+
+    public function onEndShowHeadElements(Action $action)
+    {
+        switch ($action->getActionName()) {
+        case 'attachment':
+            $action->element('link',array('rel'=>'alternate',
+                'type'=>'application/json+oembed',
+                'href'=>common_local_url(
+                    'oembed',
+                    array(),
+                    array('format'=>'json', 'url'=>
+                        common_local_url('attachment',
+                            array('attachment' => $action->attachment->id)))),
+                'title'=>'oEmbed'),null);
+            $action->element('link',array('rel'=>'alternate',
+                'type'=>'text/xml+oembed',
+                'href'=>common_local_url(
+                    'oembed',
+                    array(),
+                    array('format'=>'xml','url'=>
+                        common_local_url('attachment',
+                            array('attachment' => $action->attachment->id)))),
+                'title'=>'oEmbed'),null);
+            break;
+        case 'shownotice':
+            $this->element('link',array('rel'=>'alternate',
+                'type'=>'application/json+oembed',
+                'href'=>common_local_url(
+                    'oembed',
+                    array(),
+                    array('format'=>'json','url'=>$this->notice->getUrl())),
+                'title'=>'oEmbed'),null);
+            $this->element('link',array('rel'=>'alternate',
+                'type'=>'text/xml+oembed',
+                'href'=>common_local_url(
+                    'oembed',
+                    array(),
+                    array('format'=>'xml','url'=>$this->notice->getUrl())),
+                'title'=>'oEmbed'),null);
+            break;
+        }
+
+        return true;
+    }
+
+    /**
+     * Save embedding information for a File, if applicable.
+     *
+     * Normally this event is called through File::saveNew()
+     *
+     * @param File   $file       The newly inserted File object.
+     * @param array  $redir_data lookup data eg from File_redirection::where()
+     * @param string $given_url
+     *
+     * @return boolean success
+     */
+    public function onEndFileSaveNew(File $file, array $redir_data, $given_url)
+    {
+        $fo = File_oembed::getKV('file_id', $file->id);
+        if ($fo instanceof File_oembed) {
+            common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file $file_id", __FILE__);
+             return true;
+        }
+
+        if (isset($redir_data['oembed']['json'])
+                && !empty($redir_data['oembed']['json'])) {
+            File_oembed::saveNew($redir_data['oembed']['json'], $file->id);
+        } elseif (isset($redir_data['type'])
+                && (('text/html' === substr($redir_data['type'], 0, 9)
+                || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21)))) {
+
+            try {
+                $oembed_data = File_oembed::_getOembed($given_url);
+                if ($oembed_data === false) {
+                    throw new Exception('Did not get oEmbed data from URL');
+                }
+            } catch (Exception $e) {
+                return true;
+            }
+
+            File_oembed::saveNew($oembed_data, $file->id);
+        }
+        return true;
+    }
+
+    public function onEndShowAttachmentLink(HTMLOutputter $out, File $file)
+    {
+        $oembed = File_oembed::getKV('file_id', $file->id);
+        if (empty($oembed->author_name) && empty($oembed->provider)) {
+            return true;
+        }
+        $out->elementStart('div', array('id'=>'oembed_info', 'class'=>'entry-content'));
+        if (!empty($oembed->author_name)) {
+            $out->elementStart('div', 'fn vcard author');
+            if (empty($oembed->author_url)) {
+                $out->text($oembed->author_name);
+            } else {
+                $out->element('a', array('href' => $oembed->author_url,
+                                         'class' => 'url'),
+                                $oembed->author_name);
+            }
+        }
+        if (!empty($oembed->provider)) {
+            $out->elementStart('div', 'fn vcard');
+            if (empty($oembed->provider_url)) {
+                $out->text($oembed->provider);
+            } else {
+                $out->element('a', array('href' => $oembed->provider_url,
+                                         'class' => 'url'),
+                                $oembed->provider);
+            }
+        }
+        $out->elementEnd('div');
+    }
+
+    public function onFileEnclosureMetadata(File $file, &$enclosure)
+    {
+        // Never treat generic HTML links as an enclosure type!
+        // But if we have oEmbed info, we'll consider it golden.
+        $oembed = File_oembed::getKV('file_id', $file->id);
+        if (!$oembed instanceof File_oembed || !in_array($oembed->type, array('photo', 'video'))) {
+            return true;
+        }
+
+        foreach (array('mimetype', 'url', 'title', 'modified') as $key) {
+            if (!empty($oembed->{$key})) {
+                $enclosure->{$key} = $oembed->{$key};
+            }
+        }
+        return true;
+    }
+    
+    public function onStartShowAttachmentRepresentation(HTMLOutputter $out, File $file)
+    {
+        $oembed = File_oembed::getKV('file_id', $file->id);
+        if (empty($oembed->type)) {
+            return true;
+        }
+        switch ($oembed->type) {
+        case 'rich':
+        case 'video':
+        case 'link':
+            if (!empty($oembed->html)) {
+                require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
+                $config = array(
+                    'safe'=>1,
+                    'elements'=>'*+object+embed');
+                $out->raw(htmLawed($oembed->html,$config));
+            }
+            break;
+
+        case 'photo':
+            $out->element('img', array('src' => $oembed->url, 'width' => $oembed->width, 'height' => $oembed->height, 'alt' => 'alt'));
+            break;
+
+        default:
+            Event::handle('ShowUnsupportedAttachmentRepresentation', array($out, $file));
+        }
+    }
+
+    public function onPluginVersion(array &$versions)
+    {
+        $versions[] = array('name' => 'Oembed',
+                            'version' => GNUSOCIAL_VERSION,
+                            'author' => 'Mikael Nordfeldth',
+                            'homepage' => 'http://gnu.io/',
+                            'description' =>
+                            // TRANS: Plugin description.
+                            _m('Plugin for using and representing Oembed data.'));
+        return true;
+    }
+}
diff --git a/plugins/Oembed/README b/plugins/Oembed/README
new file mode 100644 (file)
index 0000000..be8c09f
--- /dev/null
@@ -0,0 +1 @@
+It's really called oEmbed.
diff --git a/plugins/Oembed/actions/oembed.php b/plugins/Oembed/actions/oembed.php
new file mode 100644 (file)
index 0000000..509c3a7
--- /dev/null
@@ -0,0 +1,236 @@
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * oEmbed data action for /main/oembed(.xml|.json) requests
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+/**
+ * Oembed provider implementation
+ *
+ * This class handles all /main/oembed(.xml|.json)/ requests.
+ *
+ * @category  oEmbed
+ * @package   GNUsocial
+ * @author    Craig Andrews <candrews@integralblue.com>
+ * @author    Mikael Nordfeldth <mmn@hethane.se>
+ * @copyright 2008 StatusNet, Inc.
+ * @copyright 2014 Free Software Foundation, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://status.net/
+ */
+
+class OembedAction extends Action
+{
+    protected function handle()
+    {
+        parent::handle();
+
+        $url = $this->trimmed('url');
+        if (substr(strtolower($url),0,strlen(common_root_url())) == strtolower(common_root_url())) {
+            $path = substr($url,strlen(common_root_url()));
+
+            $r = Router::get();
+
+            $proxy_args = $r->map($path);
+            if (!$proxy_args) {
+                // TRANS: Server error displayed in oEmbed action when path not found.
+                // TRANS: %s is a path.
+                $this->serverError(sprintf(_('"%s" not found.'),$path), 404);
+            }
+
+            $oembed=array();
+            $oembed['version']='1.0';
+            $oembed['provider_name']=common_config('site', 'name');
+            $oembed['provider_url']=common_root_url();
+            
+            switch ($proxy_args['action']) {
+            case 'shownotice':
+                $oembed['type']='link';
+                $id = $proxy_args['notice'];
+                $notice = Notice::getKV($id);
+                if(empty($notice)){
+                    // TRANS: Server error displayed in oEmbed action when notice not found.
+                    // TRANS: %s is a notice.
+                    $this->serverError(sprintf(_("Notice %s not found."),$id), 404);
+                }
+                $profile = $notice->getProfile();
+                if (empty($profile)) {
+                    // TRANS: Server error displayed in oEmbed action when notice has not profile.
+                    $this->serverError(_('Notice has no profile.'), 500);
+                }
+                $authorname = $profile->getFancyName();
+                // TRANS: oEmbed title. %1$s is the author name, %2$s is the creation date.
+                $oembed['title'] = sprintf(_('%1$s\'s status on %2$s'),
+                    $authorname,
+                    common_exact_date($notice->created));
+                $oembed['author_name']=$authorname;
+                $oembed['author_url']=$profile->profileurl;
+                $oembed['url']=$notice->getUrl();
+                $oembed['html']=$notice->rendered;
+                break;
+
+            case 'attachment':
+                $id = $proxy_args['attachment'];
+                $attachment = File::getKV($id);
+                if(empty($attachment)){
+                    // TRANS: Server error displayed in oEmbed action when attachment not found.
+                    // TRANS: %d is an attachment ID.
+                    $this->serverError(sprintf(_('Attachment %s not found.'),$id), 404);
+                }
+                if (empty($attachment->filename) && $file_oembed = File_oembed::getKV('file_id', $attachment->id)) {
+                    // Proxy the existing oembed information
+                    $oembed['type']=$file_oembed->type;
+                    $oembed['provider']=$file_oembed->provider;
+                    $oembed['provider_url']=$file_oembed->provider_url;
+                    $oembed['width']=$file_oembed->width;
+                    $oembed['height']=$file_oembed->height;
+                    $oembed['html']=$file_oembed->html;
+                    $oembed['title']=$file_oembed->title;
+                    $oembed['author_name']=$file_oembed->author_name;
+                    $oembed['author_url']=$file_oembed->author_url;
+                    $oembed['url']=$file_oembed->getUrl();
+                } elseif (substr($attachment->mimetype,0,strlen('image/'))==='image/') {
+                    $oembed['type']='photo';
+                    if ($attachment->filename) {
+                        $filepath = File::path($attachment->filename);
+                        $gis = @getimagesize($filepath);
+                        if ($gis) {
+                            $oembed['width'] = $gis[0];
+                            $oembed['height'] = $gis[1];
+                        } else {
+                            // TODO Either throw an error or find a fallback?
+                        }
+                    }
+                    $oembed['url']=$attachment->getUrl();
+                    try {
+                        $thumb = $attachment->getThumbnail();
+                        $oembed['thumbnail_url'] = $thumb->getUrl();
+                        $oembed['thumbnail_width'] = $thumb->width;
+                        $oembed['thumbnail_height'] = $thumb->height;
+                        unset($thumb);
+                    } catch (UnsupportedMediaException $e) {
+                        // No thumbnail data available
+                    }
+                } else {
+                    $oembed['type']='link';
+                    $oembed['url']=common_local_url('attachment',
+                        array('attachment' => $attachment->id));
+                }
+                if ($attachment->title) {
+                    $oembed['title']=$attachment->title;
+                }
+                break;
+            default:
+                // TRANS: Server error displayed in oEmbed request when a path is not supported.
+                // TRANS: %s is a path.
+                $this->serverError(sprintf(_('"%s" not supported for oembed requests.'),$path), 501);
+            }
+            switch ($this->trimmed('format')) {
+            case 'xml':
+                $this->init_document('xml');
+                $this->elementStart('oembed');
+                foreach(array(
+                            'version', 'type', 'provider_name',
+                            'provider_url', 'title', 'author_name',
+                            'author_url', 'url', 'html', 'width',
+                            'height', 'cache_age', 'thumbnail_url',
+                            'thumbnail_width', 'thumbnail_height',
+                        ) as $key) {
+                    if (isset($oembed[$key]) && $oembed[$key]!='') {
+                        $this->element($key, null, $oembed[$key]);
+                    }
+                }
+                $this->elementEnd('oembed');
+                $this->end_document('xml');
+                break;
+
+            case 'json':
+            case null:
+                $this->init_document('json');
+                $this->raw(json_encode($oembed));
+                $this->end_document('json');
+                break;
+            default:
+                // TRANS: Error message displaying attachments. %s is a raw MIME type (eg 'image/png')
+                $this->serverError(sprintf(_('Content type %s not supported.'), $apidata['content-type']), 501);
+            }
+        } else {
+            // TRANS: Error message displaying attachments. %s is the site's base URL.
+            $this->serverError(sprintf(_('Only %s URLs over plain HTTP please.'), common_root_url()), 404);
+        }
+    }
+
+    public function init_document($type)
+    {
+        switch ($type) {
+        case 'xml':
+            header('Content-Type: application/xml; charset=utf-8');
+            $this->startXML();
+            break;
+        case 'json':
+            header('Content-Type: application/json; charset=utf-8');
+
+            // Check for JSONP callback
+            $callback = $this->arg('callback');
+            if ($callback) {
+                print $callback . '(';
+            }
+            break;
+        default:
+            // TRANS: Server error displayed in oEmbed action when request specifies an unsupported data format.
+            $this->serverError(_('Not a supported data format.'), 501);
+            break;
+        }
+    }
+
+    public function end_document($type)
+    {
+        switch ($type) {
+        case 'xml':
+            $this->endXML();
+            break;
+        case 'json':
+            // Check for JSONP callback
+            $callback = $this->arg('callback');
+            if ($callback) {
+                print ')';
+            }
+            break;
+        default:
+            // TRANS: Server error displayed in oEmbed action when request specifies an unsupported data format.
+            $this->serverError(_('Not a supported data format.'), 501);
+            break;
+        }
+        return;
+    }
+
+    /**
+     * Is this action read-only?
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return true;
+    }
+}
diff --git a/plugins/Oembed/classes/File_oembed.php b/plugins/Oembed/classes/File_oembed.php
new file mode 100644 (file)
index 0000000..cb8420e
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.     If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('GNUSOCIAL')) { exit(1); }
+
+/**
+ * Table Definition for file_oembed
+ */
+
+class File_oembed extends Managed_DataObject
+{
+    public $__table = 'file_oembed';                     // table name
+    public $file_id;                         // int(4)  primary_key not_null
+    public $version;                         // varchar(20)
+    public $type;                            // varchar(20)
+    public $mimetype;                        // varchar(50)
+    public $provider;                        // varchar(50)
+    public $provider_url;                    // varchar(255)
+    public $width;                           // int(4)
+    public $height;                          // int(4)
+    public $html;                            // text()
+    public $title;                           // varchar(255)
+    public $author_name;                     // varchar(50)
+    public $author_url;                      // varchar(255)
+    public $url;                             // varchar(255)
+    public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
+
+    public static function schemaDef()
+    {
+        return array(
+            'fields' => array(
+                'file_id' => array('type' => 'int', 'not null' => true, 'description' => 'oEmbed for that URL/file'),
+                'version' => array('type' => 'varchar', 'length' => 20, 'description' => 'oEmbed spec. version'),
+                'type' => array('type' => 'varchar', 'length' => 20, 'description' => 'oEmbed type: photo, video, link, rich'),
+                'mimetype' => array('type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'),
+                'provider' => array('type' => 'varchar', 'length' => 50, 'description' => 'name of this oEmbed provider'),
+                'provider_url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL of this oEmbed provider'),
+                'width' => array('type' => 'int', 'description' => 'width of oEmbed resource when available'),
+                'height' => array('type' => 'int', 'description' => 'height of oEmbed resource when available'),
+                'html' => array('type' => 'text', 'description' => 'html representation of this oEmbed resource when applicable'),
+                'title' => array('type' => 'varchar', 'length' => 255, 'description' => 'title of oEmbed resource when available'),
+                'author_name' => array('type' => 'varchar', 'length' => 50, 'description' => 'author name for this oEmbed resource'),
+                'author_url' => array('type' => 'varchar', 'length' => 255, 'description' => 'author URL for this oEmbed resource'),
+                'url' => array('type' => 'varchar', 'length' => 255, 'description' => 'URL for this oEmbed resource when applicable (photo, link)'),
+                'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
+            ),
+            'primary key' => array('file_id'),
+            'foreign keys' => array(
+                'file_oembed_file_id_fkey' => array('file', array('file_id' => 'id')),
+            ),
+        );
+    }
+
+    function _getOembed($url) {
+        $parameters = array(
+            'maxwidth' => common_config('thumbnail', 'width'),
+            'maxheight' => common_config('thumbnail', 'height'),
+        );
+        try {
+            return oEmbedHelper::getObject($url, $parameters);
+        } catch (Exception $e) {
+            common_log(LOG_INFO, "Error during oembed lookup for $url - " . $e->getMessage());
+            return false;
+        }
+    }
+
+    public function getUrl()
+    {
+        return $this->url;
+    }
+
+    /**
+     * Save embedding info for a new file.
+     *
+     * @param object $data Services_oEmbed_Object_*
+     * @param int $file_id
+     */
+    public static function saveNew($data, $file_id) {
+        $file_oembed = new File_oembed;
+        $file_oembed->file_id = $file_id;
+        if (!isset($data->version)) {
+            common_debug('DEBUGGING oEmbed: data->version undefined in variable $data: '.var_export($data, true));
+        }
+        $file_oembed->version = $data->version;
+        $file_oembed->type = $data->type;
+        if (!empty($data->provider_name)) $file_oembed->provider = $data->provider_name;
+        if (!empty($data->provider)) $file_oembed->provider = $data->provider;
+        if (!empty($data->provider_url)) $file_oembed->provider_url = $data->provider_url;
+        if (!empty($data->width)) $file_oembed->width = intval($data->width);
+        if (!empty($data->height)) $file_oembed->height = intval($data->height);
+        if (!empty($data->html)) $file_oembed->html = $data->html;
+        if (!empty($data->title)) $file_oembed->title = $data->title;
+        if (!empty($data->author_name)) $file_oembed->author_name = $data->author_name;
+        if (!empty($data->author_url)) $file_oembed->author_url = $data->author_url;
+        if (!empty($data->url)){
+            $file_oembed->url = $data->url;
+            $given_url = File_redirection::_canonUrl($file_oembed->url);
+            if (! empty($given_url)){
+                $file = File::getKV('url', $given_url);
+                if ($file instanceof File) {
+                    $file_oembed->mimetype = $file->mimetype;
+                } else {
+                    $file_redir = File_redirection::getKV('url', $given_url);
+                    if (empty($file_redir)) {
+                        $redir_data = File_redirection::where($given_url);
+                        $file_oembed->mimetype = $redir_data['type'];
+                    } else {
+                        $file_id = $file_redir->file_id;
+                    }
+                }
+            }
+        }
+        $file_oembed->insert();
+        if (!empty($data->thumbnail_url) || ($data->type == 'photo')) {
+            $ft = File_thumbnail::getKV('file_id', $file_id);
+            if ($ft instanceof File_thumbnail) {
+                common_log(LOG_WARNING, "Strangely, a File_thumbnail object exists for new file $file_id",
+                           __FILE__);
+            } else {
+                File_thumbnail::saveNew($data, $file_id);
+            }
+        }
+    }
+}
diff --git a/plugins/Oembed/lib/oembedhelper.php b/plugins/Oembed/lib/oembedhelper.php
new file mode 100644 (file)
index 0000000..fab1131
--- /dev/null
@@ -0,0 +1,363 @@
+<?php
+/*
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2008-2010, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.     If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+
+/**
+ * Utility class to wrap basic oEmbed lookups.
+ *
+ * Blacklisted hosts will use an alternate lookup method:
+ *  - Twitpic
+ *
+ * Whitelisted hosts will use known oEmbed API endpoints:
+ *  - Flickr, YFrog
+ *
+ * Sites that provide discovery links will use them directly; a bug
+ * in use of discovery links with query strings is worked around.
+ *
+ * Others will fall back to oohembed (unless disabled).
+ * The API endpoint can be configured or disabled through config
+ * as 'oohembed'/'endpoint'.
+ */
+class oEmbedHelper
+{
+    protected static $apiMap = array(
+        'flickr.com' => 'http://www.flickr.com/services/oembed/',
+        'yfrog.com' => 'http://www.yfrog.com/api/oembed',
+        'youtube.com' => 'http://www.youtube.com/oembed',
+        'viddler.com' => 'http://lab.viddler.com/services/oembed/',
+        'qik.com' => 'http://qik.com/api/oembed.json',
+        'revision3.com' => 'http://revision3.com/api/oembed/',
+        'hulu.com' => 'http://www.hulu.com/api/oembed.json',
+        'vimeo.com' => 'http://www.vimeo.com/api/oembed.json',
+        'my.opera.com' => 'http://my.opera.com/service/oembed',
+    );
+    protected static $functionMap = array(
+        'twitpic.com' => 'oEmbedHelper::twitPic',
+    );
+
+    /**
+     * Perform or fake an oEmbed lookup for the given resource.
+     *
+     * Some known hosts are whitelisted with API endpoints where we
+     * know they exist but autodiscovery data isn't available.
+     * If autodiscovery links are missing and we don't recognize the
+     * host, we'll pass it to noembed.com's public service which
+     * will either proxy or fake info on a lot of sites.
+     *
+     * A few hosts are blacklisted due to known problems with oohembed,
+     * in which case we'll look up the info another way and return
+     * equivalent data.
+     *
+     * Throws exceptions on failure.
+     *
+     * @param string $url
+     * @param array $params
+     * @return object
+     */
+    public static function getObject($url, $params=array())
+    {
+        $host = parse_url($url, PHP_URL_HOST);
+        if (substr($host, 0, 4) == 'www.') {
+            $host = substr($host, 4);
+        }
+
+        common_log(LOG_INFO, 'Checking for oEmbed data for ' . $url);
+
+        // You can fiddle with the order of discovery -- either skipping
+        // some types or re-ordering them.
+
+        $order = common_config('oembed', 'order');
+
+        foreach ($order as $method) {
+
+            switch ($method) {
+            case 'built-in':
+                common_log(LOG_INFO, 'Considering built-in oEmbed methods...');
+                // Blacklist: systems with no oEmbed API of their own, which are
+                // either missing from or broken on noembed.com's proxy.
+                // we know how to look data up in another way...
+                if (array_key_exists($host, self::$functionMap)) {
+                    common_log(LOG_INFO, 'We have a built-in method for ' . $host);
+                    $func = self::$functionMap[$host];
+                    return call_user_func($func, $url, $params);
+                }
+                break;
+            case 'well-known':
+                common_log(LOG_INFO, 'Considering well-known oEmbed endpoints...');
+                // Whitelist: known API endpoints for sites that don't provide discovery...
+                if (array_key_exists($host, self::$apiMap)) {
+                    $api = self::$apiMap[$host];
+                    common_log(LOG_INFO, 'Using well-known endpoint "' . $api . '" for "' . $host . '"');
+                    break 2;
+                }
+                break;
+            case 'discovery':
+                try {
+                    common_log(LOG_INFO, 'Trying to discover an oEmbed endpoint using link headers.');
+                    $api = self::discover($url);
+                    common_log(LOG_INFO, 'Found API endpoint ' . $api . ' for URL ' . $url);
+                    break 2;
+                } catch (Exception $e) {
+                    common_log(LOG_INFO, 'Could not find an oEmbed endpoint using link headers.');
+                    // Just ignore it!
+                }
+                break;
+            case 'service':
+                $api = common_config('oembed', 'endpoint');
+                common_log(LOG_INFO, 'Using service API endpoint ' . $api);
+                break 2;
+                break;
+            }
+        }
+
+        if (empty($api)) {
+            // TRANS: Server exception thrown in oEmbed action if no API endpoint is available.
+            throw new ServerException(_('No oEmbed API endpoint available.'));
+        }
+
+        return self::getObjectFrom($api, $url, $params);
+    }
+
+    /**
+     * Perform basic discovery.
+     * @return string
+     */
+    static function discover($url)
+    {
+        // @fixme ideally skip this for non-HTML stuff!
+        $body = self::http($url);
+        return self::discoverFromHTML($url, $body);
+    }
+
+    /**
+     * Partially ripped from OStatus' FeedDiscovery class.
+     *
+     * @param string $url source URL, used to resolve relative links
+     * @param string $body HTML body text
+     * @return mixed string with URL or false if no target found
+     */
+    static function discoverFromHTML($url, $body)
+    {
+        // DOMDocument::loadHTML may throw warnings on unrecognized elements,
+        // and notices on unrecognized namespaces.
+        $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
+        $dom = new DOMDocument();
+        $ok = $dom->loadHTML($body);
+        error_reporting($old);
+
+        if (!$ok) {
+            throw new oEmbedHelper_BadHtmlException();
+        }
+
+        // Ok... now on to the links!
+        $feeds = array(
+            'application/json+oembed' => false,
+        );
+
+        $nodes = $dom->getElementsByTagName('link');
+        for ($i = 0; $i < $nodes->length; $i++) {
+            $node = $nodes->item($i);
+            if ($node->hasAttributes()) {
+                $rel = $node->attributes->getNamedItem('rel');
+                $type = $node->attributes->getNamedItem('type');
+                $href = $node->attributes->getNamedItem('href');
+                if ($rel && $type && $href) {
+                    $rel = array_filter(explode(" ", $rel->value));
+                    $type = trim($type->value);
+                    $href = trim($href->value);
+
+                    if (in_array('alternate', $rel) && array_key_exists($type, $feeds) && empty($feeds[$type])) {
+                        // Save the first feed found of each type...
+                        $feeds[$type] = $href;
+                    }
+                }
+            }
+        }
+
+        // Return the highest-priority feed found
+        foreach ($feeds as $type => $url) {
+            if ($url) {
+                return $url;
+            }
+        }
+
+        throw new oEmbedHelper_DiscoveryException();
+    }
+
+    /**
+     * Actually do an oEmbed lookup to a particular API endpoint.
+     *
+     * @param string $api oEmbed API endpoint URL
+     * @param string $url target URL to look up info about
+     * @param array $params
+     * @return object
+     */
+    static function getObjectFrom($api, $url, $params=array())
+    {
+        $params['url'] = $url;
+        $params['format'] = 'json';
+        $key=common_config('oembed','apikey');
+        if(isset($key)) {
+            $params['key'] = common_config('oembed','apikey');
+        }
+        $data = self::json($api, $params);
+        return self::normalize($data);
+    }
+
+    /**
+     * Normalize oEmbed format.
+     *
+     * @param object $orig
+     * @return object
+     */
+    static function normalize($orig)
+    {
+        $data = clone($orig);
+
+        if (empty($data->type)) {
+            throw new Exception('Invalid oEmbed data: no type field.');
+        }
+
+        if ($data->type == 'image') {
+            // YFrog does this.
+            $data->type = 'photo';
+        }
+
+        if (isset($data->thumbnail_url)) {
+            if (!isset($data->thumbnail_width)) {
+                // !?!?!
+                $data->thumbnail_width = common_config('thumbnail', 'width');
+                $data->thumbnail_height = common_config('thumbnail', 'height');
+            }
+        }
+
+        return $data;
+    }
+
+    /**
+     * Using a local function for twitpic lookups, as oohembed's adapter
+     * doesn't return a valid result:
+     * http://code.google.com/p/oohembed/issues/detail?id=19
+     *
+     * This code fetches metadata from Twitpic's own API, and attempts
+     * to guess proper thumbnail size from the original's size.
+     *
+     * @todo respect maxwidth and maxheight params
+     *
+     * @param string $url
+     * @param array $params
+     * @return object
+     */
+    static function twitPic($url, $params=array())
+    {
+        $matches = array();
+        if (preg_match('!twitpic\.com/(\w+)!', $url, $matches)) {
+            $id = $matches[1];
+        } else {
+            throw new Exception("Invalid twitpic URL");
+        }
+
+        // Grab metadata from twitpic's API...
+        // http://dev.twitpic.com/docs/2/media_show
+        $data = self::json('http://api.twitpic.com/2/media/show.json',
+                array('id' => $id));
+        $oembed = (object)array('type' => 'photo',
+                                'url' => 'http://twitpic.com/show/full/' . $data->short_id,
+                                'width' => $data->width,
+                                'height' => $data->height);
+        if (!empty($data->message)) {
+            $oembed->title = $data->message;
+        }
+
+        // Thumbnail is cropped and scaled to 150x150 box:
+        // http://dev.twitpic.com/docs/thumbnails/
+        $thumbSize = 150;
+        $oembed->thumbnail_url = 'http://twitpic.com/show/thumb/' . $data->short_id;
+        $oembed->thumbnail_width = $thumbSize;
+        $oembed->thumbnail_height = $thumbSize;
+
+        return $oembed;
+    }
+
+    /**
+     * Fetch some URL and return JSON data.
+     *
+     * @param string $url
+     * @param array $params query-string params
+     * @return object
+     */
+    static protected function json($url, $params=array())
+    {
+        $data = self::http($url, $params);
+        return json_decode($data);
+    }
+
+    /**
+     * Hit some web API and return data on success.
+     * @param string $url
+     * @param array $params
+     * @return string
+     */
+    static protected function http($url, $params=array())
+    {
+        $client = HTTPClient::start();
+        if ($params) {
+            $query = http_build_query($params, null, '&');
+            if (strpos($url, '?') === false) {
+                $url .= '?' . $query;
+            } else {
+                $url .= '&' . $query;
+            }
+        }
+        $response = $client->get($url);
+        if ($response->isOk()) {
+            return $response->getBody();
+        } else {
+            throw new Exception('Bad HTTP response code: ' . $response->getStatus());
+        }
+    }
+}
+
+class oEmbedHelper_Exception extends Exception
+{
+    public function __construct($message = "", $code = 0, $previous = null)
+    {
+        parent::__construct($message, $code);
+    }
+}
+
+class oEmbedHelper_BadHtmlException extends oEmbedHelper_Exception
+{
+    function __construct($previous=null)
+    {
+        return parent::__construct('Bad HTML in discovery data.', 0, $previous);
+    }
+}
+
+class oEmbedHelper_DiscoveryException extends oEmbedHelper_Exception
+{
+    function __construct($previous=null)
+    {
+        return parent::__construct('No oEmbed discovery data.', 0, $previous);
+    }
+}
diff --git a/plugins/Oembed/scripts/fixup_files.php b/plugins/Oembed/scripts/fixup_files.php
new file mode 100755 (executable)
index 0000000..50ddc8a
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2010 StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
+
+$longoptions = array('dry-run');
+
+$helptext = <<<END_OF_USERROLE_HELP
+fixup_files.php [options]
+Patches up file entries with corrupted types and titles (the "h bug").
+
+     --dry-run  look but don't touch
+
+END_OF_USERROLE_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$dry = have_option('dry-run');
+
+$f = new File();
+$f->title = 'h';
+$f->mimetype = 'h';
+$f->size = 0;
+$f->protected = 0;
+$f->find();
+echo "Found $f->N bad items:\n";
+
+while ($f->fetch()) {
+    echo "$f->id $f->url";
+
+    $data = File_redirection::lookupWhere($f->url);
+    if ($dry) {
+        if (is_array($data)) {
+            echo " (unchanged)\n";
+        } else {
+            echo " (unchanged, but embedding lookup failed)\n";
+        }
+    } else {
+        // NULL out the mime/title/size/protected fields
+        $sql = sprintf("UPDATE file " .
+                       "SET mimetype=null,title=null,size=null,protected=null " .
+                       "WHERE id=%d",
+                       $f->id);
+        $f->query($sql);
+        $f->decache();
+        
+        if (is_array($data)) {
+            Event::handle('EndFileSaveNew', array($f, $data, $f->url));
+            echo " (ok)\n";
+        } else {
+            echo " (ok, but embedding lookup failed)\n";
+        }
+    }
+}
+
+echo "done.\n";
+
diff --git a/plugins/Oembed/tests/oEmbedTest.php b/plugins/Oembed/tests/oEmbedTest.php
new file mode 100644 (file)
index 0000000..49e070c
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+
+if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
+    print "This script must be run from the command line\n";
+    exit();
+}
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));
+define('GNUSOCIAL', true);
+define('STATUSNET', true);  // compatibility
+
+require_once INSTALLDIR . '/lib/common.php';
+
+class oEmbedTest extends PHPUnit_Framework_TestCase
+{
+
+    public function setup()
+    {
+        $this->old_ohembed = common_config('ohembed', 'endpoint');
+    }
+
+    public function tearDown()
+    {
+        $GLOBALS['config']['oembed']['endpoint'] = $this->old_ohembed;
+    }
+
+    /**
+     * Test with ohembed DISABLED.
+     *
+     * @dataProvider discoverableSources
+     */
+    public function testoEmbed($url, $expectedType)
+    {
+        $GLOBALS['config']['oembed']['endpoint'] = false;
+        $this->_doTest($url, $expectedType);
+    }
+
+    /**
+     * Test with oohembed ENABLED.
+     *
+     * @dataProvider fallbackSources
+     */
+    public function testnoEmbed($url, $expectedType)
+    {
+        $GLOBALS['config']['oembed']['endpoint'] = $this->_endpoint();
+        $this->_doTest($url, $expectedType);
+    }
+
+    /**
+     * Get default oembed endpoint.
+     *
+     * @return string
+     */
+    function _endpoint()
+    {
+        $default = array();
+        $_server = 'localhost'; $_path = '';
+        require INSTALLDIR . '/lib/default.php';
+        return $default['oembed']['endpoint'];
+    }
+
+    /**
+     * Actually run an individual test.
+     *
+     * @param string $url
+     * @param string $expectedType
+     */
+    function _doTest($url, $expectedType)
+    {
+        try {
+            $data = oEmbedHelper::getObject($url);
+            $this->assertEquals($expectedType, $data->type);
+            if ($data->type == 'photo') {
+                $this->assertTrue(!empty($data->url), 'Photo must have a URL.');
+                $this->assertTrue(!empty($data->width), 'Photo must have a width.');
+                $this->assertTrue(!empty($data->height), 'Photo must have a height.');
+            } else if ($data->type == 'video') {
+                $this->assertTrue(!empty($data->html), 'Video must have embedding HTML.');
+                $this->assertTrue(!empty($data->thumbnail_url), 'Video should have a thumbnail.');
+            }
+            if (!empty($data->thumbnail_url)) {
+                $this->assertTrue(!empty($data->thumbnail_width), 'Thumbnail must list a width.');
+                $this->assertTrue(!empty($data->thumbnail_height), 'Thumbnail must list a height.');
+            }
+        } catch (Exception $e) {
+            if ($expectedType == 'none') {
+                $this->assertEquals($expectedType, 'none', 'Should not have data for this URL.');
+            } else {
+                throw $e;
+            }
+        }
+    }
+
+    /**
+     * Sample oEmbed targets for sites we know ourselves...
+     * @return array
+     */
+    static public function knownSources()
+    {
+        $sources = array(
+            array('http://www.flickr.com/photos/brionv/5172500179/', 'photo'),
+            array('http://yfrog.com/fy42747177j', 'photo'),
+            array('http://twitpic.com/36adw6', 'photo'),
+        );
+        return $sources;
+    }
+
+    /**
+     * Sample oEmbed targets that can be found via discovery.
+     * Includes also knownSources() output.
+     *
+     * @return array
+     */
+    static public function discoverableSources()
+    {
+        $sources = array(
+
+            array('http://www.youtube.com/watch?v=eUgLR232Cnw', 'video'),
+            array('http://vimeo.com/9283184', 'video'),
+
+            // Will fail discovery:
+            array('http://leuksman.com/log/2010/10/29/statusnet-0-9-6-release/', 'none'),
+        );
+        return array_merge(self::knownSources(), $sources);
+    }
+
+    /**
+     * Sample oEmbed targets that can be found via noembed.com.
+     * Includes also discoverableSources() output.
+     *
+     * @return array
+     */
+    static public function fallbackSources()
+    {
+
+        $sources = array(
+            array('https://github.com/git/git/commit/85e9c7e1d42849c5c3084a9da748608468310c0e', 'Github Commit'), // @fixme in future there may be a native provider -- will change to 'photo'
+        );
+
+        $sources = array();
+
+        return array_merge(self::discoverableSources(), $sources);
+    }
+}
diff --git a/scripts/fixup_files.php b/scripts/fixup_files.php
deleted file mode 100755 (executable)
index 18feaf2..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env php
-<?php
-/*
- * StatusNet - a distributed open-source microblogging tool
- * Copyright (C) 2010 StatusNet, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-
-$longoptions = array('dry-run');
-
-$helptext = <<<END_OF_USERROLE_HELP
-fixup_files.php [options]
-Patches up file entries with corrupted types and titles (the "h bug").
-
-     --dry-run  look but don't touch
-
-END_OF_USERROLE_HELP;
-
-require_once INSTALLDIR.'/scripts/commandline.inc';
-
-$dry = have_option('dry-run');
-
-$f = new File();
-$f->title = 'h';
-$f->mimetype = 'h';
-$f->size = 0;
-$f->protected = 0;
-$f->find();
-echo "Found $f->N bad items:\n";
-
-while ($f->fetch()) {
-    echo "$f->id $f->url";
-
-    $data = File_redirection::lookupWhere($f->url);
-    if ($dry) {
-        if (is_array($data)) {
-            echo " (unchanged)\n";
-        } else {
-            echo " (unchanged, but embedding lookup failed)\n";
-        }
-    } else {
-        // NULL out the mime/title/size/protected fields
-        $sql = sprintf("UPDATE file " .
-                       "SET mimetype=null,title=null,size=null,protected=null " .
-                       "WHERE id=%d",
-                       $f->id);
-        $f->query($sql);
-        $f->decache();
-        
-        if (is_array($data)) {
-            if ($f->saveOembed($data, $f->url)) {
-                echo " (ok)\n";
-            } else {
-                echo " (ok, no embedding data)\n";
-            }
-        } else {
-            echo " (ok, but embedding lookup failed)\n";
-        }
-    }
-}
-
-echo "done.\n";
-
diff --git a/tests/oEmbedTest.php b/tests/oEmbedTest.php
deleted file mode 100644 (file)
index 50f3fd2..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-
-if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
-    print "This script must be run from the command line\n";
-    exit();
-}
-
-define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
-define('GNUSOCIAL', true);
-define('STATUSNET', true);  // compatibility
-
-require_once INSTALLDIR . '/lib/common.php';
-
-class oEmbedTest extends PHPUnit_Framework_TestCase
-{
-
-    public function setup()
-    {
-        $this->old_ohembed = common_config('ohembed', 'endpoint');
-    }
-
-    public function tearDown()
-    {
-        $GLOBALS['config']['oembed']['endpoint'] = $this->old_ohembed;
-    }
-
-    /**
-     * Test with ohembed DISABLED.
-     *
-     * @dataProvider discoverableSources
-     */
-    public function testoEmbed($url, $expectedType)
-    {
-        $GLOBALS['config']['oembed']['endpoint'] = false;
-        $this->_doTest($url, $expectedType);
-    }
-
-    /**
-     * Test with oohembed ENABLED.
-     *
-     * @dataProvider fallbackSources
-     */
-    public function testnoEmbed($url, $expectedType)
-    {
-        $GLOBALS['config']['oembed']['endpoint'] = $this->_endpoint();
-        $this->_doTest($url, $expectedType);
-    }
-
-    /**
-     * Get default oembed endpoint.
-     *
-     * @return string
-     */
-    function _endpoint()
-    {
-        $default = array();
-        $_server = 'localhost'; $_path = '';
-        require INSTALLDIR . '/lib/default.php';
-        return $default['oembed']['endpoint'];
-    }
-
-    /**
-     * Actually run an individual test.
-     *
-     * @param string $url
-     * @param string $expectedType
-     */
-    function _doTest($url, $expectedType)
-    {
-        try {
-            $data = oEmbedHelper::getObject($url);
-            $this->assertEquals($expectedType, $data->type);
-            if ($data->type == 'photo') {
-                $this->assertTrue(!empty($data->url), 'Photo must have a URL.');
-                $this->assertTrue(!empty($data->width), 'Photo must have a width.');
-                $this->assertTrue(!empty($data->height), 'Photo must have a height.');
-            } else if ($data->type == 'video') {
-                $this->assertTrue(!empty($data->html), 'Video must have embedding HTML.');
-                $this->assertTrue(!empty($data->thumbnail_url), 'Video should have a thumbnail.');
-            }
-            if (!empty($data->thumbnail_url)) {
-                $this->assertTrue(!empty($data->thumbnail_width), 'Thumbnail must list a width.');
-                $this->assertTrue(!empty($data->thumbnail_height), 'Thumbnail must list a height.');
-            }
-        } catch (Exception $e) {
-            if ($expectedType == 'none') {
-                $this->assertEquals($expectedType, 'none', 'Should not have data for this URL.');
-            } else {
-                throw $e;
-            }
-        }
-    }
-
-    /**
-     * Sample oEmbed targets for sites we know ourselves...
-     * @return array
-     */
-    static public function knownSources()
-    {
-        $sources = array(
-            array('http://www.flickr.com/photos/brionv/5172500179/', 'photo'),
-            array('http://yfrog.com/fy42747177j', 'photo'),
-            array('http://twitpic.com/36adw6', 'photo'),
-        );
-        return $sources;
-    }
-
-    /**
-     * Sample oEmbed targets that can be found via discovery.
-     * Includes also knownSources() output.
-     *
-     * @return array
-     */
-    static public function discoverableSources()
-    {
-        $sources = array(
-
-            array('http://www.youtube.com/watch?v=eUgLR232Cnw', 'video'),
-            array('http://vimeo.com/9283184', 'video'),
-
-            // Will fail discovery:
-            array('http://leuksman.com/log/2010/10/29/statusnet-0-9-6-release/', 'none'),
-        );
-        return array_merge(self::knownSources(), $sources);
-    }
-
-    /**
-     * Sample oEmbed targets that can be found via noembed.com.
-     * Includes also discoverableSources() output.
-     *
-     * @return array
-     */
-    static public function fallbackSources()
-    {
-
-        $sources = array(
-            array('https://github.com/git/git/commit/85e9c7e1d42849c5c3084a9da748608468310c0e', 'Github Commit'), // @fixme in future there may be a native provider -- will change to 'photo'
-        );
-
-        $sources = array();
-
-        return array_merge(self::discoverableSources(), $sources);
-    }
-}