Merge branch '1.0.x' into profile-fixups

* 1.0.x:
  For good measure; trip short error mode in earlier on ajax shownotice
  Realtime plugin: fix i18n, thumbnails, location display, OStatus server display, micro-apps display.
  'ajax' param on shownotice action so we can pull items in full html version for realtime
  Whoops, need to reset the background color on the aside.
  Realtime work in progress: switch makeNoticeData to async -- next we'll load fresh copies from server, maintaining proper language and plugin enhancements.
  Check the site minifications etting for realtime plugin; debugging aid.
This commit is contained in:
Zach Copley 2011-03-15 09:11:31 -07:00
commit 4f217a3133
6 changed files with 105 additions and 149 deletions

View File

@ -78,6 +78,9 @@ class ShownoticeAction extends OwnerDesignAction
function prepare($args)
{
parent::prepare($args);
if ($this->boolean('ajax')) {
StatusNet::setApi(true);
}
$id = $this->arg('notice');
@ -188,22 +191,26 @@ class ShownoticeAction extends OwnerDesignAction
{
parent::handle($args);
if ($this->notice->is_local == Notice::REMOTE_OMB) {
if (!empty($this->notice->url)) {
$target = $this->notice->url;
} else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) {
// Old OMB posts saved the remote URL only into the URI field.
$target = $this->notice->uri;
} else {
// Shouldn't happen.
$target = false;
}
if ($target && $target != $this->selfUrl()) {
common_redirect($target, 301);
return false;
if ($this->boolean('ajax')) {
$this->showAjax();
} else {
if ($this->notice->is_local == Notice::REMOTE_OMB) {
if (!empty($this->notice->url)) {
$target = $this->notice->url;
} else if (!empty($this->notice->uri) && preg_match('/^https?:/', $this->notice->uri)) {
// Old OMB posts saved the remote URL only into the URI field.
$target = $this->notice->uri;
} else {
// Shouldn't happen.
$target = 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');
}
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
*

View File

@ -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 ==
* i18n
* Update mark behaviour (on notice send)
* Pause, Send a notice ~ should not update counter
* Pause ~ retain up to 50-100 most recent notices
* Add geo data
* 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
is still an issue)

View File

@ -45,9 +45,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
*/
class RealtimePlugin extends Plugin
{
protected $replyurl = null;
protected $favorurl = null;
protected $deleteurl = null;
protected $showurl = null;
/**
* When it's time to initialize the plugin, calculate and
@ -56,11 +54,8 @@ class RealtimePlugin extends Plugin
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
$this->deleteurl = common_local_url('deletenotice',
$this->showurl = common_local_url('shownotice',
array('notice' => '0000000000'));
return true;
}
@ -323,7 +318,12 @@ class RealtimePlugin extends Plugin
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)
{
return "RealtimeUpdate.init($user_id, \"$this->replyurl\", \"$this->favorurl\", \"$this->repeaturl\", \"$this->deleteurl\"); ";
return "RealtimeUpdate.init($user_id, \"$this->showurl\"); ";
}
function _connect()

View File

@ -44,10 +44,7 @@
*/
RealtimeUpdate = {
_userid: 0,
_replyurl: '',
_favorurl: '',
_repeaturl: '',
_deleteurl: '',
_showurl: '',
_updatecounter: 0,
_maxnotices: 50,
_windowhasfocus: true,
@ -66,21 +63,15 @@ RealtimeUpdate = {
* feed data into the RealtimeUpdate object!
*
* @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} 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.
* @param {String} showurl: URL for shownotice action, used when fetching formatting notices.
* This URL contains a stub value of 0000000000 which will be replaced with the notice ID.
*
* @access public
*/
init: function(userid, replyurl, favorurl, repeaturl, deleteurl)
init: function(userid, showurl)
{
RealtimeUpdate._userid = userid;
RealtimeUpdate._replyurl = replyurl;
RealtimeUpdate._favorurl = favorurl;
RealtimeUpdate._repeaturl = repeaturl;
RealtimeUpdate._deleteurl = deleteurl;
RealtimeUpdate._showurl = showurl;
RealtimeUpdate._documenttitle = document.title;
@ -163,50 +154,51 @@ RealtimeUpdate = {
return;
}
var noticeItem = RealtimeUpdate.makeNoticeItem(data);
var noticeItemID = $(noticeItem).attr('id');
RealtimeUpdate.makeNoticeItem(data, function(noticeItem) {
var noticeItemID = $(noticeItem).attr('id');
var list = $("#notices_primary .notices:first")
var prepend = true;
var list = $("#notices_primary .notices:first")
var prepend = true;
var threaded = list.hasClass('threaded-notices');
if (threaded && data.in_reply_to_status_id) {
// aho!
var parent = $('#notice-' + data.in_reply_to_status_id);
if (parent.length == 0) {
// @todo fetch the original, insert it, and finish the rest
} else {
// Check the parent notice to make sure it's not a reply itself.
// If so, use it's parent as the parent.
var parentList = parent.closest('.notices');
if (parentList.hasClass('threaded-replies')) {
parent = parentList.closest('.notice');
var threaded = list.hasClass('threaded-notices');
if (threaded && data.in_reply_to_status_id) {
// aho!
var parent = $('#notice-' + data.in_reply_to_status_id);
if (parent.length == 0) {
// @todo fetch the original, insert it, and finish the rest
} else {
// Check the parent notice to make sure it's not a reply itself.
// If so, use it's parent as the parent.
var parentList = parent.closest('.notices');
if (parentList.hasClass('threaded-replies')) {
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);
if (prepend) {
list.prepend(newNotice);
} else {
var placeholder = list.find('li.notice-reply-placeholder')
if (placeholder.length > 0) {
newNotice.insertBefore(placeholder)
var newNotice = $(noticeItem);
if (prepend) {
list.prepend(newNotice);
} else {
newNotice.appendTo(list);
SN.U.NoticeInlineReplyPlaceholder(parent);
var placeholder = list.find('li.notice-reply-placeholder')
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.NoticeWithAttachment($('#'+noticeItemID));
SN.U.NoticeReplyTo($('#'+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
* @return {String} HTML fragment
*
* @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
* @param {function} callback: function(DOMNode) to receive new code
*
* @access private
*/
makeNoticeItem: function(data)
makeNoticeItem: function(data, callback)
{
if (data.hasOwnProperty('retweeted_status')) {
original = data['retweeted_status'];
repeat = data;
data = original;
unique = repeat['id'];
responsible = repeat['user'];
} else {
original = null;
repeat = null;
unique = data['id'];
responsible = data['user'];
}
user = data['user'];
html = data['html'].replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&quot;/g,'"').replace(/&amp;/g,'&');
source = data['source'].replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&quot;/g,'"').replace(/&amp;/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;
var url = RealtimeUpdate._showurl.replace('0000000000', data.id);
$.get(url, {ajax: 1}, function(data, textStatus, xhr) {
var notice = $('li.notice:first', data);
if (notice.length) {
var node = document._importNode(notice[0], true);
callback(node);
}
});
},
/**

File diff suppressed because one or more lines are too long

View File

@ -392,6 +392,7 @@ address {
left: 802px;
padding: 22px 10px 40px 10px;
margin-top: 0px;
background: none;
}
#aside_primary .section {