From 883f7a6c0b1464f6723e51bf99d06641a612f968 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 13:27:54 -0800 Subject: Avoid marking files as attachments that are not locally uploaded, unless they're really oembedable. HTML-y things now excluded properly. --- classes/File.php | 3 +++ lib/util.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/classes/File.php b/classes/File.php index 16e00024a..d71403e64 100644 --- a/classes/File.php +++ b/classes/File.php @@ -352,6 +352,9 @@ class File extends Memcached_DataObject $mimetype = substr($mimetype,0,$semicolon); } if(in_array($mimetype,$notEnclosureMimeTypes)){ + // Never treat HTML as an enclosure type! + return false; + } else { $oembed = File_oembed::staticGet('file_id',$this->id); if($oembed){ $mimetype = strtolower($oembed->mimetype); diff --git a/lib/util.php b/lib/util.php index 8f2a9f173..e6b62f750 100644 --- a/lib/util.php +++ b/lib/util.php @@ -877,7 +877,7 @@ function common_linkify($url) { } if (!empty($f)) { - if ($f->getEnclosure() || File_oembed::staticGet('file_id',$f->id)) { + if ($f->getEnclosure()) { $is_attachment = true; $attachment_id = $f->id; -- cgit v1.2.3-54-g00ecf From 32321de5e048ee50417a6d91b651180c28a801b1 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 14:20:23 -0800 Subject: Some initial testing w/ thumb gen --- js/util.js | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/js/util.js b/js/util.js index 1be3f3053..a4eb0fc28 100644 --- a/js/util.js +++ b/js/util.js @@ -428,30 +428,15 @@ var SN = { // StatusNet }).attr('title', SN.msg('showmore_tooltip')); } else { - $.fn.jOverlay.options = { - method : 'GET', - data : '', - url : '', - color : '#000', - opacity : '0.6', - zIndex : 9999, - center : false, - imgLoading : $('address .url')[0].href+'theme/base/images/illustrations/illu_progress_loading-01.gif', - bgClickToClose : true, - success : function() { - $('#jOverlayContent').append(''); - $('#jOverlayContent button').click($.closeOverlay); - }, - timeout : 0, - autoHide : true, - css : {'max-width':'542px', 'top':'5%', 'left':'32.5%'} - }; + //imgLoading : $('address .url')[0].href+'theme/base/images/illustrations/illu_progress_loading-01.gif', - notice.find('a.attachment').click(function() { + notice.find('a.attachment').each(function() { var attachId = ($(this).attr('id').substring('attachment'.length + 1)); if (attachId) { - $().jOverlay({url: $('address .url')[0].href+'attachment/' + attachId + '/ajax'}); - return false; + var thumbUrl = $('address .url')[0].href+'attachment/' + attachId + '/thumb'; + var thumb = $('
Thumb:
'); + thumb.find('img').attr('src', thumbUrl).last(); + notice.append(thumb); } }); -- cgit v1.2.3-54-g00ecf From 551b196a3572ac9dabcda47abc92db201fb0e6c9 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 15:32:41 -0800 Subject: doomy doom doom --- actions/shownotice.php | 5 ---- js/util.js | 8 ++++-- lib/attachmentlist.php | 64 ++++++++++++++++++++++++++++++++-------------- lib/noticelist.php | 6 +++++ theme/base/css/display.css | 20 ++++++++++++--- 5 files changed, 73 insertions(+), 30 deletions(-) diff --git a/actions/shownotice.php b/actions/shownotice.php index b7e61a137..7a11787b6 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -356,9 +356,4 @@ class SingleNoticeItem extends DoFollowListItem $this->profile->fullname : $this->profile->nickname)); } - - function showNoticeAttachments() { - $al = new AttachmentList($this->notice, $this->out); - $al->show(); - } } diff --git a/js/util.js b/js/util.js index a4eb0fc28..15fb16310 100644 --- a/js/util.js +++ b/js/util.js @@ -431,16 +431,19 @@ var SN = { // StatusNet //imgLoading : $('address .url')[0].href+'theme/base/images/illustrations/illu_progress_loading-01.gif', notice.find('a.attachment').each(function() { + /* var attachId = ($(this).attr('id').substring('attachment'.length + 1)); if (attachId) { var thumbUrl = $('address .url')[0].href+'attachment/' + attachId + '/thumb'; - var thumb = $('
Thumb:
'); + var thumb = $('
Thumb:
'); thumb.find('img').attr('src', thumbUrl).last(); - notice.append(thumb); + notice.find('.entry-title .entry-content').append(thumb); } + */ }); if ($('#shownotice').length == 0) { + /* var t; notice.find('a.thumbnail').hover( function() { @@ -465,6 +468,7 @@ var SN = { // StatusNet $(this).closest('.entry-title').removeClass('ov'); } ); + */ } } }, diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php index f6b09fb49..f29d32ada 100644 --- a/lib/attachmentlist.php +++ b/lib/attachmentlist.php @@ -181,9 +181,11 @@ class AttachmentListItem extends Widget */ function show() { - $this->showStart(); - $this->showNoticeAttachment(); - $this->showEnd(); + if ($this->attachment->isEnclosure()) { + $this->showStart(); + $this->showNoticeAttachment(); + $this->showEnd(); + } } function linkAttr() { @@ -203,9 +205,44 @@ class AttachmentListItem extends Widget } function showRepresentation() { + $thumb = $this->getThumbInfo(); + if ($thumb) { + $thumb = $this->sizeThumb($thumb); + $this->out->element('img', array('alt' => '', 'src' => $thumb->url, 'width' => $thumb->width, 'height' => $thumb->height)); + } + } + + function getThumbInfo() + { $thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id); - if (!empty($thumbnail)) { - $this->out->element('img', array('alt' => '', 'src' => $thumbnail->url, 'width' => $thumbnail->width, 'height' => $thumbnail->height)); + if ($thumbnail) { + return $thumbnail; + } else { + switch ($this->attachment->mimetype) { + case 'image/gif': + case 'image/png': + case 'image/jpg': + case 'image/jpeg': + $thumb = (object)array(); + $thumb->url = $this->attachment->url; + $thumb->width = 100; + $thumb->height = 75; // @fixme + return $thumb; + } + } + return false; + } + + function sizeThumb($thumbnail) { + $maxWidth = 100; + $maxHeight = 75; + if ($thumbnail->width > $maxWidth) { + $thumb = clone($thumbnail); + $thumb->width = $maxWidth; + $thumb->height = intval($thumbnail->height * $maxWidth / $thumbnail->width); + return $thumb; + } else { + return $thumbnail; } } @@ -234,6 +271,9 @@ class AttachmentListItem extends Widget } } +/** + * used for one-off attachment action + */ class Attachment extends AttachmentListItem { function showLink() { @@ -414,18 +454,4 @@ class Attachment extends AttachmentListItem return $scrubbed; } - - function showFallback() - { - // If we don't know how to display an attachment inline, we probably - // shouldn't have gotten to this point. - // - // But, here we are... displaying details on a file or remote URL - // either on the main view or in an ajax-loaded lightbox. As a lesser - // of several evils, we'll try redirecting to the actual target via - // client-side JS. - - common_log(LOG_ERR, "Empty or unknown type for file id {$this->attachment->id}; falling back to client-side redirect."); - $this->out->raw(''); - } } diff --git a/lib/noticelist.php b/lib/noticelist.php index 6f82c9269..fb5db2374 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -208,6 +208,7 @@ class NoticeListItem extends Widget $this->showStart(); if (Event::handle('StartShowNoticeItem', array($this))) { $this->showNotice(); + $this->showNoticeAttachments(); $this->showNoticeInfo(); $this->showNoticeOptions(); Event::handle('EndShowNoticeItem', array($this)); @@ -383,6 +384,11 @@ class NoticeListItem extends Widget $this->out->elementEnd('p'); } + function showNoticeAttachments() { + $al = new AttachmentList($this->notice, $this->out); + $al->show(); + } + /** * show the link to the main page for the notice * diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 7ac66095a..29f7d0ae0 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -1150,7 +1150,8 @@ border-radius:4px; -webkit-border-radius:4px; } -.notice div.entry-content { +.notice div.entry-content, +.notice dl.entry-content { clear:left; float:left; font-size:0.95em; @@ -1325,6 +1326,7 @@ margin-left:4px; .notice .attachment.more { padding-left:0; } +/* .notice .attachment img { position:absolute; top:18px; @@ -1334,20 +1336,30 @@ z-index:99; #shownotice .notice .attachment img { position:static; } +*/ -#attachments { + +/* Small inline attachment list */ +#attachments ol li { + list-style-type: none; +} +#attachments dt { + display: none; +} + +#shownotice #attachments { clear:both; float:left; width:100%; margin-top:18px; } -#attachments dt { +#shownotice #attachments dt { font-weight:bold; font-size:1.3em; margin-bottom:4px; } -#attachments ol li { +#shownotice #attachments ol li { margin-bottom:18px; list-style-type:decimal; float:left; -- cgit v1.2.3-54-g00ecf From a2994e3aa232106f39a6c36c9176608467b8822e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 15:50:06 -0800 Subject: Testing... using photo info for temp thumbnails --- classes/File.php | 7 +++---- lib/attachmentlist.php | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/classes/File.php b/classes/File.php index d71403e64..e3b922d13 100644 --- a/classes/File.php +++ b/classes/File.php @@ -352,11 +352,10 @@ class File extends Memcached_DataObject $mimetype = substr($mimetype,0,$semicolon); } if(in_array($mimetype,$notEnclosureMimeTypes)){ - // Never treat HTML as an enclosure type! - return false; - } else { + // Never treat generic HTML links as an enclosure type! + // But if we have oEmbed info, we'll consider it golden. $oembed = File_oembed::staticGet('file_id',$this->id); - if($oembed){ + if($oembed && in_array($oembed->type, array('photo', 'video'))){ $mimetype = strtolower($oembed->mimetype); $semicolon = strpos($mimetype,';'); if($semicolon){ diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php index f29d32ada..8e6ad038a 100644 --- a/lib/attachmentlist.php +++ b/lib/attachmentlist.php @@ -212,19 +212,30 @@ class AttachmentListItem extends Widget } } + /** + * Pull a thumbnail image reference for the given file. + * In order we check: + * 1) file_thumbnail table (thumbnails found via oEmbed) + * 2) image URL from direct dereference or oEmbed 'photo' type URL + * 3) ??? + * + * @return mixed object with (url, width, height) properties, or false + */ function getThumbInfo() { $thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id); if ($thumbnail) { return $thumbnail; - } else { - switch ($this->attachment->mimetype) { + } + $enc = $this->attachment->getEnclosure(); + if ($enc) { + switch ($enc->mimetype) { case 'image/gif': case 'image/png': case 'image/jpg': case 'image/jpeg': $thumb = (object)array(); - $thumb->url = $this->attachment->url; + $thumb->url = $enc->url; $thumb->width = 100; $thumb->height = 75; // @fixme return $thumb; -- cgit v1.2.3-54-g00ecf From dc497ed090d94561db195983a4346a2f66503422 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 16:51:31 -0800 Subject: Break out ImageFile->resizeTo() from ImageFile->resize(); allows resizing images to non-square sizes and to arbitrary destinations. Will be used for creating thumbnails as well as the originala use of cropping/sizing avatars. --- lib/imagefile.php | 103 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/lib/imagefile.php b/lib/imagefile.php index b70fd248e..159deead6 100644 --- a/lib/imagefile.php +++ b/lib/imagefile.php @@ -115,10 +115,46 @@ class ImageFile return new ImageFile(null, $_FILES[$param]['tmp_name']); } + /** + * Compat interface for old code generating avatar thumbnails... + * Saves the scaled file directly into the avatar area. + * + * @param int $size target width & height -- must be square + * @param int $x (default 0) upper-left corner to crop from + * @param int $y (default 0) upper-left corner to crop from + * @param int $w (default full) width of image area to crop + * @param int $h (default full) height of image area to crop + * @return string filename + */ function resize($size, $x = 0, $y = 0, $w = null, $h = null) + { + $targetType = $this->preferredType($this->type); + $outname = Avatar::filename($this->id, + image_type_to_extension($targetType), + $size, + common_timestamp()); + $outpath = Avatar::path($outname); + $this->resizeTo($outpath, $size, $size, $x, $y, $w, $h); + return $outname; + } + + /** + * Create and save a thumbnail image. + * + * @param string $outpath + * @param int $width target width + * @param int $height target height + * @param int $x (default 0) upper-left corner to crop from + * @param int $y (default 0) upper-left corner to crop from + * @param int $w (default full) width of image area to crop + * @param int $h (default full) height of image area to crop + * @return string full local filesystem filename + */ + function resizeTo($outpath, $width, $height, $x=0, $y=0, $w=null, $h=null) { $w = ($w === null) ? $this->width:$w; $h = ($h === null) ? $this->height:$h; + $targetType = $this->preferredType($this->type); if (!file_exists($this->filepath)) { throw new Exception(_('Lost our file.')); @@ -126,20 +162,16 @@ class ImageFile } // Don't crop/scale if it isn't necessary - if ($size === $this->width - && $size === $this->height + if ($width === $this->width + && $height === $this->height && $x === 0 && $y === 0 && $w === $this->width - && $h === $this->height) { + && $h === $this->height + && $this->type == $targetType) { - $outname = Avatar::filename($this->id, - image_type_to_extension($this->type), - $size, - common_timestamp()); - $outpath = Avatar::path($outname); @copy($this->filepath, $outpath); - return $outname; + return $outpath; } switch ($this->type) { @@ -166,7 +198,7 @@ class ImageFile return; } - $image_dest = imagecreatetruecolor($size, $size); + $image_dest = imagecreatetruecolor($width, $height); if ($this->type == IMAGETYPE_GIF || $this->type == IMAGETYPE_PNG || $this->type == IMAGETYPE_BMP) { @@ -189,30 +221,9 @@ class ImageFile } } - imagecopyresampled($image_dest, $image_src, 0, 0, $x, $y, $size, $size, $w, $h); - - if($this->type == IMAGETYPE_BMP) { - //we don't want to save BMP... it's an inefficient, rare, antiquated format - //save png instead - $this->type = IMAGETYPE_PNG; - } else if($this->type == IMAGETYPE_WBMP) { - //we don't want to save WBMP... it's a rare format that we can't guarantee clients will support - //save png instead - $this->type = IMAGETYPE_PNG; - } else if($this->type == IMAGETYPE_XBM) { - //we don't want to save XBM... it's a rare format that we can't guarantee clients will support - //save png instead - $this->type = IMAGETYPE_PNG; - } - - $outname = Avatar::filename($this->id, - image_type_to_extension($this->type), - $size, - common_timestamp()); - - $outpath = Avatar::path($outname); + imagecopyresampled($image_dest, $image_src, 0, 0, $x, $y, $width, $height, $w, $h); - switch ($this->type) { + switch ($targetType) { case IMAGETYPE_GIF: imagegif($image_dest, $outpath); break; @@ -230,7 +241,31 @@ class ImageFile imagedestroy($image_src); imagedestroy($image_dest); - return $outname; + return $outpath; + } + + /** + * Several obscure file types should be normalized to PNG on resize. + * + * @param int $type + * @return int + */ + function preferredType($type) + { + if($type == IMAGETYPE_BMP) { + //we don't want to save BMP... it's an inefficient, rare, antiquated format + //save png instead + return IMAGETYPE_PNG; + } else if($type == IMAGETYPE_WBMP) { + //we don't want to save WBMP... it's a rare format that we can't guarantee clients will support + //save png instead + return IMAGETYPE_PNG; + } else if($type == IMAGETYPE_XBM) { + //we don't want to save XBM... it's a rare format that we can't guarantee clients will support + //save png instead + return IMAGETYPE_PNG; + } + return $type; } function unlink() -- cgit v1.2.3-54-g00ecf From c36fecb79431218016615cde45215337d67fee67 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 17:20:04 -0800 Subject: Save a thumbnail image when uploading an image file into the file attachments system. Currently hardcoded to 100x75, needs aspect-sensitivity etc. --- classes/File_thumbnail.php | 30 ++++++++++++++++++++++++++---- lib/mediafile.php | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php index edae8ac21..d371b9e8a 100644 --- a/classes/File_thumbnail.php +++ b/classes/File_thumbnail.php @@ -48,12 +48,34 @@ class File_thumbnail extends Memcached_DataObject return array(false, false, false); } - function saveNew($data, $file_id) { + /** + * Save oEmbed-provided thumbnail data + * + * @param object $data + * @param int $file_id + */ + public static function saveNew($data, $file_id) { + self::saveThumbnail($file_id, + $data->thumbnail_url, + $data->thumbnail_width, + $data->thumbnail_height); + } + + /** + * Save a thumbnail record for the referenced file record. + * + * @param int $file_id + * @param string $url + * @param int $width + * @param int $height + */ + static function saveThumbnail($file_id, $url, $width, $height) + { $tn = new File_thumbnail; $tn->file_id = $file_id; - $tn->url = $data->thumbnail_url; - $tn->width = intval($data->thumbnail_width); - $tn->height = intval($data->thumbnail_height); + $tn->url = $url; + $tn->width = intval($width); + $tn->height = intval($height); $tn->insert(); } } diff --git a/lib/mediafile.php b/lib/mediafile.php index aad3575d7..2c04b4650 100644 --- a/lib/mediafile.php +++ b/lib/mediafile.php @@ -48,11 +48,14 @@ class MediaFile { if ($user == null) { $this->user = common_current_user(); + } else { + $this->user = $user; } $this->filename = $filename; $this->mimetype = $mimetype; $this->fileRecord = $this->storeFile(); + $this->thumbnailRecord = $this->storeThumbnail(); $this->fileurl = common_local_url('attachment', array('attachment' => $this->fileRecord->id)); @@ -102,6 +105,38 @@ class MediaFile return $file; } + /** + * Generate and store a thumbnail image for the uploaded file, if applicable. + * + * @return File_thumbnail or null + */ + function storeThumbnail() + { + if (substr($this->mimetype, 0, strlen('image/')) != 'image/') { + // @fixme video thumbs would be nice! + return null; + } + try { + $image = new ImageFile($this->fileRecord->id, + File::path($this->filename)); + } catch (Exception $e) { + // Unsupported image type. + return null; + } + + $outname = File::filename($this->user->getProfile(), 'thumb-' . $this->filename, $this->mimetype); + $outpath = File::path($outname); + + $width = 100; + $height = 75; + + $image->resizeTo($outpath, $width, $height); + File_thumbnail::saveThumbnail($this->fileRecord->id, + File::url($outname), + $width, + $height); + } + function rememberFile($file, $short) { $this->maybeAddRedir($file->id, $short); -- cgit v1.2.3-54-g00ecf From 6d7f02ff31c6c929223030b051541b1bf103f3a8 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 17:22:01 -0800 Subject: Pass file attachment thumbnails along with oEmbed data. --- actions/oembed.php | 6 ++++++ classes/File.php | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/actions/oembed.php b/actions/oembed.php index da3aa0c71..11d814583 100644 --- a/actions/oembed.php +++ b/actions/oembed.php @@ -112,6 +112,12 @@ class OembedAction extends Action //$oembed['width']= //$oembed['height']= $oembed['url']=$attachment->url; + $thumb = $attachment->getThumbnail(); + if ($thumb) { + $oembed['thumbnail_url'] = $thumb->url; + $oembed['thumbnail_width'] = $thumb->width; + $oembed['thumbnail_height'] = $thumb->height; + } }else{ $oembed['type']='link'; $oembed['url']=common_local_url('attachment', diff --git a/classes/File.php b/classes/File.php index e3b922d13..56bc73ab2 100644 --- a/classes/File.php +++ b/classes/File.php @@ -384,4 +384,14 @@ class File extends Memcached_DataObject $enclosure = $this->getEnclosure(); return !empty($enclosure); } + + /** + * Get the attachment's thumbnail record, if any. + * + * @return File_thumbnail + */ + function getThumbnail() + { + return File_thumbnail::staticGet('file_id', $this->id); + } } -- cgit v1.2.3-54-g00ecf From 694448e0aa81edb7b010f102ee9ee0e6961f6f7c Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 17:36:02 -0800 Subject: Add attachments 'thumb_width' and 'thumb_height' settings for inline thumbs, defaulting to 100x75. This is used as the max thumb width/height for oEmbed requests (replacing the old default of 500x400 which was more suitable for the lightbox). --- classes/File_oembed.php | 6 +++--- lib/default.php | 2 ++ lib/mediafile.php | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/classes/File_oembed.php b/classes/File_oembed.php index 4813d5dda..a5540ecfe 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -58,11 +58,11 @@ class File_oembed extends Memcached_DataObject return array(false, false, false); } - function _getOembed($url, $maxwidth = 500, $maxheight = 400) { + function _getOembed($url) { require_once INSTALLDIR.'/extlib/Services/oEmbed.php'; $parameters = array( - 'maxwidth'=>$maxwidth, - 'maxheight'=>$maxheight, + 'maxwidth' => common_config('attachments', 'thumb_width'), + 'maxheight' => common_config('attachments', 'thumb_height'), ); try{ $oEmbed = new Services_oEmbed($url); diff --git a/lib/default.php b/lib/default.php index a19453fce..87f4e45c0 100644 --- a/lib/default.php +++ b/lib/default.php @@ -250,6 +250,8 @@ $default = 'monthly_quota' => 15000000, 'uploads' => true, 'filecommand' => '/usr/bin/file', + 'thumb_width' => 100, + 'thumb_height' => 75, ), 'application' => array('desclimit' => null), diff --git a/lib/mediafile.php b/lib/mediafile.php index 2c04b4650..febf4603a 100644 --- a/lib/mediafile.php +++ b/lib/mediafile.php @@ -127,8 +127,8 @@ class MediaFile $outname = File::filename($this->user->getProfile(), 'thumb-' . $this->filename, $this->mimetype); $outpath = File::path($outname); - $width = 100; - $height = 75; + $width = common_config('attachments', 'thumb_width'); + $height = common_config('attachments', 'thumb_height'); $image->resizeTo($outpath, $width, $height); File_thumbnail::saveThumbnail($this->fileRecord->id, -- cgit v1.2.3-54-g00ecf From 504529e8cd8fbaf5e8e1b980260d1d87d9e880ac Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Nov 2010 17:51:53 -0800 Subject: Keep aspect ratio when generating local thumbnails --- lib/mediafile.php | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/mediafile.php b/lib/mediafile.php index febf4603a..a41d7c76b 100644 --- a/lib/mediafile.php +++ b/lib/mediafile.php @@ -127,8 +127,9 @@ class MediaFile $outname = File::filename($this->user->getProfile(), 'thumb-' . $this->filename, $this->mimetype); $outpath = File::path($outname); - $width = common_config('attachments', 'thumb_width'); - $height = common_config('attachments', 'thumb_height'); + $maxWidth = common_config('attachments', 'thumb_width'); + $maxHeight = common_config('attachments', 'thumb_height'); + list($width, $height) = $this->scaleToFit($image->width, $image->height, $maxWidth, $maxHeight); $image->resizeTo($outpath, $width, $height); File_thumbnail::saveThumbnail($this->fileRecord->id, @@ -137,6 +138,19 @@ class MediaFile $height); } + function scaleToFit($width, $height, $maxWidth, $maxHeight) + { + $aspect = $maxWidth / $maxHeight; + $w1 = $maxWidth; + $h1 = intval($height * $maxWidth / $width); + if ($h1 > $maxHeight) { + $w2 = intval($width * $maxHeight / $height); + $h2 = $maxHeight; + return array($w2, $h2); + } + return array($w1, $h1); + } + function rememberFile($file, $short) { $this->maybeAddRedir($file->id, $short); -- cgit v1.2.3-54-g00ecf From f25accc43ea1e66f290c8bc1d284ae04b4bf004f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Nov 2010 10:45:19 -0800 Subject: split out InlineAttachmentList from AttachmentList --- actions/shownotice.php | 9 ++++ lib/attachmentlist.php | 32 +++++++++------ lib/inlineattachmentlist.php | 97 ++++++++++++++++++++++++++++++++++++++++++++ lib/noticelist.php | 2 +- theme/base/css/display.css | 6 +++ 5 files changed, 133 insertions(+), 13 deletions(-) create mode 100644 lib/inlineattachmentlist.php diff --git a/actions/shownotice.php b/actions/shownotice.php index 7a11787b6..b4af7dbaa 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -331,6 +331,15 @@ class SingleNoticeItem extends DoFollowListItem $this->showEnd(); } + /** + * For our zoomed-in special case we'll use a fuller list + * for the attachment info. + */ + function showNoticeAttachments() { + $al = new AttachmentList($this->notice, $this->out); + $al->show(); + } + /** * show the avatar of the notice's author * diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php index 8e6ad038a..f9ef7499e 100644 --- a/lib/attachmentlist.php +++ b/lib/attachmentlist.php @@ -79,23 +79,33 @@ class AttachmentList extends Widget $atts = new File; $att = $atts->getAttachments($this->notice->id); if (empty($att)) return 0; + $this->showListStart(); + + foreach ($att as $n=>$attachment) { + $item = $this->newListItem($attachment); + $item->show(); + } + + $this->showListEnd(); + + return count($att); + } + + function showListStart() + { $this->out->elementStart('dl', array('id' =>'attachments', 'class' => 'entry-content')); // TRANS: DT element label in attachment list. $this->out->element('dt', null, _('Attachments')); $this->out->elementStart('dd'); $this->out->elementStart('ol', array('class' => 'attachments')); + } - foreach ($att as $n=>$attachment) { - $item = $this->newListItem($attachment); - $item->show(); - } - + function showListEnd() + { $this->out->elementEnd('dd'); $this->out->elementEnd('ol'); $this->out->elementEnd('dl'); - - return count($att); } /** @@ -181,11 +191,9 @@ class AttachmentListItem extends Widget */ function show() { - if ($this->attachment->isEnclosure()) { - $this->showStart(); - $this->showNoticeAttachment(); - $this->showEnd(); - } + $this->showStart(); + $this->showNoticeAttachment(); + $this->showEnd(); } function linkAttr() { diff --git a/lib/inlineattachmentlist.php b/lib/inlineattachmentlist.php new file mode 100644 index 000000000..8b1a1cd9b --- /dev/null +++ b/lib/inlineattachmentlist.php @@ -0,0 +1,97 @@ +. + * + * @category UI + * @package StatusNet + * @author Brion Vibber + * @copyright 2010 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/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +class InlineAttachmentList extends AttachmentList +{ + function showListStart() + { + $this->out->elementStart('div', array('class' => 'entry-content thumbnails')); + } + + function showListEnd() + { + $this->out->elementEnd('div'); + } + + /** + * returns a new list item for the current attachment + * + * @param File $notice the current attachment + * + * @return ListItem a list item for displaying the attachment + */ + function newListItem($attachment) + { + return new InlineAttachmentListItem($attachment, $this->out); + } +} + +class InlineAttachmentListItem extends AttachmentListItem +{ + function show() + { + if ($this->attachment->isEnclosure()) { + parent::show(); + } + } + + function showLink() { + $this->out->elementStart('a', $this->linkAttr()); + $this->showRepresentation(); + $this->out->elementEnd('a'); + } + + /** + * start a single notice. + * + * @return void + */ + function showStart() + { + // XXX: RDFa + // TODO: add notice_type class e.g., notice_video, notice_image + $this->out->elementStart('span', array('class' => 'inline-attachment')); + } + + /** + * finish the notice + * + * Close the last elements in the notice list item + * + * @return void + */ + function showEnd() + { + $this->out->elementEnd('span'); + } +} diff --git a/lib/noticelist.php b/lib/noticelist.php index fb5db2374..d2ac7ed84 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -385,7 +385,7 @@ class NoticeListItem extends Widget } function showNoticeAttachments() { - $al = new AttachmentList($this->notice, $this->out); + $al = new InlineAttachmentList($this->notice, $this->out); $al->show(); } diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 29f7d0ae0..6615e13eb 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -1728,6 +1728,12 @@ width:auto; min-width:0; } +.inline-attachment img { + /* Why on earth is this changed to block at the top? */ + display: inline; + border: solid 1px #aaa; + padding: 1px; +} }/*end of @media screen, projection, tv*/ -- cgit v1.2.3-54-g00ecf From dbb95b76a4d384fd62fd24b4d427baefd007cb90 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Nov 2010 12:04:07 -0800 Subject: Allow YouTube-style media links to be counted as enclosures for purposes of listing attachments/thumbs --- classes/File.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/classes/File.php b/classes/File.php index 56bc73ab2..499c8d72c 100644 --- a/classes/File.php +++ b/classes/File.php @@ -361,15 +361,19 @@ class File extends Memcached_DataObject if($semicolon){ $mimetype = substr($mimetype,0,$semicolon); } - if(in_array($mimetype,$notEnclosureMimeTypes)){ - return false; - }else{ + // @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; } -- cgit v1.2.3-54-g00ecf From a4654bfe9f950fa3ca76d109f21030a03507c3da Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 9 Nov 2010 12:53:57 -0500 Subject: session table was missing from upgrade scripts --- db/074to080.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/db/074to080.sql b/db/074to080.sql index ff0819159..e3631e214 100644 --- a/db/074to080.sql +++ b/db/074to080.sql @@ -107,3 +107,15 @@ create table group_alias ( index group_alias_group_id_idx (group_id) ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; + +create table session ( + + id varchar(32) primary key comment 'session ID', + session_data text comment 'session data', + created datetime not null comment 'date this record was created', + modified timestamp comment 'date this record was modified', + + index session_modified_idx (modified) + +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; + -- cgit v1.2.3-54-g00ecf From 3afb031d9270a29db7f1ac4a964bb4b796759827 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 9 Nov 2010 17:08:11 -0500 Subject: Missing one close-paren in newgroup.php --- actions/newgroup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/newgroup.php b/actions/newgroup.php index 295192036..371e50837 100644 --- a/actions/newgroup.php +++ b/actions/newgroup.php @@ -147,7 +147,7 @@ class NewgroupAction extends Action $this->showForm(sprintf(_m('Description is too long (maximum %d character).', 'Description is too long (maximum %d characters).', User_group::maxDescription(), - User_group::maxDescription())); + User_group::maxDescription()))); return; } else if (!is_null($location) && mb_strlen($location) > 255) { $this->showForm(_('Location is too long (maximum 255 characters).')); -- cgit v1.2.3-54-g00ecf From 5a3d01423d378272218072136f2b3e46b5aa5269 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Nov 2010 16:28:33 -0800 Subject: Cleanup on the CSS for inline attachments; removed some unneeded changes since the split of the inline and regular attachment lists. Removing commented-out code that seems to be for some old thumbnailing system where the thumbnails were hidden popups within the core text (wtf!) --- theme/base/css/display.css | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 6615e13eb..8c364febc 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -1150,8 +1150,7 @@ border-radius:4px; -webkit-border-radius:4px; } -.notice div.entry-content, -.notice dl.entry-content { +.notice div.entry-content { clear:left; float:left; font-size:0.95em; @@ -1326,40 +1325,20 @@ margin-left:4px; .notice .attachment.more { padding-left:0; } -/* -.notice .attachment img { -position:absolute; -top:18px; -left:0; -z-index:99; -} -#shownotice .notice .attachment img { -position:static; -} -*/ - - -/* Small inline attachment list */ -#attachments ol li { - list-style-type: none; -} -#attachments dt { - display: none; -} -#shownotice #attachments { +#attachments { clear:both; float:left; width:100%; margin-top:18px; } -#shownotice #attachments dt { +#attachments dt { font-weight:bold; font-size:1.3em; margin-bottom:4px; } -#shownotice #attachments ol li { +#attachments ol li { margin-bottom:18px; list-style-type:decimal; float:left; -- cgit v1.2.3-54-g00ecf From 592e0bc505c52a38952469bae0a081c224180bd8 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Nov 2010 16:43:37 -0800 Subject: add title attribute on attachment list items --- lib/attachmentlist.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php index f9ef7499e..6e127af86 100644 --- a/lib/attachmentlist.php +++ b/lib/attachmentlist.php @@ -197,7 +197,10 @@ class AttachmentListItem extends Widget } function linkAttr() { - return array('class' => 'attachment', 'href' => $this->attachment->url, 'id' => 'attachment-' . $this->attachment->id); + return array('class' => 'attachment', + 'href' => $this->attachment->url, + 'id' => 'attachment-' . $this->attachment->id, + 'title' => $this->title()); } function showLink() { @@ -244,8 +247,9 @@ class AttachmentListItem extends Widget case 'image/jpeg': $thumb = (object)array(); $thumb->url = $enc->url; - $thumb->width = 100; - $thumb->height = 75; // @fixme + // @fixme use the given width/height aspect + $thumb->width = common_config('attachments', 'thumb_width'); + $thumb->height = common_config('attachments', 'thumb_height'); return $thumb; } } -- cgit v1.2.3-54-g00ecf From 46223da59433e602343169a948bc895977ea253f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 10 Nov 2010 14:31:55 -0800 Subject: CSS class tweak for inline attachment thumbnails to avoid things thinking they're content links --- lib/inlineattachmentlist.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/inlineattachmentlist.php b/lib/inlineattachmentlist.php index 8b1a1cd9b..de5008e87 100644 --- a/lib/inlineattachmentlist.php +++ b/lib/inlineattachmentlist.php @@ -71,6 +71,17 @@ class InlineAttachmentListItem extends AttachmentListItem $this->out->elementEnd('a'); } + /** + * Build HTML attributes for the link + * @return array + */ + function linkAttr() + { + $attr = parent::linkAttr(); + $attr['class'] = 'attachment-thumbnail'; + return $attr; + } + /** * start a single notice. * -- cgit v1.2.3-54-g00ecf From fbd8052d05fda7a967d8440574d2b5013d4e7be1 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 10 Nov 2010 15:26:18 -0800 Subject: Add error logging for a couple send-fail cases in XMPP out --- lib/xmppmanager.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/xmppmanager.php b/lib/xmppmanager.php index 7acd7663a..238696664 100644 --- a/lib/xmppmanager.php +++ b/lib/xmppmanager.php @@ -198,10 +198,12 @@ class XmppManager extends IoManager $this->conn->processTime(0); return true; } else { + common_log(LOG_ERR, __METHOD__ . ' failed: 0 bytes sent'); return false; } } else { // Can't send right now... + common_log(LOG_ERR, __METHOD__ . ' failed: XMPP server connection currently down'); return false; } } -- cgit v1.2.3-54-g00ecf From 09aaf21e8d9be5fc0ed82df0028125c9fd96e48d Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 11 Nov 2010 10:33:26 -0800 Subject: Fix missing close of comment block --- actions/allrss.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actions/allrss.php b/actions/allrss.php index d398c8a6a..573bb4eb2 100644 --- a/actions/allrss.php +++ b/actions/allrss.php @@ -56,6 +56,8 @@ class AllrssAction extends Rss10Action * @param array $args Web and URL arguments * * @return boolean false if user doesn't exist + * + */ function prepare($args) { parent::prepare($args); -- cgit v1.2.3-54-g00ecf From b6af5a25ba61ef3c86a3772abea7ac95778689f7 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 12 Nov 2010 11:46:45 -0500 Subject: don't try to initialize the mapstraction canvas if it doesn't exist --- plugins/Mapstraction/MapstractionPlugin.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Mapstraction/MapstractionPlugin.php b/plugins/Mapstraction/MapstractionPlugin.php index c4ba6464e..d5261d8bc 100644 --- a/plugins/Mapstraction/MapstractionPlugin.php +++ b/plugins/Mapstraction/MapstractionPlugin.php @@ -156,7 +156,8 @@ class MapstractionPlugin extends Plugin ' var user = null; '. (($actionName == 'showstream') ? ' user = scrapeUser(); ' : '') . ' var notices = scrapeNotices(user); ' . - ' showMapstraction($("#map_canvas"), notices); '. + ' var canvas = $("#map_canvas")[0]; ' . + ' if (typeof(canvas) != "undefined") { showMapstraction(canvas, notices); } '. '});'); } -- cgit v1.2.3-54-g00ecf From 62467f51e520439d3ec44ceb6a66a91ad54d77b6 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 12 Nov 2010 12:10:29 -0800 Subject: Drop commented-out code from old lightbox & thumbnail popup stuff --- js/util.js | 44 -------------------------------------------- 1 file changed, 44 deletions(-) diff --git a/js/util.js b/js/util.js index 15fb16310..bf3c43fd8 100644 --- a/js/util.js +++ b/js/util.js @@ -427,50 +427,6 @@ var SN = { // StatusNet return false; }).attr('title', SN.msg('showmore_tooltip')); } - else { - //imgLoading : $('address .url')[0].href+'theme/base/images/illustrations/illu_progress_loading-01.gif', - - notice.find('a.attachment').each(function() { - /* - var attachId = ($(this).attr('id').substring('attachment'.length + 1)); - if (attachId) { - var thumbUrl = $('address .url')[0].href+'attachment/' + attachId + '/thumb'; - var thumb = $('
Thumb:
'); - thumb.find('img').attr('src', thumbUrl).last(); - notice.find('.entry-title .entry-content').append(thumb); - } - */ - }); - - if ($('#shownotice').length == 0) { - /* - var t; - notice.find('a.thumbnail').hover( - function() { - var anchor = $(this); - $('a.thumbnail').children('img').hide(); - anchor.closest(".entry-title").addClass('ov'); - - if (anchor.children('img').length === 0) { - t = setTimeout(function() { - $.get($('address .url')[0].href+'attachment/' + (anchor.attr('id').substring('attachment'.length + 1)) + '/thumbnail', null, function(data) { - anchor.append(data); - }); - }, 500); - } - else { - anchor.children('img').show(); - } - }, - function() { - clearTimeout(t); - $('a.thumbnail').children('img').hide(); - $(this).closest('.entry-title').removeClass('ov'); - } - ); - */ - } - } }, NoticeDataAttach: function() { -- cgit v1.2.3-54-g00ecf From cda59dc1771c2911491c9271662c32f56103347f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 12 Nov 2010 12:10:51 -0800 Subject: drop a comma which isn't actually an error but keeps throwing annoying warnings in netbeans --- js/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/util.js b/js/util.js index bf3c43fd8..74eef4df1 100644 --- a/js/util.js +++ b/js/util.js @@ -56,7 +56,7 @@ var SN = { // StatusNet NoticeDataGeoCookie: 'NoticeDataGeo', NoticeDataGeoSelected: 'notice_data-geo_selected', StatusNetInstance:'StatusNetInstance' - }, + } }, messages: {}, -- cgit v1.2.3-54-g00ecf From cb124fe831a3c77dfca89590ebb8d691685bb573 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 12 Nov 2010 12:24:55 -0800 Subject: Add a quick config setting to disable/enable display of thumbnails in regular notice lists (attachments/show_thumbs) - disabling gives the same display as before this feature was added (but changes to oembed handling are still there, and the lightbox popup is gone) --- lib/default.php | 1 + lib/noticelist.php | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/default.php b/lib/default.php index 87f4e45c0..ece01f2a8 100644 --- a/lib/default.php +++ b/lib/default.php @@ -250,6 +250,7 @@ $default = 'monthly_quota' => 15000000, 'uploads' => true, 'filecommand' => '/usr/bin/file', + 'show_thumbs' => true, // show thumbnails in notice lists for uploaded images, and photos and videos linked remotely that provide oEmbed info 'thumb_width' => 100, 'thumb_height' => 75, ), diff --git a/lib/noticelist.php b/lib/noticelist.php index d2ac7ed84..c6f964662 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -385,8 +385,10 @@ class NoticeListItem extends Widget } function showNoticeAttachments() { - $al = new InlineAttachmentList($this->notice, $this->out); - $al->show(); + if (common_config('attachments', 'show_thumbs')) { + $al = new InlineAttachmentList($this->notice, $this->out); + $al->show(); + } } /** -- cgit v1.2.3-54-g00ecf From 6291e8201f90ed50687f7670edc505645ea55bfb Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 12 Nov 2010 13:06:41 -0800 Subject: Fix for failure edge case in TwitterBridge outgoing repeat/retweets. When the retweet failed with a 403 error (say due to it being a private tweet, which can't be retweeted) we would end up mishandling the return value from our internal error handling. Instead of correctly discarding the message and closing out the queue item, we ended up trying to save a bogus twitter<->local ID mapping, which threw another exception and lead the queue system to re-run it. - Fixed the logic check and return values for the retweet case in broadcast_twitter(). - Added doc comments explaining the return values on some functions in twitter.php - Added check on Notice_to_status::saveNew() for empty input -- throw an exception before we try to actually insert into db. :) --- plugins/TwitterBridge/Notice_to_status.php | 7 +++++++ plugins/TwitterBridge/twitter.php | 32 +++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/plugins/TwitterBridge/Notice_to_status.php b/plugins/TwitterBridge/Notice_to_status.php index 2e32ba963..3b8f816cf 100644 --- a/plugins/TwitterBridge/Notice_to_status.php +++ b/plugins/TwitterBridge/Notice_to_status.php @@ -144,6 +144,7 @@ class Notice_to_status extends Memcached_DataObject /** * Save a mapping between a notice and a status + * Warning: status_id values may not fit in 32-bit integers. * * @param integer $notice_id ID of the notice in StatusNet * @param integer $status_id ID of the status in Twitter @@ -153,12 +154,18 @@ class Notice_to_status extends Memcached_DataObject static function saveNew($notice_id, $status_id) { + if (empty($notice_id)) { + throw new Exception("Invalid notice_id $notice_id"); + } $n2s = Notice_to_status::staticGet('notice_id', $notice_id); if (!empty($n2s)) { return $n2s; } + if (empty($status_id)) { + throw new Exception("Invalid status_id $status_id"); + } $n2s = Notice_to_status::staticGet('status_id', $status_id); if (!empty($n2s)) { diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index cd1ad70b9..b34488069 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -128,6 +128,16 @@ function is_twitter_notice($id) return (!empty($n2s)); } +/** + * Check if we need to broadcast a notice over the Twitter bridge, and + * do so if necessary. Will determine whether to do a straight post or + * a repeat/retweet + * + * This function is meant to be called directly from TwitterQueueHandler. + * + * @param Notice $notice + * @return boolean true if complete or successful, false if we should retry + */ function broadcast_twitter($notice) { $flink = Foreign_link::getByUserID($notice->profile_id, @@ -137,8 +147,13 @@ function broadcast_twitter($notice) if (!empty($flink) && TwitterOAuthClient::isPackedToken($flink->credentials)) { if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) { $retweet = retweet_notice($flink, Notice::staticGet('id', $notice->repeat_of)); - if (!empty($retweet)) { + if (is_object($retweet)) { Notice_to_status::saveNew($notice->id, $retweet->id); + return true; + } else { + // Our error processing will have decided if we need to requeue + // this or can discard safely. + return $retweet; } } else if (is_twitter_bound($notice, $flink)) { return broadcast_oauth($notice, $flink); @@ -148,6 +163,21 @@ function broadcast_twitter($notice) return true; } +/** + * Send a retweet to Twitter for a notice that has been previously bridged + * in or out. + * + * Warning: the return value is not guaranteed to be an object; some error + * conditions will return a 'true' which should be passed on to a calling + * queue handler. + * + * No local information about the resulting retweet is saved: it's up to + * caller to save new mappings etc if appropriate. + * + * @param Foreign_link $flink + * @param Notice $notice + * @return mixed object with resulting Twitter status data on success, or true/false/null on error conditions. + */ function retweet_notice($flink, $notice) { $token = TwitterOAuthClient::unpackToken($flink->credentials); -- cgit v1.2.3-54-g00ecf From 2c4313467f07cae059798ac500ec2a1c31953877 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 12 Nov 2010 14:03:08 -0800 Subject: Save oEmbed photo references as thumbnails if there's not a separate thumbnail_url entry in the return data. This fixes thumb saving for Flickr photo references. --- classes/File_oembed.php | 2 +- classes/File_thumbnail.php | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/classes/File_oembed.php b/classes/File_oembed.php index a5540ecfe..bcb2f7bac 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -120,7 +120,7 @@ class File_oembed extends Memcached_DataObject } } $file_oembed->insert(); - if (!empty($data->thumbnail_url)) { + if (!empty($data->thumbnail_url) || ($data->type == 'photo')) { $ft = File_thumbnail::staticGet('file_id', $file_id); if (!empty($ft)) { common_log(LOG_WARNING, "Strangely, a File_thumbnail object exists for new file $file_id", diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php index d371b9e8a..17bac7f08 100644 --- a/classes/File_thumbnail.php +++ b/classes/File_thumbnail.php @@ -55,10 +55,21 @@ class File_thumbnail extends Memcached_DataObject * @param int $file_id */ public static function saveNew($data, $file_id) { - self::saveThumbnail($file_id, - $data->thumbnail_url, - $data->thumbnail_width, - $data->thumbnail_height); + if (!empty($data->thumbnail_url)) { + // Non-photo types such as video will usually + // show us a thumbnail, though it's not required. + self::saveThumbnail($file_id, + $data->thumbnail_url, + $data->thumbnail_width, + $data->thumbnail_height); + } else if ($data->type == 'photo') { + // The inline photo URL given should also fit within + // our requested thumbnail size, per oEmbed spec. + self::saveThumbnail($file_id, + $data->url, + $data->width, + $data->height); + } } /** -- cgit v1.2.3-54-g00ecf From 2c33fdd2fb98c37798a80a8600798caa9dabcb0e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 12 Nov 2010 14:03:57 -0800 Subject: Only use saved thumbnails for notice list attachment thumbs -- don't attempt to search enclosures for photo types. We now save thumbs directly for oEmbed photos that don't list a separate thumb entry (like Flickr), so it's not needed. Keeps things cleaner :D --- lib/attachmentlist.php | 47 +++++++++++------------------------------------ 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php index 6e127af86..0d56791d7 100644 --- a/lib/attachmentlist.php +++ b/lib/attachmentlist.php @@ -218,55 +218,30 @@ class AttachmentListItem extends Widget function showRepresentation() { $thumb = $this->getThumbInfo(); if ($thumb) { - $thumb = $this->sizeThumb($thumb); $this->out->element('img', array('alt' => '', 'src' => $thumb->url, 'width' => $thumb->width, 'height' => $thumb->height)); } } /** - * Pull a thumbnail image reference for the given file. - * In order we check: - * 1) file_thumbnail table (thumbnails found via oEmbed) - * 2) image URL from direct dereference or oEmbed 'photo' type URL - * 3) ??? + * Pull a thumbnail image reference for the given file, and if necessary + * resize it to match currently thumbnail size settings. * - * @return mixed object with (url, width, height) properties, or false + * @return File_Thumbnail or false/null */ function getThumbInfo() { $thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id); if ($thumbnail) { - return $thumbnail; - } - $enc = $this->attachment->getEnclosure(); - if ($enc) { - switch ($enc->mimetype) { - case 'image/gif': - case 'image/png': - case 'image/jpg': - case 'image/jpeg': - $thumb = (object)array(); - $thumb->url = $enc->url; - // @fixme use the given width/height aspect - $thumb->width = common_config('attachments', 'thumb_width'); - $thumb->height = common_config('attachments', 'thumb_height'); - return $thumb; + $maxWidth = common_config('attachments', 'thumb_width'); + $maxHeight = common_config('attachments', 'thumb_height'); + if ($thumbnail->width > $maxWidth) { + $thumb = clone($thumbnail); + $thumb->width = $maxWidth; + $thumb->height = intval($thumbnail->height * $maxWidth / $thumbnail->width); + return $thumb; } } - return false; - } - - function sizeThumb($thumbnail) { - $maxWidth = 100; - $maxHeight = 75; - if ($thumbnail->width > $maxWidth) { - $thumb = clone($thumbnail); - $thumb->width = $maxWidth; - $thumb->height = intval($thumbnail->height * $maxWidth / $thumbnail->width); - return $thumb; - } else { - return $thumbnail; - } + return $thumbnail; } /** -- cgit v1.2.3-54-g00ecf From 398e622fecdb2b2b6bf6cde975e3978284db62b4 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 12 Nov 2010 17:40:34 -0800 Subject: Save attached URLs when importing a Twitter status: this lets our thumbnail detection handle photos and videos linked to by Twitter posters. --- plugins/TwitterBridge/twitterimport.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugins/TwitterBridge/twitterimport.php b/plugins/TwitterBridge/twitterimport.php index 07a9cf95f..498e9b1fc 100644 --- a/plugins/TwitterBridge/twitterimport.php +++ b/plugins/TwitterBridge/twitterimport.php @@ -189,6 +189,7 @@ class TwitterImport Notice_to_status::saveNew($notice->id, $status->id); $this->saveStatusMentions($notice, $status); + $this->saveStatusAttachments($notice, $status); $notice->blowOnInsert(); @@ -648,4 +649,20 @@ class TwitterImport } } } + + /** + * Record URL links from the notice. Needed to get thumbnail records + * for referenced photo and video posts, etc. + * + * @param Notice $notice + * @param object $status + */ + function saveStatusAttachments($notice, $status) + { + if (!empty($status->entities) && !empty($status->entities->urls)) { + foreach ($status->entities->urls as $url) { + File::processNew($url->url, $notice->id); + } + } + } } \ No newline at end of file -- cgit v1.2.3-54-g00ecf From 4f323efdf7abc5452152a87241e320aca20ce486 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 12 Nov 2010 17:41:35 -0800 Subject: Encapsulate the oEmbed -> oohembed fallback into oEmbedHelper class. Also added a chance to whitelist sites that don't show discovery info but do have oEmbed API endpoints, and to provide alternate APIs for some common services. Newly supported: - TwitPic: added a local function using TwitPic's API, since the oohembed implementation for TwitPic produced invalid output which Services_oEmbed rejects. (bug filed upstream) Tweaked... - Flickr: works, now using whitelist to use their endpoint directly instead of going through oohembed - Youtube: worked around a bug in Services_oEmbed which broke the direct use of API discovery info, so we don't have to use oohembed. Not currently working... - YFrog: whitelisting their endpoint directly as the oohembed output is broken, but this doesn't appear to work currently as I think things are confused by YFrog's servers giving a '204 No Content' response on our HEAD checks on the original link. --- classes/File_oembed.php | 20 +--- lib/oembedhelper.php | 246 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 15 deletions(-) create mode 100644 lib/oembedhelper.php diff --git a/classes/File_oembed.php b/classes/File_oembed.php index bcb2f7bac..b7bf3a5da 100644 --- a/classes/File_oembed.php +++ b/classes/File_oembed.php @@ -59,25 +59,15 @@ class File_oembed extends Memcached_DataObject } function _getOembed($url) { - require_once INSTALLDIR.'/extlib/Services/oEmbed.php'; $parameters = array( 'maxwidth' => common_config('attachments', 'thumb_width'), 'maxheight' => common_config('attachments', 'thumb_height'), ); - try{ - $oEmbed = new Services_oEmbed($url); - $object = $oEmbed->getObject($parameters); - return $object; - }catch(Exception $e){ - try{ - $oEmbed = new Services_oEmbed($url, array( - Services_oEmbed::OPTION_API => common_config('oohembed', 'endpoint') - )); - $object = $oEmbed->getObject($parameters); - return $object; - }catch(Exception $ex){ - return false; - } + try { + return oEmbedHelper::getObject($url, $parameters); + } catch (Exception $e) { + common_log(LOG_ERR, "Error during oembed lookup for $url - " . $e->getMessage()); + return false; } } diff --git a/lib/oembedhelper.php b/lib/oembedhelper.php new file mode 100644 index 000000000..ef2b59214 --- /dev/null +++ b/lib/oembedhelper.php @@ -0,0 +1,246 @@ +. + */ + +if (!defined('STATUSNET')) { + exit(1); +} +require_once INSTALLDIR.'/extlib/Services/oEmbed.php'; + + +/** + * Utility class to wrap Services_oEmbed: + * + * 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', + ); + 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 oohembed.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()) + { + common_log(LOG_INFO, 'QQQ: wtf? ' . $url); + $host = parse_url($url, PHP_URL_HOST); + if (substr($host, 0, 4) == 'www.') { + $host = substr($host, 4); + } + + // Blacklist: systems with no oEmbed API of their own, which are + // either missing from or broken on oohembed.com's proxy. + // we know how to look data up in another way... + if (array_key_exists($host, self::$functionMap)) { + $func = self::$functionMap[$host]; + return call_user_func($func, $url, $params); + } + + // 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, 'QQQ: going to: ' . $api); + } else { + $api = false; + common_log(LOG_INFO, 'QQQ: no map for ' . $host); + } + return self::getObjectFrom($api, $url, $params); + } + + /** + * Actually do an oEmbed lookup to a particular API endpoint, + * or to the autodiscovered target, or to oohembed. + * + * @param mixed $api string or false: oEmbed API endpoint URL + * @param string $url target URL to look up info about + * @param array $params + * @return object + */ + static protected function getObjectFrom($api, $url, $params=array()) + { + $options = array(); + if ($api) { + $options[Services_oEmbed::OPTION_API] = $api; + } + + try { + $oEmbed = new Services_oEmbed_Tweaked($url, $options); + } catch (Services_oEmbed_Exception_NoSupport $e) { + // Discovery failed... fall back to oohembed if enabled. + $oohembed = common_config('oohembed', 'endpoint'); + if ($oohembed) { + $options[Services_oEmbed::OPTION_API] = $oohembed; + $oEmbed = new Services_oEmbed_Tweaked($url, $options); + } else { + throw $e; + } + } + + // And.... let's go look it up! + return $oEmbed->getObject($params); + } + + /** + * 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 Services_oEmbed_Tweaked extends Services_oEmbed +{ + protected function discover($url) + { + $api = parent::discover($url); + if (strpos($api, '?') !== false) { + // Services_oEmbed doesn't expect to find existing params + // on its API endpoint, which may surprise you since the + // spec says discovery URLs should include parameters... :) + // + // Appending a '&' on the end keeps the later-appended '?' + // from breaking whatever the first parameters was. + return $api . '&'; + } + return $api; + } + + public function getObject(array $params = array()) + { + $api = $this->options[self::OPTION_API]; + if (strpos($api, '?') !== false) { + // The Services_oEmbed code appends a '?' on the end, which breaks + // the next parameter which may be something important like + // maxwidth. + // + // Sticking this bogus entry into our parameters gets us past it. + $params = array_merge(array('statusnet' => 1), $params); + } + return parent::getObject($params); + } + +} \ No newline at end of file -- cgit v1.2.3-54-g00ecf