diff options
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/LinkPreview/LinkPreviewPlugin.php | 101 | ||||
-rw-r--r-- | plugins/LinkPreview/linkpreview.js | 194 | ||||
-rw-r--r-- | plugins/LinkPreview/oembedproxyaction.php | 84 | ||||
-rw-r--r-- | plugins/Meteor/MeteorPlugin.php | 2 | ||||
-rw-r--r-- | plugins/Meteor/meteorupdater.min.js | 1 | ||||
-rw-r--r-- | plugins/Realtime/RealtimePlugin.php | 2 | ||||
-rw-r--r-- | plugins/Realtime/realtimeupdate.min.js | 1 | ||||
-rw-r--r-- | plugins/TwitterBridge/twitterimport.php | 8 |
8 files changed, 388 insertions, 5 deletions
diff --git a/plugins/LinkPreview/LinkPreviewPlugin.php b/plugins/LinkPreview/LinkPreviewPlugin.php new file mode 100644 index 000000000..39d2c9bf3 --- /dev/null +++ b/plugins/LinkPreview/LinkPreviewPlugin.php @@ -0,0 +1,101 @@ +<?php +/* + * StatusNet - the distributed open-source microblogging tool + * Copyright (C) 2010, StatusNet, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Some UI extras for now... + * + * @package LinkPreviewPlugin + * @maintainer Brion Vibber <brion@status.net> + */ +class LinkPreviewPlugin extends Plugin +{ + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'LinkPreview', + 'version' => STATUSNET_VERSION, + 'author' => 'Brion Vibber', + 'homepage' => 'http://status.net/wiki/Plugin:LinkPreview', + 'rawdescription' => + _m('UI extensions previewing thumbnails from links.')); + + return true; + } + + /** + * Load JS at runtime if we're logged in. + * + * @param Action $action + * @return boolean hook result + */ + function onEndShowScripts($action) + { + $user = common_current_user(); + if ($user && common_config('attachments', 'process_links')) { + $action->script('plugins/LinkPreview/linkpreview.js'); + $data = json_encode(array( + 'api' => common_local_url('oembedproxy'), + 'width' => common_config('attachments', 'thumbwidth'), + 'height' => common_config('attachments', 'thumbheight'), + )); + $action->inlineScript('$(function() {SN.Init.LinkPreview && SN.Init.LinkPreview('.$data.');})'); + } + return true; + } + + /** + * Autoloader + * + * Loads our classes if they're requested. + * + * @param string $cls Class requested + * + * @return boolean hook return + */ + function onAutoload($cls) + { + $lower = strtolower($cls); + switch ($lower) + { + case 'oembedproxyaction': + require_once dirname(__FILE__) . '/' . $lower . '.php'; + return false; + default: + return true; + } + } + + /** + * Hook for RouterInitialized event. + * + * @param Net_URL_Mapper $m URL mapper + * + * @return boolean hook return + */ + function onStartInitializeRouter($m) + { + $m->connect('main/oembed/proxy', + array('action' => 'oembedproxy')); + + return true; + } +} diff --git a/plugins/LinkPreview/linkpreview.js b/plugins/LinkPreview/linkpreview.js new file mode 100644 index 000000000..0c0eb734e --- /dev/null +++ b/plugins/LinkPreview/linkpreview.js @@ -0,0 +1,194 @@ +/** + * (c) 2010 StatusNet, Inc. + */ + +(function() { + var oEmbed = { + api: 'http://oohembed.com/oohembed', + width: 100, + height: 75, + cache: {}, + callbacks: {}, + + /** + * Do a cached oEmbed lookup for the given URL. + * + * @param {String} url + * @param {function} callback + */ + lookup: function(url, callback) + { + if (typeof oEmbed.cache[url] == "object") { + // We already have a successful lookup. + callback(oEmbed.cache[url]); + } else if (typeof oEmbed.callbacks[url] == "undefined") { + // No lookup yet... Start it! + oEmbed.callbacks[url] = [callback]; + + oEmbed.rawLookup(url, function(data) { + oEmbed.cache[url] = data; + var callbacks = oEmbed.callbacks[url]; + oEmbed.callbacks[url] = undefined; + for (var i = 0; i < callbacks.length; i++) { + callbacks[i](data); + } + }); + } else { + // A lookup is in progress. + oEmbed.callbacks[url].push(callback); + } + }, + + /** + * Do an oEmbed lookup for the given URL. + * + * @fixme proxy through ourselves if possible? + * @fixme use the global thumbnail size settings + * + * @param {String} url + * @param {function} callback + */ + rawLookup: function(url, callback) + { + var params = { + url: url, + format: 'json', + maxwidth: oEmbed.width, + maxheight: oEmbed.height, + token: $('#token').val() + }; + $.get(oEmbed.api, params, function(data, xhr) { + callback(data); + }, 'json'); + } + }; + + var LinkPreview = { + links: [], + + /** + * Find URL links from the source text that may be interesting. + * + * @param {String} text + * @return {Array} list of URLs + */ + findLinks: function (text) + { + // @fixme match this to core code + var re = /(?:^| )(https?:\/\/.+?\/.+?)(?= |$)/mg; + var links = []; + var matches; + while ((matches = re.exec(text)) !== null) { + links.push(matches[1]); + } + return links; + }, + + /** + * Start looking up info for a link preview... + * May start async data loads. + * + * @param {String} id + * @param {String} url + */ + prepLinkPreview: function(id, url) + { + oEmbed.lookup(url, function(data) { + var thumb = null; + var width = 100; + if (typeof data.thumbnail_url == "string") { + thumb = data.thumbnail_url; + if (typeof data.thumbnail_width !== "undefined") { + if (data.thumbnail_width < width) { + width = data.thumbnail_width; + } + } + } else if (data.type == 'photo' && typeof data.url == "string") { + thumb = data.url; + if (typeof data.width !== "undefined") { + if (data.width < width) { + width = data.width; + } + } + } + if (thumb) { + var link = $('<span class="inline-attachment"><a><img/></a></span>'); + link.find('a') + .attr('href', url) + .attr('target', '_blank') + .last() + .find('img') + .attr('src', thumb) + .attr('width', width) + .attr('title', data.title || data.url || url); + $('#' + id).append(link); + } + }); + }, + + /** + * Update the live preview section with links found in the given text. + * May start async data loads. + * + * @param {String} text: free-form input text + */ + previewLinks: function(text) + { + var old = LinkPreview.links; + var links = LinkPreview.findLinks(text); + + // Check for existing common elements... + for (var i = 0; i < old.length && i < links.length; i++) { + if (links[i] != old[i]) { + // Change an existing entry! + var id = 'link-preview-' + i; + $('#' + id).html(''); + LinkPreview.prepLinkPreview(id, links[i]); + } + } + if (links.length > old.length) { + // Adding new entries, whee! + for (var i = old.length; i < links.length; i++) { + var id = 'link-preview-' + i; + $('#link-preview').append('<span id="' + id + '"></span>'); + LinkPreview.prepLinkPreview(id, links[i]); + } + } else if (old.length > links.length) { + // Remove preview entries for links that have been removed. + for (var i = links.length; i < old.length; i++) { + var id = 'link-preview-' + i; + $('#' + id).remove(); + } + } + + LinkPreview.links = links; + }, + + /** + * Clear out any link preview data. + */ + clear: function() { + LinkPreview.links = []; + $('#link-preview').empty(); + } + }; + + SN.Init.LinkPreview = function(params) { + if (params.api) oEmbed.api = params.api; + if (params.width) oEmbed.width = params.width; + if (params.height) oEmbed.height = params.height; + + $('#form_notice') + .append('<div id="link-preview" class="thumbnails"></div>') + .bind('reset', function() { + LinkPreview.clear(); + }); + + // Piggyback on the counter update... + var origCounter = SN.U.Counter; + SN.U.Counter = function(form) { + LinkPreview.previewLinks($('#notice_data-text').val()); + return origCounter(form); + } + } +})(); diff --git a/plugins/LinkPreview/oembedproxyaction.php b/plugins/LinkPreview/oembedproxyaction.php new file mode 100644 index 000000000..470f78073 --- /dev/null +++ b/plugins/LinkPreview/oembedproxyaction.php @@ -0,0 +1,84 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * StatusNet-only extensions to the Twitter-like API + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package StatusNet + * @author Brion Vibber <brion@status.net> + * @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') && !defined('LACONICA')) { + exit(1); +} + +/** + * Oembed proxy implementation + * + * This class provides an interface for our JS-side code to pull info on + * links from other sites, using either native oEmbed, our own custom + * handlers, or the oohEmbed.com offsite proxy service as configured. + * + * @category oEmbed + * @package StatusNet + * @author Brion Vibber <brion@status.net> + * @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/ + */ + +class OembedproxyAction extends OembedAction +{ + + function handle($args) + { + // We're not a general oEmbed proxy service; limit to valid sessions. + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token. '. + 'Try again, please.')); + } + + $format = $this->arg('format'); + if ($format && $format != 'json') { + throw new ClientException('Invalid format; only JSON supported.'); + } + + $url = $this->arg('url'); + if (!common_valid_http_url($url)) { + throw new ClientException('Invalid URL.'); + } + + $params = array(); + if ($this->arg('maxwidth')) { + $params['maxwidth'] = $this->arg('maxwidth'); + } + if ($this->arg('maxheight')) { + $params['maxheight'] = $this->arg('maxheight'); + } + + $data = oEmbedHelper::getObject($url, $params); + + $this->init_document('json'); + print json_encode($data); + } + +} diff --git a/plugins/Meteor/MeteorPlugin.php b/plugins/Meteor/MeteorPlugin.php index a48c52b56..1bdccae7a 100644 --- a/plugins/Meteor/MeteorPlugin.php +++ b/plugins/Meteor/MeteorPlugin.php @@ -89,7 +89,7 @@ class MeteorPlugin extends RealtimePlugin { $scripts = parent::_getScripts(); $scripts[] = 'http://'.$this->webserver.(($this->webport == 80) ? '':':'.$this->webport).'/meteor.js'; - $scripts[] = common_path('plugins/Meteor/meteorupdater.js'); + $scripts[] = common_path('plugins/Meteor/meteorupdater.min.js'); return $scripts; } diff --git a/plugins/Meteor/meteorupdater.min.js b/plugins/Meteor/meteorupdater.min.js new file mode 100644 index 000000000..61928ab4f --- /dev/null +++ b/plugins/Meteor/meteorupdater.min.js @@ -0,0 +1 @@ +var MeteorUpdater=function(){return{init:function(c,a,b){Meteor.callbacks.process=function(d){RealtimeUpdate.receive(JSON.parse(d))};Meteor.host=c;Meteor.port=a;Meteor.joinChannel(b,0);Meteor.connect()}}}();
\ No newline at end of file diff --git a/plugins/Realtime/RealtimePlugin.php b/plugins/Realtime/RealtimePlugin.php index 99d1ff9c1..113187e1e 100644 --- a/plugins/Realtime/RealtimePlugin.php +++ b/plugins/Realtime/RealtimePlugin.php @@ -322,7 +322,7 @@ class RealtimePlugin extends Plugin function _getScripts() { - return array('plugins/Realtime/realtimeupdate.js'); + return array('plugins/Realtime/realtimeupdate.min.js'); } /** diff --git a/plugins/Realtime/realtimeupdate.min.js b/plugins/Realtime/realtimeupdate.min.js new file mode 100644 index 000000000..a06c03c4e --- /dev/null +++ b/plugins/Realtime/realtimeupdate.min.js @@ -0,0 +1 @@ +RealtimeUpdate={_userid:0,_replyurl:"",_favorurl:"",_repeaturl:"",_deleteurl:"",_updatecounter:0,_maxnotices:50,_windowhasfocus:true,_documenttitle:"",_paused:false,_queuedNotices:[],init:function(c,b,d,e,a){RealtimeUpdate._userid=c;RealtimeUpdate._replyurl=b;RealtimeUpdate._favorurl=d;RealtimeUpdate._repeaturl=e;RealtimeUpdate._deleteurl=a;RealtimeUpdate._documenttitle=document.title;$(window).bind("focus",function(){RealtimeUpdate._windowhasfocus=true});$(window).bind("blur",function(){$("#notices_primary .notice").removeClass("mark-top");$("#notices_primary .notice:first").addClass("mark-top");RealtimeUpdate._updatecounter=0;document.title=RealtimeUpdate._documenttitle;RealtimeUpdate._windowhasfocus=false;return false})},receive:function(a){if(RealtimeUpdate._paused===false){RealtimeUpdate.purgeLastNoticeItem();RealtimeUpdate.insertNoticeItem(a)}else{RealtimeUpdate._queuedNotices.push(a);RealtimeUpdate.updateQueuedCounter()}RealtimeUpdate.updateWindowCounter()},insertNoticeItem:function(a){if($("#notice-"+a.id).length>0){return}var b=RealtimeUpdate.makeNoticeItem(a);var c=$(b).attr("id");$("#notices_primary .notices").prepend(b);$("#notices_primary .notice:first").css({display:"none"});$("#notices_primary .notice:first").fadeIn(1000);SN.U.NoticeReplyTo($("#"+c));SN.U.NoticeWithAttachment($("#"+c))},purgeLastNoticeItem:function(){if($("#notices_primary .notice").length>RealtimeUpdate._maxnotices){$("#notices_primary .notice:last").remove()}},updateWindowCounter:function(){if(RealtimeUpdate._windowhasfocus===false){RealtimeUpdate._updatecounter+=1;document.title="("+RealtimeUpdate._updatecounter+") "+RealtimeUpdate._documenttitle}},makeNoticeItem:function(c){if(c.hasOwnProperty("retweeted_status")){original=c.retweeted_status;repeat=c;c=original;unique=repeat.id;responsible=repeat.user}else{original=null;repeat=null;unique=c.id;responsible=c.user}user=c.user;html=c.html.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/&/g,"&");source=c.source.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/&/g,"&");ni='<li class="hentry notice" id="notice-'+unique+'"><div class="entry-title"><span class="vcard author"><a href="'+user.profile_url+'" class="url" title="'+user.name+'"><img src="'+user.profile_image_url+'" class="avatar photo" width="48" height="48" alt="'+user.screen_name+'"/><span class="nickname fn">'+user.screen_name+'</span></a></span><p class="entry-content">'+html+'</p></div><div class="entry-content"><a class="timestamp" rel="bookmark" href="'+c.url+'" ><abbr class="published" title="'+c.created_at+'">a few seconds ago</abbr></a> <span class="source">from <span class="device">'+source+"</span></span>";if(c.conversation_url){ni=ni+' <a class="response" href="'+c.conversation_url+'">in context</a>'}if(repeat){ru=repeat.user;ni=ni+'<span class="repeat vcard">Repeated by <a href="'+ru.profile_url+'" class="url"><span class="nickname">'+ru.screen_name+"</span></a></span>"}ni=ni+"</div>";ni=ni+'<div class="notice-options">';if(RealtimeUpdate._userid!=0){var a=$("form#form_notice fieldset input#token");var b=a.val();ni=ni+RealtimeUpdate.makeFavoriteForm(c.id,b);ni=ni+RealtimeUpdate.makeReplyLink(c.id,c.user["screen_name"]);if(RealtimeUpdate._userid==responsible.id){ni=ni+RealtimeUpdate.makeDeleteLink(c.id)}else{if(RealtimeUpdate._userid!=user.id){ni=ni+RealtimeUpdate.makeRepeatForm(c.id,b)}}}ni=ni+"</div>";ni=ni+"</li>";return ni},makeFavoriteForm:function(c,b){var a;a='<form id="favor-'+c+'" class="form_favor" method="post" action="'+RealtimeUpdate._favorurl+'"><fieldset><legend>Favor this notice</legend><input name="token-'+c+'" type="hidden" id="token-'+c+'" value="'+b+'"/><input name="notice" type="hidden" id="notice-n'+c+'" value="'+c+'"/><input type="submit" id="favor-submit-'+c+'" name="favor-submit-'+c+'" class="submit" value="Favor" title="Favor this notice"/></fieldset></form>';return a},makeReplyLink:function(c,a){var b;b='<a class="notice_reply" href="'+RealtimeUpdate._replyurl+"?replyto="+a+'" title="Reply to this notice">Reply <span class="notice_id">'+c+"</span></a>";return b},makeRepeatForm:function(c,b){var a;a='<form id="repeat-'+c+'" class="form_repeat" method="post" action="'+RealtimeUpdate._repeaturl+'"><fieldset><legend>Repeat this notice?</legend><input name="token-'+c+'" type="hidden" id="token-'+c+'" value="'+b+'"/><input name="notice" type="hidden" id="notice-'+c+'" value="'+c+'"/><input type="submit" id="repeat-submit-'+c+'" name="repeat-submit-'+c+'" class="submit" value="Yes" title="Repeat this notice"/></fieldset></form>';return a},makeDeleteLink:function(c){var b,a;a=RealtimeUpdate._deleteurl.replace("0000000000",c);b='<a class="notice_delete" href="'+a+'" title="Delete this notice">Delete</a>';return b},initActions:function(a,b,c){$("#notices_primary").prepend('<ul id="realtime_actions"><li id="realtime_playpause"></li><li id="realtime_timeline"></li></ul>');RealtimeUpdate._pluginPath=c;RealtimeUpdate.initPlayPause();RealtimeUpdate.initAddPopup(a,b,RealtimeUpdate._pluginPath)},initPlayPause:function(){if(typeof(localStorage)=="undefined"){RealtimeUpdate.showPause()}else{if(localStorage.getItem("RealtimeUpdate_paused")==="true"){RealtimeUpdate.showPlay()}else{RealtimeUpdate.showPause()}}},showPause:function(){RealtimeUpdate.setPause(false);RealtimeUpdate.showQueuedNotices();RealtimeUpdate.addNoticesHover();$("#realtime_playpause").remove();$("#realtime_actions").prepend('<li id="realtime_playpause"><button id="realtime_pause" class="pause"></button></li>');$("#realtime_pause").text(SN.msg("realtime_pause")).attr("title",SN.msg("realtime_pause_tooltip")).bind("click",function(){RealtimeUpdate.removeNoticesHover();RealtimeUpdate.showPlay();return false})},showPlay:function(){RealtimeUpdate.setPause(true);$("#realtime_playpause").remove();$("#realtime_actions").prepend('<li id="realtime_playpause"><span id="queued_counter"></span> <button id="realtime_play" class="play"></button></li>');$("#realtime_play").text(SN.msg("realtime_play")).attr("title",SN.msg("realtime_play_tooltip")).bind("click",function(){RealtimeUpdate.showPause();return false})},setPause:function(a){RealtimeUpdate._paused=a;if(typeof(localStorage)!="undefined"){localStorage.setItem("RealtimeUpdate_paused",RealtimeUpdate._paused)}},showQueuedNotices:function(){$.each(RealtimeUpdate._queuedNotices,function(a,b){RealtimeUpdate.insertNoticeItem(b)});RealtimeUpdate._queuedNotices=[];RealtimeUpdate.removeQueuedCounter()},updateQueuedCounter:function(){$("#realtime_playpause #queued_counter").html("("+RealtimeUpdate._queuedNotices.length+")")},removeQueuedCounter:function(){$("#realtime_playpause #queued_counter").empty()},addNoticesHover:function(){$("#notices_primary .notices").hover(function(){if(RealtimeUpdate._paused===false){RealtimeUpdate.showPlay()}},function(){if(RealtimeUpdate._paused===true){RealtimeUpdate.showPause()}})},removeNoticesHover:function(){$("#notices_primary .notices").unbind()},initAddPopup:function(a,b,c){$("#realtime_timeline").append('<button id="realtime_popup"></button>');$("#realtime_popup").text(SN.msg("realtime_popup")).attr("title",SN.msg("realtime_popup_tooltip")).bind("click",function(){window.open(a,"","toolbar=no,resizable=yes,scrollbars=yes,status=no,menubar=no,personalbar=no,location=no,width=500,height=550");return false})},initPopupWindow:function(){$(".notices .entry-title a, .notices .entry-content a").bind("click",function(){window.open(this.href,"");return false});$("#showstream .entity_profile").css({width:"69%"})}};
\ No newline at end of file diff --git a/plugins/TwitterBridge/twitterimport.php b/plugins/TwitterBridge/twitterimport.php index 498e9b1fc..9e53849d8 100644 --- a/plugins/TwitterBridge/twitterimport.php +++ b/plugins/TwitterBridge/twitterimport.php @@ -659,9 +659,11 @@ class TwitterImport */ function saveStatusAttachments($notice, $status) { - if (!empty($status->entities) && !empty($status->entities->urls)) { - foreach ($status->entities->urls as $url) { - File::processNew($url->url, $notice->id); + if (common_config('attachments', 'process_links')) { + if (!empty($status->entities) && !empty($status->entities->urls)) { + foreach ($status->entities->urls as $url) { + File::processNew($url->url, $notice->id); + } } } } |