From 1776c90cb98d5ca738db143703fb998612ada31e Mon Sep 17 00:00:00 2001 From: Mikael Nordfeldth Date: Tue, 6 May 2014 23:00:30 +0200 Subject: [PATCH] Moved oEmbed stuff out to a plugin (Oembed). --- CONFIGURE | 17 -- actions/attachment.php | 22 -- actions/oembed.php | 244 ------------------ actions/shownotice.php | 15 -- classes/File.php | 90 ++----- classes/File_redirection.php | 3 - db/core.php | 1 - lib/action.php | 5 + lib/attachmentlist.php | 79 +----- lib/default.php | 1 + lib/router.php | 3 - plugins/Bookmark/README | 1 + plugins/LinkPreview/README | 1 + plugins/Oembed/CONFIGURE | 18 ++ plugins/Oembed/OembedPlugin.php | 187 ++++++++++++++ plugins/Oembed/README | 1 + plugins/Oembed/actions/oembed.php | 236 +++++++++++++++++ .../Oembed/classes}/File_oembed.php | 0 {lib => plugins/Oembed/lib}/oembedhelper.php | 0 .../Oembed/scripts}/fixup_files.php | 9 +- .../Oembed/tests}/oEmbedTest.php | 2 +- 21 files changed, 481 insertions(+), 454 deletions(-) delete mode 100644 actions/oembed.php create mode 100644 plugins/Bookmark/README create mode 100644 plugins/LinkPreview/README create mode 100644 plugins/Oembed/CONFIGURE create mode 100644 plugins/Oembed/OembedPlugin.php create mode 100644 plugins/Oembed/README create mode 100644 plugins/Oembed/actions/oembed.php rename {classes => plugins/Oembed/classes}/File_oembed.php (100%) rename {lib => plugins/Oembed/lib}/oembedhelper.php (100%) rename {scripts => plugins/Oembed/scripts}/fixup_files.php (89%) rename {tests => plugins/Oembed/tests}/oEmbedTest.php (98%) diff --git a/CONFIGURE b/CONFIGURE index 141c475563..bec9e90a6f 100644 --- 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 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 ------ diff --git a/actions/attachment.php b/actions/attachment.php index 30499c15f4..d9cacb1193 100644 --- a/actions/attachment.php +++ b/actions/attachment.php @@ -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 index da0352edf8..0000000000 --- a/actions/oembed.php +++ /dev/null @@ -1,244 +0,0 @@ -. - * - * @category Twitter - * @package StatusNet - * @author Evan Prodromou - * @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 - * @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; - } -} diff --git a/actions/shownotice.php b/actions/shownotice.php index ff39080eeb..28cb68c121 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -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', diff --git a/classes/File.php b/classes/File.php index 29dc7b5492..69aee7fcee 100644 --- a/classes/File.php +++ b/classes/File.php @@ -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_redirection.php b/classes/File_redirection.php index 42021668d9..0bcccc6cff 100644 --- a/classes/File_redirection.php +++ b/classes/File_redirection.php @@ -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); diff --git a/db/core.php b/db/core.php index d435c03cd6..61f8228cb5 100644 --- a/db/core.php +++ b/db/core.php @@ -67,7 +67,6 @@ $classes = array('Schema_version', 'Group_inbox', 'Group_member', 'File', - 'File_oembed', 'File_redirection', 'File_thumbnail', 'File_to_post', diff --git a/lib/action.php b/lib/action.php index e219847c70..4d917dc69e 100644 --- a/lib/action.php +++ b/lib/action.php @@ -166,6 +166,11 @@ class Action extends HTMLOutputter // lawsuit return $this->scoped; } + public function getActionName() + { + return $this->action; + } + /** * Show page, a template method. * diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php index 7f356f9527..66b03182b3 100644 --- a/lib/attachmentlist.php +++ b/lib/attachmentlist.php @@ -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': diff --git a/lib/default.php b/lib/default.php index d2270fa6a5..65301bbb45 100644 --- a/lib/default.php +++ b/lib/default.php @@ -303,6 +303,7 @@ $default = 'ClientSideShorten' => array(), 'EmailAuthentication' => array(), 'Event' => array(), + 'Oembed' => array(), 'OpenID' => array(), 'Poll' => array(), 'QnA' => array(), diff --git a/lib/router.php b/lib/router.php index 71bf4e8e82..3febbbeff4 100644 --- a/lib/router.php +++ b/lib/router.php @@ -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 index 0000000000..e853760544 --- /dev/null +++ b/plugins/Bookmark/README @@ -0,0 +1 @@ +Depends on the oEmbed plugin (Oembed). diff --git a/plugins/LinkPreview/README b/plugins/LinkPreview/README new file mode 100644 index 0000000000..a1b1a87303 --- /dev/null +++ b/plugins/LinkPreview/README @@ -0,0 +1 @@ +Depends on the oEmbed plugin (Oembed) diff --git a/plugins/Oembed/CONFIGURE b/plugins/Oembed/CONFIGURE new file mode 100644 index 0000000000..b8d24f909f --- /dev/null +++ b/plugins/Oembed/CONFIGURE @@ -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 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 index 0000000000..177fd6ed3e --- /dev/null +++ b/plugins/Oembed/OembedPlugin.php @@ -0,0 +1,187 @@ +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 index 0000000000..be8c09f7a6 --- /dev/null +++ b/plugins/Oembed/README @@ -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 index 0000000000..509c3a73ee --- /dev/null +++ b/plugins/Oembed/actions/oembed.php @@ -0,0 +1,236 @@ +. + */ + +if (!defined('GNUSOCIAL')) { exit(1); } + +/** + * Oembed provider implementation + * + * This class handles all /main/oembed(.xml|.json)/ requests. + * + * @category oEmbed + * @package GNUsocial + * @author Craig Andrews + * @author Mikael Nordfeldth + * @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/classes/File_oembed.php b/plugins/Oembed/classes/File_oembed.php similarity index 100% rename from classes/File_oembed.php rename to plugins/Oembed/classes/File_oembed.php diff --git a/lib/oembedhelper.php b/plugins/Oembed/lib/oembedhelper.php similarity index 100% rename from lib/oembedhelper.php rename to plugins/Oembed/lib/oembedhelper.php diff --git a/scripts/fixup_files.php b/plugins/Oembed/scripts/fixup_files.php similarity index 89% rename from scripts/fixup_files.php rename to plugins/Oembed/scripts/fixup_files.php index 18feaf2218..50ddc8a994 100755 --- a/scripts/fixup_files.php +++ b/plugins/Oembed/scripts/fixup_files.php @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); $longoptions = array('dry-run'); @@ -62,11 +62,8 @@ while ($f->fetch()) { $f->decache(); if (is_array($data)) { - if ($f->saveOembed($data, $f->url)) { - echo " (ok)\n"; - } else { - echo " (ok, no embedding data)\n"; - } + Event::handle('EndFileSaveNew', array($f, $data, $f->url)); + echo " (ok)\n"; } else { echo " (ok, but embedding lookup failed)\n"; } diff --git a/tests/oEmbedTest.php b/plugins/Oembed/tests/oEmbedTest.php similarity index 98% rename from tests/oEmbedTest.php rename to plugins/Oembed/tests/oEmbedTest.php index 50f3fd2919..49e070cb6d 100644 --- a/tests/oEmbedTest.php +++ b/plugins/Oembed/tests/oEmbedTest.php @@ -5,7 +5,7 @@ if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { exit(); } -define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..')); define('GNUSOCIAL', true); define('STATUSNET', true); // compatibility -- 2.39.5