forked from GNUsocial/gnu-social
[Embed] Refactoring and bug fixing
This commit is contained in:
parent
462ea26303
commit
0c20d35206
@ -1073,7 +1073,7 @@ class Schema
|
|||||||
try {
|
try {
|
||||||
$this->getTableDef($new_name);
|
$this->getTableDef($new_name);
|
||||||
// New table exists, can't work
|
// New table exists, can't work
|
||||||
throw new ServerException("Both table {$old_name} and {$new_name} exist. You're on your own");
|
throw new ServerException("Both table {$old_name} and {$new_name} exist. You're on your own.");
|
||||||
} catch(SchemaTableMissingException $e) {
|
} catch(SchemaTableMissingException $e) {
|
||||||
// New table doesn't exist, carry on
|
// New table doesn't exist, carry on
|
||||||
}
|
}
|
||||||
|
@ -170,10 +170,10 @@ class EmbedPlugin extends Plugin
|
|||||||
// sometimes sites serve the path, not the full URL, for images
|
// sometimes sites serve the path, not the full URL, for images
|
||||||
// let's "be liberal in what you accept from others"!
|
// let's "be liberal in what you accept from others"!
|
||||||
// add protocol and host if the thumbnail_url starts with /
|
// add protocol and host if the thumbnail_url starts with /
|
||||||
if (substr($metadata->thumbnail_url, 0, 1) == '/') {
|
if ($metadata->thumbnail_url[0] == '/') {
|
||||||
$thumbnail_url_parsed = parse_url($metadata->url);
|
$thumbnail_url_parsed = parse_url($metadata->url);
|
||||||
$metadata->thumbnail_url = $thumbnail_url_parsed['scheme']."://".
|
$metadata->thumbnail_url = "{$thumbnail_url_parsed['scheme']}://".
|
||||||
$thumbnail_url_parsed['host'].$metadata->thumbnail_url;
|
"{$thumbnail_url_parsed['host']}{$metadata->thumbnail_url}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// some wordpress opengraph implementations sometimes return a white blank image
|
// some wordpress opengraph implementations sometimes return a white blank image
|
||||||
@ -191,59 +191,33 @@ class EmbedPlugin extends Plugin
|
|||||||
{
|
{
|
||||||
switch ($action->getActionName()) {
|
switch ($action->getActionName()) {
|
||||||
case 'attachment':
|
case 'attachment':
|
||||||
$action->element('link', array('rel'=>'alternate',
|
$url = common_local_url('attachment', array('attachment' => $action->attachment->getID()));
|
||||||
'type'=>'application/json+oembed',
|
|
||||||
'href'=>common_local_url(
|
|
||||||
'oembed',
|
|
||||||
array(),
|
|
||||||
array('format'=>'json', 'url'=>
|
|
||||||
common_local_url(
|
|
||||||
'attachment',
|
|
||||||
array('attachment' => $action->attachment->getID())
|
|
||||||
))
|
|
||||||
),
|
|
||||||
'title'=>'oEmbed'));
|
|
||||||
$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->getID())
|
|
||||||
))
|
|
||||||
),
|
|
||||||
'title'=>'oEmbed'));
|
|
||||||
break;
|
break;
|
||||||
case 'shownotice':
|
case 'shownotice':
|
||||||
if (!$action->notice->isLocal()) {
|
if (!$action->notice->isLocal()) {
|
||||||
break;
|
return true;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$action->element('link', array('rel'=>'alternate',
|
$url = $action->notice->getUrl();
|
||||||
'type'=>'application/json+oembed',
|
|
||||||
'href'=>common_local_url(
|
|
||||||
'oembed',
|
|
||||||
array(),
|
|
||||||
array('format'=>'json','url'=>$action->notice->getUrl())
|
|
||||||
),
|
|
||||||
'title'=>'oEmbed'));
|
|
||||||
$action->element('link', array('rel'=>'alternate',
|
|
||||||
'type'=>'text/xml+oembed',
|
|
||||||
'href'=>common_local_url(
|
|
||||||
'oembed',
|
|
||||||
array(),
|
|
||||||
array('format'=>'xml','url'=>$action->notice->getUrl())
|
|
||||||
),
|
|
||||||
'title'=>'oEmbed'));
|
|
||||||
} catch (InvalidUrlException $e) {
|
} catch (InvalidUrlException $e) {
|
||||||
// The notice is probably a share or similar, which don't
|
// The notice is probably a share or similar, which don't
|
||||||
// have a representational URL of their own.
|
// have a representational URL of their own.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($url)) {
|
||||||
|
foreach (['xml', 'json'] as $format) {
|
||||||
|
$action->element('link',
|
||||||
|
array('rel' =>'alternate',
|
||||||
|
'type' => "application/{$format}+oembed",
|
||||||
|
'href' => common_local_url('oembed',
|
||||||
|
array(),
|
||||||
|
array('format' => $format, 'url' => $url)),
|
||||||
|
'title' => 'oEmbed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,29 +232,30 @@ class EmbedPlugin extends Plugin
|
|||||||
*
|
*
|
||||||
* Normally this event is called through File::saveNew()
|
* Normally this event is called through File::saveNew()
|
||||||
*
|
*
|
||||||
* @param File $file The newly inserted File object.
|
* @param File $file The newly inserted File object.
|
||||||
*
|
*
|
||||||
* @return boolean success
|
* @return boolean success
|
||||||
*/
|
*/
|
||||||
public function onEndFileSaveNew(File $file)
|
public function onEndFileSaveNew(File $file)
|
||||||
{
|
{
|
||||||
$fo = File_embed::getKV('file_id', $file->getID());
|
$fe = File_embed::getKV('file_id', $file->getID());
|
||||||
if ($fo instanceof File_embed) {
|
if ($fe instanceof File_embed) {
|
||||||
common_log(LOG_WARNING, "Strangely, a File_embed object exists for new file {$file->getID()}", __FILE__);
|
common_log(LOG_WARNING, "Strangely, a File_embed object exists for new file {$file->getID()}", __FILE__);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($file->mimetype)
|
if (isset($file->mimetype)
|
||||||
&& (('text/html' === substr($file->mimetype, 0, 9)
|
&& (('text/html' === substr($file->mimetype, 0, 9) ||
|
||||||
|| 'application/xhtml+xml' === substr($file->mimetype, 0, 21)))) {
|
'application/xhtml+xml' === substr($file->mimetype, 0, 21)))) {
|
||||||
try {
|
try {
|
||||||
$embed_data = File_embed::_getEmbed($file->url);
|
$embed_data = File_embed::getEmbed($file->url);
|
||||||
if ($embed_data === false) {
|
if ($embed_data === false) {
|
||||||
throw new Exception('Did not get embed data from URL');
|
throw new Exception("Did not get Embed data from URL {$file->url}");
|
||||||
}
|
}
|
||||||
$file->setTitle($embed_data->title);
|
$file->setTitle($embed_data->title);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
common_log(LOG_WARNING, sprintf(__METHOD__.': %s thrown when getting embed data: %s', get_class($e), _ve($e->getMessage())));
|
common_log(LOG_WARNING, sprintf(__METHOD__.': %s thrown when getting embed data: %s',
|
||||||
|
get_class($e), _ve($e->getMessage())));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,30 +271,20 @@ class EmbedPlugin extends Plugin
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$out->elementStart('div', array('id'=>'oembed_info', 'class'=>'e-content'));
|
$out->elementStart('div', array('id'=>'oembed_info', 'class'=>'e-content'));
|
||||||
if (!empty($embed->author_name)) {
|
foreach (['author_name' => ['class' => ' author', 'url' => 'author_url'],
|
||||||
$out->elementStart('div', 'fn vcard author');
|
'provider' => ['class' => '', 'url' => 'provider_url']]
|
||||||
if (empty($embed->author_url)) {
|
as $field => $options) {
|
||||||
$out->text($embed->author_name);
|
if (!empty($embed->{$field})) {
|
||||||
} else {
|
$out->elementStart('div', "fn vcard" . $options['class']);
|
||||||
$out->element(
|
if (empty($embed->{$options['url']})) {
|
||||||
'a',
|
$out->text($embed->{$field});
|
||||||
array('href' => $embed->author_url,
|
} else {
|
||||||
'class' => 'url'),
|
$out->element('a',
|
||||||
$embed->author_name
|
array('href' => $embed->{$options['url']},
|
||||||
);
|
'class' => 'url'),
|
||||||
}
|
$embed->{$field}
|
||||||
}
|
);
|
||||||
if (!empty($embed->provider)) {
|
}
|
||||||
$out->elementStart('div', 'fn vcard');
|
|
||||||
if (empty($embed->provider_url)) {
|
|
||||||
$out->text($embed->provider);
|
|
||||||
} else {
|
|
||||||
$out->element(
|
|
||||||
'a',
|
|
||||||
array('href' => $embed->provider_url,
|
|
||||||
'class' => 'url'),
|
|
||||||
$embed->provider
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$out->elementEnd('div');
|
$out->elementEnd('div');
|
||||||
@ -334,7 +299,7 @@ class EmbedPlugin extends Plugin
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (array('mimetype', 'url', 'title', 'modified', 'width', 'height') as $key) {
|
foreach (['mimetype', 'url', 'title', 'modified', 'width', 'height'] as $key) {
|
||||||
if (isset($embed->{$key}) && !empty($embed->{$key})) {
|
if (isset($embed->{$key}) && !empty($embed->{$key})) {
|
||||||
$enclosure->{$key} = $embed->{$key};
|
$enclosure->{$key} = $embed->{$key};
|
||||||
}
|
}
|
||||||
@ -396,7 +361,7 @@ class EmbedPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
$out->elementEnd('div');
|
$out->elementEnd('div');
|
||||||
$out->elementEnd('header');
|
$out->elementEnd('header');
|
||||||
$out->elementStart('div', ['class'=>'p-summary oembed']);
|
$out->elementStart('div', ['class'=>'p-summary embed']);
|
||||||
$out->raw(common_purify($embed->html));
|
$out->raw(common_purify($embed->html));
|
||||||
$out->elementEnd('div');
|
$out->elementEnd('div');
|
||||||
$out->elementStart('footer');
|
$out->elementStart('footer');
|
||||||
@ -422,7 +387,8 @@ class EmbedPlugin extends Plugin
|
|||||||
&& (GNUsocial::isAjax() || common_config('attachments', 'show_html'))) {
|
&& (GNUsocial::isAjax() || common_config('attachments', 'show_html'))) {
|
||||||
require_once INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php';
|
require_once INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php';
|
||||||
$purifier = new HTMLPurifier();
|
$purifier = new HTMLPurifier();
|
||||||
// FIXME: do we allow <object> and <embed> here? we did that when we used htmLawed, but I'm not sure anymore...
|
// FIXME: do we allow <object> and <embed> here? we did that when we used htmLawed,
|
||||||
|
// but I'm not sure anymore...
|
||||||
$out->raw($purifier->purify($embed->html));
|
$out->raw($purifier->purify($embed->html));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -510,27 +476,21 @@ class EmbedPlugin extends Plugin
|
|||||||
*
|
*
|
||||||
* @return string|bool the file size if it succeeds, false otherwise.
|
* @return string|bool the file size if it succeeds, false otherwise.
|
||||||
*/
|
*/
|
||||||
private function getRemoteFileSize($url)
|
private function getRemoteFileSize($url, $headers = null)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
if (empty($url)) {
|
if ($headers === null) {
|
||||||
return false;
|
if (!common_valid_http_url($url)) {
|
||||||
}
|
common_log(LOG_ERR, "Invalid URL in Embed::getRemoteFileSize()");
|
||||||
stream_context_set_default(array('http' => array('method' => 'HEAD')));
|
|
||||||
$head = @get_headers($url, 1);
|
|
||||||
if (gettype($head)=="array") {
|
|
||||||
$head = array_change_key_case($head);
|
|
||||||
$size = isset($head['content-length']) ? $head['content-length'] : 0;
|
|
||||||
|
|
||||||
if (!$size) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
$head = (new HTTPClient())->head($url);
|
||||||
return false;
|
$headers = $head->getHeader();
|
||||||
}
|
}
|
||||||
return $size; // return formatted size
|
return $headers['content-length'] ?: false;
|
||||||
} catch (Exception $err) {
|
} catch (Exception $err) {
|
||||||
common_log(LOG_ERR, __CLASS__.': getRemoteFileSize on URL : '._ve($url).' threw exception: '.$err->getMessage());
|
common_log(LOG_ERR, __CLASS__.': getRemoteFileSize on URL : '._ve($url).
|
||||||
|
' threw exception: '.$err->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,31 +501,17 @@ class EmbedPlugin extends Plugin
|
|||||||
*
|
*
|
||||||
* @return bool true if the remote URL is an image, or false otherwise.
|
* @return bool true if the remote URL is an image, or false otherwise.
|
||||||
*/
|
*/
|
||||||
private function isRemoteImage($url)
|
private function isRemoteImage($url, $headers = null)
|
||||||
{
|
{
|
||||||
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
if (empty($headers)) {
|
||||||
common_log(LOG_ERR, "Invalid URL in Embed::isRemoteImage()");
|
if (!common_valid_http_url($url)) {
|
||||||
return false;
|
common_log(LOG_ERR, "Invalid URL in Embed::isRemoteImage()");
|
||||||
}
|
|
||||||
if ($url==null) {
|
|
||||||
common_log(LOG_ERR, "URL not specified in Embed::isRemoteImage()");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$curl = curl_init($url);
|
|
||||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_HEADER, true);
|
|
||||||
curl_setopt($curl, CURLOPT_NOBODY, true);
|
|
||||||
curl_exec($curl);
|
|
||||||
$type = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
|
|
||||||
if (strpos($type, 'image') !== false) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} finally {
|
$head = (new HTTPClient())->head($url);
|
||||||
return false;
|
$headers = $head->getHeader();
|
||||||
}
|
}
|
||||||
|
return !empty($headers['content-type']) && common_get_mime_media($headers['content-type']) === 'image';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -585,11 +531,14 @@ class EmbedPlugin extends Plugin
|
|||||||
$url = $thumbnail->getUrl();
|
$url = $thumbnail->getUrl();
|
||||||
$this->checkWhitelist($url);
|
$this->checkWhitelist($url);
|
||||||
|
|
||||||
|
$head = (new HTTPClient())->head($url);
|
||||||
|
$headers = $head->getHeader();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$isImage = $this->isRemoteImage($url);
|
$isImage = $this->isRemoteImage($url, $headers);
|
||||||
if ($isImage==true) {
|
if ($isImage==true) {
|
||||||
$max_size = common_get_preferred_php_upload_limit();
|
$max_size = common_get_preferred_php_upload_limit();
|
||||||
$file_size = $this->getRemoteFileSize($url);
|
$file_size = $this->getRemoteFileSize($url, $headers);
|
||||||
if (($file_size!=false) && ($file_size > $max_size)) {
|
if (($file_size!=false) && ($file_size > $max_size)) {
|
||||||
common_debug("Went to store remote thumbnail of size " . $file_size .
|
common_debug("Went to store remote thumbnail of size " . $file_size .
|
||||||
" but the upload limit is " . $max_size . " so we aborted.");
|
" but the upload limit is " . $max_size . " so we aborted.");
|
||||||
@ -613,20 +562,29 @@ class EmbedPlugin extends Plugin
|
|||||||
throw new UnsupportedMediaException(_('Image file had impossible geometry (0 width or height)'));
|
throw new UnsupportedMediaException(_('Image file had impossible geometry (0 width or height)'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$ext = File::guessMimeExtension($info['mime']);
|
$filehash = hash(File::FILEHASH_ALG, $imgData);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// We'll trust sha256 (File::FILEHASH_ALG) not to have collision issues any time soon :)
|
$original_name = HTTPClient::get_filename($url, $headers);
|
||||||
$original_filename = bin2hex('embed.' . $ext);
|
$filename = MediaFile::encodeFilename($original_name, $filehash);
|
||||||
$filehash = hash(File::FILEHASH_ALG, $imgData);
|
|
||||||
$filename = "{$original_filename}-{$filehash}";
|
|
||||||
$fullpath = File_thumbnail::path($filename);
|
$fullpath = File_thumbnail::path($filename);
|
||||||
// Write the file to disk. Throw Exception on failure
|
// Write the file to disk. Throw Exception on failure
|
||||||
if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) {
|
if (!file_exists($fullpath)) {
|
||||||
throw new ServerException(_('Could not write downloaded file to disk.'));
|
if (strpos($fullpath, INSTALLDIR) !== 0 || file_put_contents($fullpath, $imgData) === false) {
|
||||||
|
throw new ServerException(_('Could not write downloaded file to disk.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (common_get_mime_media(MediaFile::getUploadedMimeType($fullpath)) !== 'image') {
|
||||||
|
@unlink($fullpath);
|
||||||
|
throw new UnsupportedMediaException(
|
||||||
|
_('Remote file format was not identified as an image.'), $url);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new AlreadyFulfilledException('A thumbnail seems to already exist for remote file with id==' .
|
||||||
|
$thumbnail->file_id);
|
||||||
}
|
}
|
||||||
} catch (Exception $err) {
|
} catch (Exception $err) {
|
||||||
common_log(LOG_ERROR, "Went to write a thumbnail to disk in EmbedPlugin::storeRemoteThumbnail " .
|
common_log(LOG_ERR, "Went to write a thumbnail to disk in EmbedPlugin::storeRemoteThumbnail " .
|
||||||
"but encountered error: {$err}");
|
"but encountered error: {$err}");
|
||||||
return $err;
|
return $err;
|
||||||
} finally {
|
} finally {
|
||||||
@ -642,7 +600,7 @@ class EmbedPlugin extends Plugin
|
|||||||
// Throws exception on failure.
|
// Throws exception on failure.
|
||||||
$thumbnail->updateWithKeys($orig);
|
$thumbnail->updateWithKeys($orig);
|
||||||
} catch (exception $err) {
|
} catch (exception $err) {
|
||||||
common_log(LOG_ERROR, "Went to write a thumbnail entry to the database in " .
|
common_log(LOG_ERR, "Went to write a thumbnail entry to the database in " .
|
||||||
"EmbedPlugin::storeRemoteThumbnail but encountered error: ".$err);
|
"EmbedPlugin::storeRemoteThumbnail but encountered error: ".$err);
|
||||||
return $err;
|
return $err;
|
||||||
}
|
}
|
||||||
|
@ -77,12 +77,12 @@ class File_embed extends Managed_DataObject
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function _getEmbed($url)
|
public static function getEmbed($url)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return EmbedHelper::getObject($url);
|
return EmbedHelper::getObject($url);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
common_log(LOG_INFO, "Error during oembed lookup for $url - " . $e->getMessage());
|
common_log(LOG_INFO, "Error during Embed lookup for {$url} - " . $e->getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,16 +116,16 @@ class File_embed extends Managed_DataObject
|
|||||||
$file_embed = new File_embed;
|
$file_embed = new File_embed;
|
||||||
$file_embed->file_id = $file_id;
|
$file_embed->file_id = $file_id;
|
||||||
if (!isset($data->version)) {
|
if (!isset($data->version)) {
|
||||||
common_debug('DEBUGGING oEmbed: data->version undefined in variable $data: '.var_export($data, true));
|
common_debug('Embed: data->version undefined in variable $data: '.var_export($data, true));
|
||||||
}
|
}
|
||||||
$file_embed->version = $data->version;
|
$file_embed->version = $data->version;
|
||||||
$file_embed->type = $data->type;
|
$file_embed->type = $data->type;
|
||||||
if (!empty($data->provider_name)) {
|
|
||||||
$file_embed->provider = $data->provider_name;
|
|
||||||
}
|
|
||||||
if (!empty($data->provider)) {
|
if (!empty($data->provider)) {
|
||||||
$file_embed->provider = $data->provider;
|
$file_embed->provider = $data->provider;
|
||||||
}
|
}
|
||||||
|
if (!empty($data->provider_name)) {
|
||||||
|
$file_embed->provider = $data->provider_name;
|
||||||
|
}
|
||||||
if (!empty($data->provider_url)) {
|
if (!empty($data->provider_url)) {
|
||||||
$file_embed->provider_url = $data->provider_url;
|
$file_embed->provider_url = $data->provider_url;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
.u-photo.oembed {
|
.u-photo.embed {
|
||||||
float: left;
|
float: left;
|
||||||
margin-bottom: 1ex;
|
margin-bottom: 1ex;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-author.oembed {
|
.p-author.embed {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-summary.oembed {
|
.p-summary.embed {
|
||||||
line-height: 1.25em;
|
line-height: 1.25em;
|
||||||
max-height: 5em;
|
max-height: 5em;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
Loading…
Reference in New Issue
Block a user