Merge branch 'realtime' into 1.0.x
This commit is contained in:
commit
7f4a9c4145
@ -78,6 +78,9 @@ class ShownoticeAction extends OwnerDesignAction
|
|||||||
function prepare($args)
|
function prepare($args)
|
||||||
{
|
{
|
||||||
parent::prepare($args);
|
parent::prepare($args);
|
||||||
|
if ($this->boolean('ajax')) {
|
||||||
|
StatusNet::setApi(true);
|
||||||
|
}
|
||||||
|
|
||||||
$id = $this->arg('notice');
|
$id = $this->arg('notice');
|
||||||
|
|
||||||
@ -188,22 +191,26 @@ class ShownoticeAction extends OwnerDesignAction
|
|||||||
{
|
{
|
||||||
parent::handle($args);
|
parent::handle($args);
|
||||||
|
|
||||||
if ($this->notice->is_local == Notice::REMOTE_OMB) {
|
if ($this->boolean('ajax')) {
|
||||||
if (!empty($this->notice->url)) {
|
$this->showAjax();
|
||||||
$target = $this->notice->url;
|
} else {
|
||||||
} else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) {
|
if ($this->notice->is_local == Notice::REMOTE_OMB) {
|
||||||
// Old OMB posts saved the remote URL only into the URI field.
|
if (!empty($this->notice->url)) {
|
||||||
$target = $this->notice->uri;
|
$target = $this->notice->url;
|
||||||
} else {
|
} else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) {
|
||||||
// Shouldn't happen.
|
// Old OMB posts saved the remote URL only into the URI field.
|
||||||
$target = false;
|
$target = $this->notice->uri;
|
||||||
}
|
} else {
|
||||||
if ($target && $target != $this->selfUrl()) {
|
// Shouldn't happen.
|
||||||
common_redirect($target, 301);
|
$target = false;
|
||||||
return false;
|
}
|
||||||
|
if ($target && $target != $this->selfUrl()) {
|
||||||
|
common_redirect($target, 301);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
$this->showPage();
|
||||||
}
|
}
|
||||||
$this->showPage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -232,6 +239,21 @@ class ShownoticeAction extends OwnerDesignAction
|
|||||||
$this->elementEnd('ol');
|
$this->elementEnd('ol');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showAjax()
|
||||||
|
{
|
||||||
|
header('Content-Type: text/xml;charset=utf-8');
|
||||||
|
$this->xw->startDocument('1.0', 'UTF-8');
|
||||||
|
$this->elementStart('html');
|
||||||
|
$this->elementStart('head');
|
||||||
|
$this->element('title', null, _('Notice'));
|
||||||
|
$this->elementEnd('head');
|
||||||
|
$this->elementStart('body');
|
||||||
|
$nli = new NoticeListItem($this->notice, $this);
|
||||||
|
$nli->show();
|
||||||
|
$this->elementEnd('body');
|
||||||
|
$this->elementEnd('html');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't show page notice
|
* Don't show page notice
|
||||||
*
|
*
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
As of StatusNet 1.0.x, actual formatting of the notices is done server-side,
|
||||||
|
loaded by AJAX after the real-time notification comes in. This has the drawback
|
||||||
|
that we may make extra HTTP requests and delay incoming notices a little, but
|
||||||
|
means that formatting and internationalization is consistent.
|
||||||
|
|
||||||
== TODO ==
|
== TODO ==
|
||||||
* i18n
|
|
||||||
* Update mark behaviour (on notice send)
|
* Update mark behaviour (on notice send)
|
||||||
* Pause, Send a notice ~ should not update counter
|
* Pause, Send a notice ~ should not update counter
|
||||||
* Pause ~ retain up to 50-100 most recent notices
|
* Pause ~ retain up to 50-100 most recent notices
|
||||||
* Add geo data
|
|
||||||
* Make it work for Conversation page (perhaps a little tricky)
|
* Make it work for Conversation page (perhaps a little tricky)
|
||||||
* IE is updating the counter in document title all the time (Not sure if this
|
* IE is updating the counter in document title all the time (Not sure if this
|
||||||
is still an issue)
|
is still an issue)
|
||||||
|
@ -45,9 +45,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||||||
*/
|
*/
|
||||||
class RealtimePlugin extends Plugin
|
class RealtimePlugin extends Plugin
|
||||||
{
|
{
|
||||||
protected $replyurl = null;
|
protected $showurl = null;
|
||||||
protected $favorurl = null;
|
|
||||||
protected $deleteurl = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When it's time to initialize the plugin, calculate and
|
* When it's time to initialize the plugin, calculate and
|
||||||
@ -56,11 +54,8 @@ class RealtimePlugin extends Plugin
|
|||||||
|
|
||||||
function onInitializePlugin()
|
function onInitializePlugin()
|
||||||
{
|
{
|
||||||
$this->replyurl = common_local_url('newnotice');
|
|
||||||
$this->favorurl = common_local_url('favor');
|
|
||||||
$this->repeaturl = common_local_url('repeat');
|
|
||||||
// FIXME: need to find a better way to pass this pattern in
|
// FIXME: need to find a better way to pass this pattern in
|
||||||
$this->deleteurl = common_local_url('deletenotice',
|
$this->showurl = common_local_url('shownotice',
|
||||||
array('notice' => '0000000000'));
|
array('notice' => '0000000000'));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -323,7 +318,12 @@ class RealtimePlugin extends Plugin
|
|||||||
|
|
||||||
function _getScripts()
|
function _getScripts()
|
||||||
{
|
{
|
||||||
return array(Plugin::staticPath('Realtime', 'realtimeupdate.min.js'));
|
if (common_config('site', 'minify')) {
|
||||||
|
$js = 'realtimeupdate.min.js';
|
||||||
|
} else {
|
||||||
|
$js = 'realtimeupdate.js';
|
||||||
|
}
|
||||||
|
return array(Plugin::staticPath('Realtime', $js));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -354,7 +354,7 @@ class RealtimePlugin extends Plugin
|
|||||||
|
|
||||||
function _updateInitialize($timeline, $user_id)
|
function _updateInitialize($timeline, $user_id)
|
||||||
{
|
{
|
||||||
return "RealtimeUpdate.init($user_id, \"$this->replyurl\", \"$this->favorurl\", \"$this->repeaturl\", \"$this->deleteurl\"); ";
|
return "RealtimeUpdate.init($user_id, \"$this->showurl\"); ";
|
||||||
}
|
}
|
||||||
|
|
||||||
function _connect()
|
function _connect()
|
||||||
|
@ -44,10 +44,7 @@
|
|||||||
*/
|
*/
|
||||||
RealtimeUpdate = {
|
RealtimeUpdate = {
|
||||||
_userid: 0,
|
_userid: 0,
|
||||||
_replyurl: '',
|
_showurl: '',
|
||||||
_favorurl: '',
|
|
||||||
_repeaturl: '',
|
|
||||||
_deleteurl: '',
|
|
||||||
_updatecounter: 0,
|
_updatecounter: 0,
|
||||||
_maxnotices: 50,
|
_maxnotices: 50,
|
||||||
_windowhasfocus: true,
|
_windowhasfocus: true,
|
||||||
@ -66,21 +63,15 @@ RealtimeUpdate = {
|
|||||||
* feed data into the RealtimeUpdate object!
|
* feed data into the RealtimeUpdate object!
|
||||||
*
|
*
|
||||||
* @param {int} userid: local profile ID of the currently logged-in user
|
* @param {int} userid: local profile ID of the currently logged-in user
|
||||||
* @param {String} replyurl: URL for newnotice action, used when generating reply buttons
|
* @param {String} showurl: URL for shownotice action, used when fetching formatting notices.
|
||||||
* @param {String} favorurl: URL for favor action, used when generating fave buttons
|
|
||||||
* @param {String} repeaturl: URL for repeat action, used when generating repeat buttons
|
|
||||||
* @param {String} deleteurl: URL template for deletenotice action, used when generating delete buttons.
|
|
||||||
* This URL contains a stub value of 0000000000 which will be replaced with the notice ID.
|
* This URL contains a stub value of 0000000000 which will be replaced with the notice ID.
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
init: function(userid, replyurl, favorurl, repeaturl, deleteurl)
|
init: function(userid, showurl)
|
||||||
{
|
{
|
||||||
RealtimeUpdate._userid = userid;
|
RealtimeUpdate._userid = userid;
|
||||||
RealtimeUpdate._replyurl = replyurl;
|
RealtimeUpdate._showurl = showurl;
|
||||||
RealtimeUpdate._favorurl = favorurl;
|
|
||||||
RealtimeUpdate._repeaturl = repeaturl;
|
|
||||||
RealtimeUpdate._deleteurl = deleteurl;
|
|
||||||
|
|
||||||
RealtimeUpdate._documenttitle = document.title;
|
RealtimeUpdate._documenttitle = document.title;
|
||||||
|
|
||||||
@ -163,50 +154,51 @@ RealtimeUpdate = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var noticeItem = RealtimeUpdate.makeNoticeItem(data);
|
RealtimeUpdate.makeNoticeItem(data, function(noticeItem) {
|
||||||
var noticeItemID = $(noticeItem).attr('id');
|
var noticeItemID = $(noticeItem).attr('id');
|
||||||
|
|
||||||
var list = $("#notices_primary .notices:first")
|
var list = $("#notices_primary .notices:first")
|
||||||
var prepend = true;
|
var prepend = true;
|
||||||
|
|
||||||
var threaded = list.hasClass('threaded-notices');
|
var threaded = list.hasClass('threaded-notices');
|
||||||
if (threaded && data.in_reply_to_status_id) {
|
if (threaded && data.in_reply_to_status_id) {
|
||||||
// aho!
|
// aho!
|
||||||
var parent = $('#notice-' + data.in_reply_to_status_id);
|
var parent = $('#notice-' + data.in_reply_to_status_id);
|
||||||
if (parent.length == 0) {
|
if (parent.length == 0) {
|
||||||
// @todo fetch the original, insert it, and finish the rest
|
// @todo fetch the original, insert it, and finish the rest
|
||||||
} else {
|
} else {
|
||||||
// Check the parent notice to make sure it's not a reply itself.
|
// Check the parent notice to make sure it's not a reply itself.
|
||||||
// If so, use it's parent as the parent.
|
// If so, use it's parent as the parent.
|
||||||
var parentList = parent.closest('.notices');
|
var parentList = parent.closest('.notices');
|
||||||
if (parentList.hasClass('threaded-replies')) {
|
if (parentList.hasClass('threaded-replies')) {
|
||||||
parent = parentList.closest('.notice');
|
parent = parentList.closest('.notice');
|
||||||
|
}
|
||||||
|
list = parent.find('.threaded-replies');
|
||||||
|
if (list.length == 0) {
|
||||||
|
list = $('<ul class="notices threaded-replies xoxo"></ul>');
|
||||||
|
parent.append(list);
|
||||||
|
}
|
||||||
|
prepend = false;
|
||||||
}
|
}
|
||||||
list = parent.find('.threaded-replies');
|
|
||||||
if (list.length == 0) {
|
|
||||||
list = $('<ul class="notices threaded-replies xoxo"></ul>');
|
|
||||||
parent.append(list);
|
|
||||||
}
|
|
||||||
prepend = false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var newNotice = $(noticeItem);
|
var newNotice = $(noticeItem);
|
||||||
if (prepend) {
|
if (prepend) {
|
||||||
list.prepend(newNotice);
|
list.prepend(newNotice);
|
||||||
} else {
|
|
||||||
var placeholder = list.find('li.notice-reply-placeholder')
|
|
||||||
if (placeholder.length > 0) {
|
|
||||||
newNotice.insertBefore(placeholder)
|
|
||||||
} else {
|
} else {
|
||||||
newNotice.appendTo(list);
|
var placeholder = list.find('li.notice-reply-placeholder')
|
||||||
SN.U.NoticeInlineReplyPlaceholder(parent);
|
if (placeholder.length > 0) {
|
||||||
|
newNotice.insertBefore(placeholder)
|
||||||
|
} else {
|
||||||
|
newNotice.appendTo(list);
|
||||||
|
SN.U.NoticeInlineReplyPlaceholder(parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
newNotice.css({display:"none"}).fadeIn(1000);
|
||||||
newNotice.css({display:"none"}).fadeIn(1000);
|
|
||||||
|
|
||||||
SN.U.NoticeReplyTo($('#'+noticeItemID));
|
SN.U.NoticeReplyTo($('#'+noticeItemID));
|
||||||
SN.U.NoticeWithAttachment($('#'+noticeItemID));
|
SN.U.NoticeWithAttachment($('#'+noticeItemID));
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,86 +255,24 @@ RealtimeUpdate = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a notice HTML block from JSON API-style data.
|
* Builds a notice HTML block from JSON API-style data;
|
||||||
|
* loads data from server, so runs async.
|
||||||
*
|
*
|
||||||
* @param {Object} data: extended JSON API-formatted notice
|
* @param {Object} data: extended JSON API-formatted notice
|
||||||
* @return {String} HTML fragment
|
* @param {function} callback: function(DOMNode) to receive new code
|
||||||
*
|
|
||||||
* @fixme this replicates core StatusNet code, making maintenance harder
|
|
||||||
* @fixme sloppy HTML building (raw concat without escaping)
|
|
||||||
* @fixme no i18n support
|
|
||||||
* @fixme local variables pollute global namespace
|
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
makeNoticeItem: function(data)
|
makeNoticeItem: function(data, callback)
|
||||||
{
|
{
|
||||||
if (data.hasOwnProperty('retweeted_status')) {
|
var url = RealtimeUpdate._showurl.replace('0000000000', data.id);
|
||||||
original = data['retweeted_status'];
|
$.get(url, {ajax: 1}, function(data, textStatus, xhr) {
|
||||||
repeat = data;
|
var notice = $('li.notice:first', data);
|
||||||
data = original;
|
if (notice.length) {
|
||||||
unique = repeat['id'];
|
var node = document._importNode(notice[0], true);
|
||||||
responsible = repeat['user'];
|
callback(node);
|
||||||
} else {
|
}
|
||||||
original = null;
|
});
|
||||||
repeat = null;
|
|
||||||
unique = data['id'];
|
|
||||||
responsible = data['user'];
|
|
||||||
}
|
|
||||||
|
|
||||||
user = data['user'];
|
|
||||||
html = data['html'].replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/&/g,'&');
|
|
||||||
source = data['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=\""+data['url']+"\" >"+
|
|
||||||
"<abbr class=\"published\" title=\""+data['created_at']+"\">a few seconds ago</abbr>"+
|
|
||||||
"</a> "+
|
|
||||||
"<span class=\"source\">"+
|
|
||||||
"from "+
|
|
||||||
"<span class=\"device\">"+source+"</span>"+ // may have a link
|
|
||||||
"</span>";
|
|
||||||
if (data['conversation_url']) {
|
|
||||||
ni = ni+" <a class=\"response\" href=\""+data['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 input = $("form#form_notice fieldset input#token");
|
|
||||||
var session_key = input.val();
|
|
||||||
ni = ni+RealtimeUpdate.makeFavoriteForm(data['id'], session_key);
|
|
||||||
ni = ni+RealtimeUpdate.makeReplyLink(data['id'], data['user']['screen_name']);
|
|
||||||
if (RealtimeUpdate._userid == responsible['id']) {
|
|
||||||
ni = ni+RealtimeUpdate.makeDeleteLink(data['id']);
|
|
||||||
} else if (RealtimeUpdate._userid != user['id']) {
|
|
||||||
ni = ni+RealtimeUpdate.makeRepeatForm(data['id'], session_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ni = ni+"</div>";
|
|
||||||
|
|
||||||
ni = ni+"</li>";
|
|
||||||
return ni;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
2
plugins/Realtime/realtimeupdate.min.js
vendored
2
plugins/Realtime/realtimeupdate.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user