Merge branch '0.9.x' into 1.0.x

This commit is contained in:
Craig Andrews 2010-01-27 13:41:02 -05:00
commit 5224c7d6c2
36 changed files with 992 additions and 370 deletions

View File

@ -115,11 +115,11 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction
$original = clone($profile);
if (empty($this->name)) {
if (!empty($this->name)) {
$profile->fullname = $this->name;
}
if (empty($this->url)) {
if (!empty($this->url)) {
$profile->homepage = $this->url;
}

View File

@ -112,7 +112,7 @@ class ApiStatusesRetweetAction extends ApiAuthAction
$repeat = $this->original->repeat($this->user->id, $this->source);
common_broadcast_notice($repeat);
$this->showNotice($repeat);
}

View File

@ -255,7 +255,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
$upload->attachToNotice($this->notice);
}
common_broadcast_notice($this->notice);
}
$this->showNotice();

View File

@ -201,7 +201,7 @@ class NewnoticeAction extends Action
$upload->attachToNotice($notice);
}
common_broadcast_notice($notice);
if ($this->boolean('ajax')) {
header('Content-Type: text/xml;charset=utf-8');

View File

@ -106,7 +106,7 @@ class RepeatAction extends Action
{
$repeat = $this->notice->repeat($this->user->id, 'web');
common_broadcast_notice($repeat);
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');

View File

@ -120,11 +120,7 @@ class Inbox extends Memcached_DataObject
$notice_id, $user_id));
if ($result) {
$c = self::memcache();
if (!empty($c)) {
$c->delete(self::cacheKey('inbox', 'user_id', $user_id));
}
self::blow('inbox:user_id:%d', $user_id);
}
return $result;

View File

@ -458,4 +458,23 @@ class Memcached_DataObject extends DB_DataObject
return $dsn;
}
static function blow()
{
$c = self::memcache();
if (empty($c)) {
return false;
}
$args = func_get_args();
$format = array_shift($args);
$keyPart = vsprintf($format, $args);
$cacheKey = common_cache_key($keyPart);
return $c->delete($cacheKey);
}
}

View File

@ -94,10 +94,6 @@ class Notice extends Memcached_DataObject
function delete()
{
$this->blowCaches(true);
$this->blowFavesCache(true);
$this->blowSubsCache(true);
// For auditing purposes, save a record that the notice
// was deleted.
@ -109,31 +105,20 @@ class Notice extends Memcached_DataObject
$deleted->created = $this->created;
$deleted->deleted = common_sql_now();
$this->query('BEGIN');
$deleted->insert();
//Null any notices that are replies to this notice
$this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id));
// Clear related records
//Null any notices that are repeats of this notice
//XXX: probably need to uncache these, too
$this->clearReplies();
$this->clearRepeats();
$this->clearFaves();
$this->clearTags();
$this->clearGroupInboxes();
$this->query(sprintf("UPDATE notice set repeat_of = null WHERE repeat_of = %d", $this->id));
// NOTE: we don't clear inboxes
// NOTE: we don't clear queue items
$related = array('Reply',
'Fave',
'Notice_tag',
'Group_inbox',
'Queue_item');
foreach ($related as $cls) {
$inst = new $cls();
$inst->notice_id = $this->id;
$inst->delete();
}
$result = parent::delete();
$this->query('COMMIT');
}
function saveTags()
@ -155,6 +140,7 @@ class Notice extends Memcached_DataObject
foreach(array_unique($hashtags) as $hashtag) {
/* elide characters we don't want in the tag */
$this->saveTag($hashtag);
self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, $tag->tag);
}
return true;
}
@ -172,6 +158,9 @@ class Notice extends Memcached_DataObject
$last_error->message));
return;
}
// if it's saved, blow its cache
$tag->blowCache(false);
}
/**
@ -331,29 +320,45 @@ class Notice extends Memcached_DataObject
}
}
// XXX: do we need to change this for remote users?
$notice->saveTags();
$groups = $notice->saveGroups();
$recipients = $notice->saveReplies();
$notice->addToInboxes($groups, $recipients);
$notice->saveUrls();
Event::handle('EndNoticeSave', array($notice));
}
# Clear the cache for subscribed users, so they'll update at next request
# XXX: someone clever could prepend instead of clearing the cache
$notice->blowOnInsert();
$notice->blowCaches();
$qm = QueueManager::get();
$qm->enqueue($notice, 'distrib');
return $notice;
}
function blowOnInsert()
{
self::blow('profile:notice_ids:%d', $this->profile_id);
self::blow('public');
if ($this->conversation != $this->id) {
self::blow('notice:conversation_ids:%d', $this->conversation);
}
if (!empty($this->repeat_of)) {
self::blow('notice:repeats:%d', $this->repeat_of);
}
$original = Notice::staticGet('id', $this->repeat_of);
if (!empty($original)) {
$originalUser = User::staticGet('id', $original->profile_id);
if (!empty($originalUser)) {
self::blow('user:repeats_of_me:%d', $originalUser->id);
}
}
$profile = Profile::staticGet($this->profile_id);
$profile->blowNoticeCount();
}
/** save all urls in the notice to the db
*
* follow redirects and save all available file information
@ -456,227 +461,6 @@ class Notice extends Memcached_DataObject
return $att;
}
function blowCaches($blowLast=false)
{
$this->blowSubsCache($blowLast);
$this->blowNoticeCache($blowLast);
$this->blowRepliesCache($blowLast);
$this->blowPublicCache($blowLast);
$this->blowTagCache($blowLast);
$this->blowGroupCache($blowLast);
$this->blowConversationCache($blowLast);
$this->blowRepeatCache();
$profile = Profile::staticGet($this->profile_id);
$profile->blowNoticeCount();
}
function blowRepeatCache()
{
if (!empty($this->repeat_of)) {
$cache = common_memcache();
if (!empty($cache)) {
// XXX: only blow if <100 in cache
$ck = common_cache_key('notice:repeats:'.$this->repeat_of);
$result = $cache->delete($ck);
$user = User::staticGet('id', $this->profile_id);
if (!empty($user)) {
$uk = common_cache_key('user:repeated_by_me:'.$user->id);
$cache->delete($uk);
$user->free();
unset($user);
}
$original = Notice::staticGet('id', $this->repeat_of);
if (!empty($original)) {
$originalUser = User::staticGet('id', $original->profile_id);
if (!empty($originalUser)) {
$ouk = common_cache_key('user:repeats_of_me:'.$originalUser->id);
$cache->delete($ouk);
$originalUser->free();
unset($originalUser);
}
$original->free();
unset($original);
}
}
}
}
function blowConversationCache($blowLast=false)
{
$cache = common_memcache();
if ($cache) {
$ck = common_cache_key('notice:conversation_ids:'.$this->conversation);
$cache->delete($ck);
if ($blowLast) {
$cache->delete($ck.';last');
}
}
}
function blowGroupCache($blowLast=false)
{
$cache = common_memcache();
if ($cache) {
$group_inbox = new Group_inbox();
$group_inbox->notice_id = $this->id;
if ($group_inbox->find()) {
while ($group_inbox->fetch()) {
$cache->delete(common_cache_key('user_group:notice_ids:' . $group_inbox->group_id));
if ($blowLast) {
$cache->delete(common_cache_key('user_group:notice_ids:' . $group_inbox->group_id.';last'));
}
$member = new Group_member();
$member->group_id = $group_inbox->group_id;
if ($member->find()) {
while ($member->fetch()) {
$cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id));
$cache->delete(common_cache_key('notice_inbox:by_user_own:' . $member->profile_id));
if (empty($this->repeat_of)) {
$cache->delete(common_cache_key('user:friends_timeline:' . $member->profile_id));
$cache->delete(common_cache_key('user:friends_timeline_own:' . $member->profile_id));
}
if ($blowLast) {
$cache->delete(common_cache_key('notice_inbox:by_user:' . $member->profile_id . ';last'));
$cache->delete(common_cache_key('notice_inbox:by_user_own:' . $member->profile_id . ';last'));
if (empty($this->repeat_of)) {
$cache->delete(common_cache_key('user:friends_timeline:' . $member->profile_id . ';last'));
$cache->delete(common_cache_key('user:friends_timeline_own:' . $member->profile_id . ';last'));
}
}
}
}
}
}
$group_inbox->free();
unset($group_inbox);
}
}
function blowTagCache($blowLast=false)
{
$cache = common_memcache();
if ($cache) {
$tag = new Notice_tag();
$tag->notice_id = $this->id;
if ($tag->find()) {
while ($tag->fetch()) {
$tag->blowCache($blowLast);
$ck = 'profile:notice_ids_tagged:' . $this->profile_id . ':' . $tag->tag;
$cache->delete($ck);
if ($blowLast) {
$cache->delete($ck . ';last');
}
}
}
$tag->free();
unset($tag);
}
}
function blowSubsCache($blowLast=false)
{
$cache = common_memcache();
if ($cache) {
$user = new User();
$UT = common_config('db','type')=='pgsql'?'"user"':'user';
$user->query('SELECT id ' .
"FROM $UT JOIN subscription ON $UT.id = subscription.subscriber " .
'WHERE subscription.subscribed = ' . $this->profile_id);
while ($user->fetch()) {
$cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id));
$cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id));
if (empty($this->repeat_of)) {
$cache->delete(common_cache_key('user:friends_timeline:'.$user->id));
$cache->delete(common_cache_key('user:friends_timeline_own:'.$user->id));
}
if ($blowLast) {
$cache->delete(common_cache_key('notice_inbox:by_user:'.$user->id.';last'));
$cache->delete(common_cache_key('notice_inbox:by_user_own:'.$user->id.';last'));
if (empty($this->repeat_of)) {
$cache->delete(common_cache_key('user:friends_timeline:'.$user->id.';last'));
$cache->delete(common_cache_key('user:friends_timeline_own:'.$user->id.';last'));
}
}
}
$user->free();
unset($user);
}
}
function blowNoticeCache($blowLast=false)
{
if ($this->is_local) {
$cache = common_memcache();
if (!empty($cache)) {
$cache->delete(common_cache_key('profile:notice_ids:'.$this->profile_id));
if ($blowLast) {
$cache->delete(common_cache_key('profile:notice_ids:'.$this->profile_id.';last'));
}
}
}
}
function blowRepliesCache($blowLast=false)
{
$cache = common_memcache();
if ($cache) {
$reply = new Reply();
$reply->notice_id = $this->id;
if ($reply->find()) {
while ($reply->fetch()) {
$cache->delete(common_cache_key('reply:stream:'.$reply->profile_id));
if ($blowLast) {
$cache->delete(common_cache_key('reply:stream:'.$reply->profile_id.';last'));
}
}
}
$reply->free();
unset($reply);
}
}
function blowPublicCache($blowLast=false)
{
if ($this->is_local == Notice::LOCAL_PUBLIC) {
$cache = common_memcache();
if ($cache) {
$cache->delete(common_cache_key('public'));
if ($blowLast) {
$cache->delete(common_cache_key('public').';last');
}
}
}
}
function blowFavesCache($blowLast=false)
{
$cache = common_memcache();
if ($cache) {
$fave = new Fave();
$fave->notice_id = $this->id;
if ($fave->find()) {
while ($fave->fetch()) {
$cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id));
$cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id));
if ($blowLast) {
$cache->delete(common_cache_key('fave:ids_by_user:'.$fave->user_id.';last'));
$cache->delete(common_cache_key('fave:by_user_own:'.$fave->user_id.';last'));
}
}
}
$fave->free();
unset($fave);
}
}
function getStreamByIds($ids)
{
$cache = common_memcache();
@ -999,7 +783,14 @@ class Notice extends Memcached_DataObject
$gi->notice_id = $this->id;
$gi->created = $this->created;
return $gi->insert();
$result = $gi->insert();
if (!result) {
common_log_db_error($gi, 'INSERT', __FILE__);
throw new ServerException(_('Problem saving group inbox.'));
}
self::blow('user_group:notice_ids:%d', $gi->group_id);
}
return true;
@ -1094,7 +885,8 @@ class Notice extends Memcached_DataObject
foreach ($recipientIds as $recipientId) {
$user = User::staticGet('id', $recipientId);
if ($user) {
if (!empty($user)) {
self::blow('reply:stream:%d', $reply->profile_id);
mail_notify_attn($user, $this);
}
}
@ -1553,4 +1345,104 @@ class Notice extends Memcached_DataObject
return $options;
}
function clearReplies()
{
$replyNotice = new Notice();
$replyNotice->reply_to = $this->id;
//Null any notices that are replies to this notice
if ($replyNotice->find()) {
while ($replyNotice->fetch()) {
$orig = clone($replyNotice);
$replyNotice->reply_to = null;
$replyNotice->update($orig);
}
}
// Reply records
$reply = new Reply();
$reply->notice_id = $this->id;
if ($reply->find()) {
while($reply->fetch()) {
self::blow('reply:stream:%d', $reply->profile_id);
$reply->delete();
}
}
$reply->free();
return $ids;
}
function clearRepeats()
{
$repeatNotice = new Notice();
$repeatNotice->repeat_of = $this->id;
//Null any notices that are repeats of this notice
if ($repeatNotice->find()) {
while ($repeatNotice->fetch()) {
$orig = clone($repeatNotice);
$repeatNotice->repeat_of = null;
$repeatNotice->update($orig);
}
}
}
function clearFaves()
{
$fave = new Fave();
$fave->notice_id = $this->id;
if ($fave->find()) {
while ($fave->fetch()) {
self::blow('fave:ids_by_user_own:%d', $fave->user_id);
self::blow('fave:ids_by_user_own:%d;last', $fave->user_id);
self::blow('fave:ids_by_user:%d', $fave->user_id);
self::blow('fave:ids_by_user:%d;last', $fave->user_id);
$fave->delete();
}
}
$fave->free();
}
function clearTags()
{
$tag = new Notice_tag();
$tag->notice_id = $this->id;
if ($tag->find()) {
while ($tag->fetch()) {
self::blow('profile:notice_ids_tagged:%d:%s', $this->profile_id, common_keyize($tag->tag));
self::blow('profile:notice_ids_tagged:%d:%s;last', $this->profile_id, common_keyize($tag->tag));
self::blow('notice_tag:notice_ids:%s', common_keyize($tag->tag));
self::blow('notice_tag:notice_ids:%s;last', common_keyize($tag->tag));
$tag->delete();
}
}
$tag->free();
}
function clearGroupInboxes()
{
$gi = new Group_inbox();
$gi->notice_id = $this->id;
if ($gi->find()) {
while ($gi->fetch()) {
self::blow('user_group:notice_ids:%d', $gi->group_id);
$gi->delete();
}
}
$gi->free();
}
}

View File

@ -86,13 +86,9 @@ class Notice_tag extends Memcached_DataObject
function blowCache($blowLast=false)
{
$cache = common_memcache();
if ($cache) {
$idkey = common_cache_key('notice_tag:notice_ids:' . common_keyize($this->tag));
$cache->delete($idkey);
if ($blowLast) {
$cache->delete($idkey.';last');
}
self::blow('notice_tag:notice_ids:%s', common_keyize($this->tag));
if ($blowLast) {
self::blow('notice_tag:notice_ids:%s;last', common_keyize($this->tag));
}
}

View File

@ -48,6 +48,7 @@ class Status_network extends DB_DataObject
static $cache = null;
static $base = null;
static $wildcard = null;
/**
* @param string $dbhost
@ -187,7 +188,12 @@ class Status_network extends DB_DataObject
$config['db']['database'] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname";
$config['site']['name'] = $sn->sitename;
$config['site']['name'] = $sn->sitename;
$config['site']['nickname'] = $sn->nickname;
self::$wildcard = $wildcard;
$config['site']['wildcard'] =& self::$wildcard;
if (!empty($sn->hostname)) {
$config['site']['server'] = $sn->hostname;
@ -230,4 +236,13 @@ class Status_network extends DB_DataObject
exit;
}
function getServerName()
{
if (!empty($this->hostname)) {
return $this->hostname;
} else {
return $this->nickname . '.' . self::$wildcard;
}
}
}

View File

@ -383,7 +383,7 @@ class User extends Memcached_DataObject
common_config('site', 'name'),
$user->nickname),
'system');
common_broadcast_notice($notice);
}
}

View File

@ -495,7 +495,7 @@ var SN = { // StatusNet
$('#'+SN.C.S.NoticeLocationId).val('');
$('#'+SN.C.S.NoticeDataGeo).attr('checked', false);
$.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled');
$.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled', { path: '/', expires: SN.U.GetFullYear(2029, 0, 1) });
}
function getJSONgeocodeURL(geocodeURL, data) {
@ -537,7 +537,8 @@ var SN = { // StatusNet
NLNU: location.url,
NDG: true
};
$.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue));
$.cookie(SN.C.S.NoticeDataGeoCookie, JSON.stringify(cookieValue), { path: '/', expires: SN.U.GetFullYear(2029, 0, 1) });
});
}
@ -658,6 +659,13 @@ var SN = { // StatusNet
}
return false;
});
},
GetFullYear: function(year, month, day) {
var date = new Date();
date.setFullYear(year, month, day);
return date;
}
},

View File

@ -422,7 +422,7 @@ class RepeatCommand extends Command
$repeat = $notice->repeat($this->user->id, $channel->source);
if ($repeat) {
common_broadcast_notice($repeat);
$channel->output($this->user, sprintf(_('Notice from %s repeated'), $recipient->nickname));
} else {
$channel->error($this->user, _('Error repeating notice.'));
@ -492,7 +492,7 @@ class ReplyCommand extends Command
} else {
$channel->error($this->user, _('Error saving notice.'));
}
common_broadcast_notice($notice);
}
}

View File

@ -30,6 +30,8 @@
$default =
array('site' =>
array('name' => 'Just another StatusNet microblog',
'nickname' => 'statusnet',
'wildcard' => null,
'server' => $_server,
'theme' => 'default',
'path' => $_path,

View File

@ -0,0 +1,84 @@
<?php
/*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, 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') && !defined('LACONICA')) { exit(1); }
/**
* Base class for queue handlers.
*
* As extensions of the Daemon class, each queue handler has the ability
* to launch itself in the background, at which point it'll pass control
* to the configured QueueManager class to poll for updates.
*
* Subclasses must override at least the following methods:
* - transport
* - handle_notice
*/
class DistribQueueHandler
{
/**
* Return transport keyword which identifies items this queue handler
* services; must be defined for all subclasses.
*
* Must be 8 characters or less to fit in the queue_item database.
* ex "email", "jabber", "sms", "irc", ...
*
* @return string
*/
function transport()
{
return 'distrib';
}
/**
* Here's the meat of your queue handler -- you're handed a Notice
* object, which you may do as you will with.
*
* If this function indicates failure, a warning will be logged
* and the item is placed back in the queue to be re-run.
*
* @param Notice $notice
* @return boolean true on success, false on failure
*/
function handle($notice)
{
// XXX: do we need to change this for remote users?
$notice->saveTags();
$groups = $notice->saveGroups();
$recipients = $notice->saveReplies();
$notice->addToInboxes($groups, $recipients);
$notice->saveUrls();
Event::handle('EndNoticeSave', array($notice));
// Enqueue for other handlers
common_enqueue_notice($notice);
return true;
}
}

View File

@ -88,7 +88,7 @@ abstract class IoMaster
$sn = new Status_network();
$sn->find();
while ($sn->fetch()) {
$hosts[] = $sn->hostname;
$hosts[] = $sn->getServerName();
}
return $hosts;
}
@ -102,7 +102,7 @@ abstract class IoMaster
*/
protected function instantiate($class)
{
if (isset($this->singletons[$class])) {
if (is_string($class) && isset($this->singletons[$class])) {
// Already instantiated a multi-site-capable handler.
// Just let it know it should listen to this site too!
$this->singletons[$class]->addSite(common_config('site', 'server'));
@ -129,7 +129,11 @@ abstract class IoMaster
protected function getManager($class)
{
return call_user_func(array($class, 'get'));
if(is_object($class)){
return $class;
} else {
return call_user_func(array($class, 'get'));
}
}
/**
@ -347,7 +351,7 @@ abstract class IoMaster
* for per-queue and per-site records.
*
* @param string $key counter name
* @param array $owners list of owner keys like 'queue:jabber' or 'site:stat01'
* @param array $owners list of owner keys like 'queue:xmpp' or 'site:stat01'
*/
public function stats($key, $owners=array())
{

View File

@ -160,7 +160,7 @@ class MailHandler
foreach($mediafiles as $mf){
$mf->attachToNotice($notice);
}
common_broadcast_notice($notice);
$this->log(LOG_INFO,
'Added notice ' . $notice->id . ' from user ' . $user->nickname);
return true;

View File

@ -362,7 +362,7 @@ class StatusNetOAuthDataStore extends OAuthDataStore
array('is_local' => Notice::REMOTE_OMB,
'uri' => $omb_notice->getIdentifierURI()));
common_broadcast_notice($notice, true);
}
/**

View File

@ -170,7 +170,9 @@ abstract class QueueManager extends IoManager
{
if (isset($this->handlers[$queue])) {
$class = $this->handlers[$queue];
if (class_exists($class)) {
if(is_object($class)) {
return $class;
} else if (class_exists($class)) {
return new $class();
} else {
common_log(LOG_ERR, "Nonexistent handler class '$class' for queue '$queue'");
@ -206,6 +208,7 @@ abstract class QueueManager extends IoManager
$this->connect('plugin', 'PluginQueueHandler');
$this->connect('omb', 'OmbQueueHandler');
$this->connect('ping', 'PingQueueHandler');
$this->connect('distrib', 'DistribQueueHandler');
if (common_config('sms', 'enabled')) {
$this->connect('sms', 'SmsQueueHandler');
}
@ -213,7 +216,7 @@ abstract class QueueManager extends IoManager
// XMPP output handlers...
$this->connect('jabber', 'JabberQueueHandler');
$this->connect('public', 'PublicQueueHandler');
// @fixme this should get an actual queue
//$this->connect('confirm', 'XmppConfirmHandler');
@ -231,7 +234,7 @@ abstract class QueueManager extends IoManager
* Only registered transports will be reliably picked up!
*
* @param string $transport
* @param string $class
* @param string $class class name or object instance
* @param string $group
*/
public function connect($transport, $class, $group='queuedaemon')

View File

@ -294,7 +294,26 @@ class StompQueueManager extends QueueManager
StatusNet::init($site);
}
$item = $this->decode($frame->body);
if (is_numeric($frame->body)) {
$id = intval($frame->body);
$info = "notice $id posted at {$frame->headers['created']} in queue $queue";
$notice = Notice::staticGet('id', $id);
if (empty($notice)) {
$this->_log(LOG_WARNING, "Skipping missing $info");
$this->ack($frame);
$this->commit();
$this->begin();
$this->stats('badnotice', $queue);
return false;
}
$item = $notice;
} else {
// @fixme should we serialize, or json, or what here?
$info = "string posted at {$frame->headers['created']} in queue $queue";
$item = $frame->body;
}
$handler = $this->getHandler($queue);
if (!$handler) {

View File

@ -980,7 +980,7 @@ function common_redirect($url, $code=307)
function common_broadcast_notice($notice, $remote=false)
{
return common_enqueue_notice($notice);
// DO NOTHING!
}
// Stick the notice on the queue

View File

@ -101,7 +101,7 @@ class XmppManager extends IoManager
$this->conn->addEventHandler('reconnect', 'handle_reconnect', $this);
$this->conn->setReconnectTimeout(600);
jabber_send_presence("Send me a message to post a notice", 'available', null, 'available', -1);
jabber_send_presence("Send me a message to post a notice", 'available', null, 'available', 100);
return !is_null($this->conn);
}
@ -233,7 +233,7 @@ class XmppManager extends IoManager
common_log(LOG_NOTICE, 'XMPP reconnected');
$this->conn->processUntil('session_start');
$this->conn->presence(null, 'available', null, 'available', -1);
$this->conn->presence(null, 'available', null, 'available', 100);
}

View File

@ -397,7 +397,7 @@ class FacebookAction extends Action
return;
}
common_broadcast_notice($notice);
}

View File

@ -86,7 +86,7 @@ class ImapPlugin extends Plugin
}
}
function onStartIoManagerClasses(&$classes)
function onStartQueueDaemonIoManagers(&$classes)
{
$classes[] = new ImapManager($this);
}

View File

@ -414,7 +414,15 @@ class MobileProfilePlugin extends WAP20Plugin
return $proto.'://'.$serverpart.'/'.$pathpart.$relative;
}
function onPluginVersion(&$versions)
{
$versions[] = array('name' => 'MobileProfile',
'version' => STATUSNET_VERSION,
'author' => 'Sarven Capadisli',
'homepage' => 'http://status.net/wiki/Plugin:MobileProfile',
'rawdescription' =>
_m('XHTML MobileProfile output for supporting user agents.'));
return true;
}
}
?>

View File

@ -46,8 +46,9 @@ class PoweredByStatusNetPlugin extends Plugin
function onEndAddressData($action)
{
$action->elementStart('span', 'poweredby');
$action->text(_('powered by'));
$action->element('a', array('href' => 'http://status.net/'), 'StatusNet');
$action->raw(sprintf(_m('powered by %s'),
sprintf('<a href="http://status.net/">%s</a>',
_m('StatusNet'))));
$action->elementEnd('span');
return true;

View File

@ -0,0 +1,32 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-01-22 15:03-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: PoweredByStatusNetPlugin.php:49
#, php-format
msgid "powered by %s"
msgstr ""
#: PoweredByStatusNetPlugin.php:51
msgid "StatusNet"
msgstr ""
#: PoweredByStatusNetPlugin.php:64
msgid ""
"Outputs powered by <a href=\"http://status.net/\">StatusNet</a> after site "
"name."
msgstr ""

View File

@ -18,7 +18,8 @@ display:none;
}
.realtime-popup #form_notice label[for=notice_data-attach],
.realtime-popup #form_notice #notice_data-attach {
.realtime-popup #form_notice #notice_data-attach,
.realtime-popup #form_notice label[for=notice_data-geo] {
top:0;
}

View File

@ -20,7 +20,8 @@
* @category Plugin
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2009 Control Yourself, Inc.
* @author Julien C <chaumond@gmail.com>
* @copyright 2009-2010 Control Yourself, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*/
@ -41,6 +42,7 @@ define('TWITTERBRIDGEPLUGIN_VERSION', '0.9');
* @category Plugin
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @author Julien C <chaumond@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
* @link http://twitter.com/
@ -72,6 +74,27 @@ class TwitterBridgePlugin extends Plugin
$m->connect('twitter/authorization',
array('action' => 'twitterauthorization'));
$m->connect('settings/twitter', array('action' => 'twittersettings'));
$m->connect('main/twitterlogin', array('action' => 'twitterlogin'));
return true;
}
/*
* Add a login tab for 'Sign in with Twitter'
*
* @param Action &action the current action
*
* @return void
*/
function onEndLoginGroupNav(&$action)
{
$action_name = $action->trimmed('action');
$action->menuItem(common_local_url('twitterlogin'),
_('Twitter'),
_('Login or register using Twitter'),
'twitterlogin' === $action_name);
return true;
}
@ -108,6 +131,7 @@ class TwitterBridgePlugin extends Plugin
switch ($cls) {
case 'TwittersettingsAction':
case 'TwitterauthorizationAction':
case 'TwitterloginAction':
include_once INSTALLDIR . '/plugins/TwitterBridge/' .
strtolower(mb_substr($cls, 0, -6)) . '.php';
return false;

View File

@ -19,10 +19,11 @@
* 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/>.
*
* @category TwitterauthorizationAction
* @category Plugin
* @package StatusNet
* @author Zach Copely <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @author Zach Copley <zach@status.net>
* @author Julien C <chaumond@gmail.com>
* @copyright 2009-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/
*/
@ -41,15 +42,21 @@ require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
* (Foreign_link) between the StatusNet user and Twitter user and stores the
* access token and secret in the link.
*
* @category Twitter
* @category Plugin
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @author Julien C <chaumond@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://laconi.ca/
*
*/
class TwitterauthorizationAction extends Action
{
var $twuid = null;
var $tw_fields = null;
var $access_token = null;
var $signin = null;
/**
* Initialize class members. Looks for 'oauth_token' parameter.
*
@ -61,6 +68,7 @@ class TwitterauthorizationAction extends Action
{
parent::prepare($args);
$this->signin = $this->boolean('signin');
$this->oauth_token = $this->arg('oauth_token');
return true;
@ -77,28 +85,61 @@ class TwitterauthorizationAction extends Action
{
parent::handle($args);
if (!common_logged_in()) {
$this->clientError(_m('Not logged in.'), 403);
if (common_logged_in()) {
$user = common_current_user();
$flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
// If there's already a foreign link record, it means we already
// have an access token, and this is unecessary. So go back.
if (isset($flink)) {
common_redirect(common_local_url('twittersettings'));
}
}
$user = common_current_user();
$flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// If there's already a foreign link record, it means we already
// have an access token, and this is unecessary. So go back.
// User was not logged in to StatusNet before
if (isset($flink)) {
common_redirect(common_local_url('twittersettings'));
}
$this->twuid = $this->trimmed('twuid');
// $this->oauth_token is only populated once Twitter authorizes our
// request token. If it's empty we're at the beginning of the auth
// process
$this->tw_fields = array('screen_name' => $this->trimmed('tw_fields_screen_name'),
'fullname' => $this->trimmed('tw_fields_fullname'));
if (empty($this->oauth_token)) {
$this->authorizeRequestToken();
$this->access_token = new OAuthToken($this->trimmed('access_token_key'), $this->trimmed('access_token_secret'));
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->showForm(_('There was a problem with your session token. Try again, please.'));
return;
}
if ($this->arg('create')) {
if (!$this->boolean('license')) {
$this->showForm(_('You can\'t register if you don\'t agree to the license.'),
$this->trimmed('newname'));
return;
}
$this->createNewUser();
} else if ($this->arg('connect')) {
$this->connectNewUser();
} else {
common_debug('Twitter Connect Plugin - ' .
print_r($this->args, true));
$this->showForm(_('Something weird happened.'),
$this->trimmed('newname'));
}
} else {
$this->saveAccessToken();
// $this->oauth_token is only populated once Twitter authorizes our
// request token. If it's empty we're at the beginning of the auth
// process
if (empty($this->oauth_token)) {
$this->authorizeRequestToken();
} else {
$this->saveAccessToken();
}
}
}
@ -123,7 +164,7 @@ class TwitterauthorizationAction extends Action
$_SESSION['twitter_request_token'] = $req_tok->key;
$_SESSION['twitter_request_token_secret'] = $req_tok->secret;
$auth_link = $client->getAuthorizeLink($req_tok);
$auth_link = $client->getAuthorizeLink($req_tok, $this->signin);
} catch (OAuthClientException $e) {
$msg = sprintf('OAuth client cURL error - code: %1s, msg: %2s',
@ -150,6 +191,8 @@ class TwitterauthorizationAction extends Action
$this->serverError(_m('Couldn\'t link your Twitter account.'));
}
$twitter_user = null;
try {
$client = new TwitterOAuthClient($_SESSION['twitter_request_token'],
@ -165,40 +208,54 @@ class TwitterauthorizationAction extends Action
$twitter_user = $client->verifyCredentials();
} catch (OAuthClientException $e) {
$msg = sprintf('OAuth client cURL error - code: %1$s, msg: %2$s',
$msg = sprintf('OAuth client error - code: %1$s, msg: %2$s',
$e->getCode(), $e->getMessage());
$this->serverError(_m('Couldn\'t link your Twitter account.'));
}
// Save the access token and Twitter user info
if (common_logged_in()) {
$this->saveForeignLink($atok, $twitter_user);
// Save the access token and Twitter user info
$user = common_current_user();
$this->saveForeignLink($user->id, $twitter_user->id, $atok);
save_twitter_user($twitter_user->id, $twitter_user->name);
} else {
$this->twuid = $twitter_user->id;
$this->tw_fields = array("screen_name" => $twitter_user->screen_name,
"name" => $twitter_user->name);
$this->access_token = $atok;
$this->tryLogin();
}
// Clean up the the mess we made in the session
unset($_SESSION['twitter_request_token']);
unset($_SESSION['twitter_request_token_secret']);
common_redirect(common_local_url('twittersettings'));
if (common_logged_in()) {
common_redirect(common_local_url('twittersettings'));
}
}
/**
* Saves a Foreign_link between Twitter user and local user,
* which includes the access token and secret.
*
* @param OAuthToken $access_token the access token to save
* @param mixed $twitter_user twitter API user object
* @param int $user_id StatusNet user ID
* @param int $twuid Twitter user ID
* @param OAuthToken $token the access token to save
*
* @return nothing
*/
function saveForeignLink($access_token, $twitter_user)
function saveForeignLink($user_id, $twuid, $access_token)
{
$user = common_current_user();
$flink = new Foreign_link();
$flink->user_id = $user->id;
$flink->foreign_id = $twitter_user->id;
$flink->user_id = $user_id;
$flink->foreign_id = $twuid;
$flink->service = TWITTER_SERVICE;
$creds = TwitterOAuthClient::packToken($access_token);
@ -214,10 +271,325 @@ class TwitterauthorizationAction extends Action
if (empty($flink_id)) {
common_log_db_error($flink, 'INSERT', __FILE__);
$this->serverError(_m('Couldn\'t link your Twitter account.'));
$this->serverError(_('Couldn\'t link your Twitter account.'));
}
save_twitter_user($twitter_user->id, $twitter_user->screen_name);
return $flink_id;
}
function showPageNotice()
{
if ($this->error) {
$this->element('div', array('class' => 'error'), $this->error);
} else {
$this->element('div', 'instructions',
sprintf(_('This is the first time you\'ve logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.'), common_config('site', 'name')));
}
}
function title()
{
return _('Twitter Account Setup');
}
function showForm($error=null, $username=null)
{
$this->error = $error;
$this->username = $username;
$this->showPage();
}
function showPage()
{
parent::showPage();
}
function showContent()
{
if (!empty($this->message_text)) {
$this->element('p', null, $this->message);
return;
}
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_twitter_connect',
'class' => 'form_settings',
'action' => common_local_url('twitterauthorization')));
$this->elementStart('fieldset', array('id' => 'settings_twitter_connect_options'));
$this->element('legend', null, _('Connection options'));
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->element('input', array('type' => 'checkbox',
'id' => 'license',
'class' => 'checkbox',
'name' => 'license',
'value' => 'true'));
$this->elementStart('label', array('class' => 'checkbox', 'for' => 'license'));
$this->text(_('My text and files are available under '));
$this->element('a', array('href' => common_config('license', 'url')),
common_config('license', 'title'));
$this->text(_(' except this private data: password, email address, IM address, phone number.'));
$this->elementEnd('label');
$this->elementEnd('li');
$this->elementEnd('ul');
$this->hidden('access_token_key', $this->access_token->key);
$this->hidden('access_token_secret', $this->access_token->secret);
$this->hidden('twuid', $this->twuid);
$this->hidden('tw_fields_screen_name', $this->tw_fields['screen_name']);
$this->hidden('tw_fields_name', $this->tw_fields['name']);
$this->elementStart('fieldset');
$this->hidden('token', common_session_token());
$this->element('legend', null,
_('Create new account'));
$this->element('p', null,
_('Create a new user with this nickname.'));
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->input('newname', _('New nickname'),
($this->username) ? $this->username : '',
_('1-64 lowercase letters or numbers, no punctuation or spaces'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->submit('create', _('Create'));
$this->elementEnd('fieldset');
$this->elementStart('fieldset');
$this->element('legend', null,
_('Connect existing account'));
$this->element('p', null,
_('If you already have an account, login with your username and password to connect it to your Twitter account.'));
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->input('nickname', _('Existing nickname'));
$this->elementEnd('li');
$this->elementStart('li');
$this->password('password', _('Password'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->submit('connect', _('Connect'));
$this->elementEnd('fieldset');
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
function message($msg)
{
$this->message_text = $msg;
$this->showPage();
}
function createNewUser()
{
if (common_config('site', 'closed')) {
$this->clientError(_('Registration not allowed.'));
return;
}
$invite = null;
if (common_config('site', 'inviteonly')) {
$code = $_SESSION['invitecode'];
if (empty($code)) {
$this->clientError(_('Registration not allowed.'));
return;
}
$invite = Invitation::staticGet($code);
if (empty($invite)) {
$this->clientError(_('Not a valid invitation code.'));
return;
}
}
$nickname = $this->trimmed('newname');
if (!Validate::string($nickname, array('min_length' => 1,
'max_length' => 64,
'format' => NICKNAME_FMT))) {
$this->showForm(_('Nickname must have only lowercase letters and numbers and no spaces.'));
return;
}
if (!User::allowed_nickname($nickname)) {
$this->showForm(_('Nickname not allowed.'));
return;
}
if (User::staticGet('nickname', $nickname)) {
$this->showForm(_('Nickname already in use. Try another one.'));
return;
}
$fullname = trim($this->tw_fields['name']);
$args = array('nickname' => $nickname, 'fullname' => $fullname);
if (!empty($invite)) {
$args['code'] = $invite->code;
}
$user = User::register($args);
$result = $this->saveForeignLink($user->id,
$this->twuid,
$this->access_token);
save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
if (!$result) {
$this->serverError(_('Error connecting user to Twitter.'));
return;
}
common_set_user($user);
common_real_login(true);
common_debug('TwitterBridge Plugin - ' .
"Registered new user $user->id from Twitter user $this->twuid");
common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)),
303);
}
function connectNewUser()
{
$nickname = $this->trimmed('nickname');
$password = $this->trimmed('password');
if (!common_check_user($nickname, $password)) {
$this->showForm(_('Invalid username or password.'));
return;
}
$user = User::staticGet('nickname', $nickname);
if (!empty($user)) {
common_debug('TwitterBridge Plugin - ' .
"Legit user to connect to Twitter: $nickname");
}
$result = $this->saveForeignLink($user->id,
$this->twuid,
$this->access_token);
save_twitter_user($this->twuid, $this->tw_fields['screen_name']);
if (!$result) {
$this->serverError(_('Error connecting user to Twitter.'));
return;
}
common_debug('TwitterBridge Plugin - ' .
"Connected Twitter user $this->twuid to local user $user->id");
common_set_user($user);
common_real_login(true);
$this->goHome($user->nickname);
}
function connectUser()
{
$user = common_current_user();
$result = $this->flinkUser($user->id, $this->twuid);
if (empty($result)) {
$this->serverError(_('Error connecting user to Twitter.'));
return;
}
common_debug('TwitterBridge Plugin - ' .
"Connected Twitter user $this->twuid to local user $user->id");
// Return to Twitter connection settings tab
common_redirect(common_local_url('twittersettings'), 303);
}
function tryLogin()
{
common_debug('TwitterBridge Plugin - ' .
"Trying login for Twitter user $this->twuid.");
$flink = Foreign_link::getByForeignID($this->twuid,
TWITTER_SERVICE);
if (!empty($flink)) {
$user = $flink->getUser();
if (!empty($user)) {
common_debug('TwitterBridge Plugin - ' .
"Logged in Twitter user $flink->foreign_id as user $user->id ($user->nickname)");
common_set_user($user);
common_real_login(true);
$this->goHome($user->nickname);
}
} else {
common_debug('TwitterBridge Plugin - ' .
"No flink found for twuid: $this->twuid - new user");
$this->showForm(null, $this->bestNewNickname());
}
}
function goHome($nickname)
{
$url = common_get_returnto();
if ($url) {
// We don't have to return to it again
common_set_returnto(null);
} else {
$url = common_local_url('all',
array('nickname' =>
$nickname));
}
common_redirect($url, 303);
}
function bestNewNickname()
{
if (!empty($this->tw_fields['name'])) {
$nickname = $this->nicknamize($this->tw_fields['name']);
if ($this->isNewNickname($nickname)) {
return $nickname;
}
}
return null;
}
// Given a string, try to make it work as a nickname
function nicknamize($str)
{
$str = preg_replace('/\W/', '', $str);
$str = str_replace(array('-', '_'), '', $str);
return strtolower($str);
}
function isNewNickname($str)
{
if (!Validate::string($str, array('min_length' => 1,
'max_length' => 64,
'format' => NICKNAME_FMT))) {
return false;
}
if (!User::allowed_nickname($str)) {
return false;
}
if (User::staticGet('nickname', $str)) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,97 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* 'Sign in with Twitter' login page
*
* 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/>.
*
* @category Login
* @package StatusNet
* @author Julien Chaumond <chaumond@gmail.com>
* @author Zach Copley <zach@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);
}
require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php';
/**
* Page for logging in with Twitter
*
* @category Login
* @package StatusNet
* @author Julien Chaumond <chaumond@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*
* @see SettingsAction
*/
class TwitterloginAction extends Action
{
function handle($args)
{
parent::handle($args);
if (common_is_real_login()) {
$this->clientError(_('Already logged in.'));
}
$this->showPage();
}
function title()
{
return _('Twitter Login');
}
function getInstructions()
{
return _('Login with your Twitter account');
}
function showPageNotice()
{
$instr = $this->getInstructions();
$output = common_markup_to_html($instr);
$this->elementStart('div', 'instructions');
$this->raw($output);
$this->elementEnd('div');
}
function showContent()
{
$this->elementStart('a', array('href' => common_local_url('twitterauthorization',
null,
array('signin' => true))));
$this->element('img', array('src' => common_path('plugins/TwitterBridge/Sign-in-with-Twitter-lighter.png'),
'alt' => 'Sign in with Twitter'));
$this->elementEnd('a');
}
function showLocalNav()
{
$nav = new LoginGroupNav($this);
$nav->show();
}
}

View File

@ -45,6 +45,7 @@ class TwitterOAuthClient extends OAuthClient
{
public static $requestTokenURL = 'https://twitter.com/oauth/request_token';
public static $authorizeURL = 'https://twitter.com/oauth/authorize';
public static $signinUrl = 'https://twitter.com/oauth/authenticate';
public static $accessTokenURL = 'https://twitter.com/oauth/access_token';
/**
@ -97,9 +98,11 @@ class TwitterOAuthClient extends OAuthClient
*
* @return the link
*/
function getAuthorizeLink($request_token)
function getAuthorizeLink($request_token, $signin = false)
{
return parent::getAuthorizeLink(self::$authorizeURL,
$url = ($signin) ? self::$signinUrl : self::$authorizeURL;
return parent::getAuthorizeLink($url,
$request_token,
common_local_url('twitterauthorization'));
}

View File

@ -121,8 +121,35 @@ class TwittersettingsAction extends ConnectSettingsAction
$this->elementEnd('p');
$this->element('p', 'form_note',
_m('Connected Twitter account'));
$this->elementEnd('fieldset');
$this->submit('remove', _m('Remove'));
$this->elementStart('fieldset');
$this->element('legend', null, _m('Disconnect my account from Twitter'));
if (!$user->password) {
$this->elementStart('p', array('class' => 'form_guide'));
$this->text(_m('Disconnecting your Twitter ' .
'could make it impossible to log in! Please '));
$this->element('a',
array('href' => common_local_url('passwordsettings')),
_m('set a password'));
$this->text(_m(' first.'));
$this->elementEnd('p');
} else {
$note = _m('Keep your %1$s account but disconnect from Twitter. ' .
'You can use your %1$s password to log in.');
$site = common_config('site', 'name');
$this->element('p', 'instructions',
sprintf($note, $site));
$this->submit('disconnect', _m('Disconnect'));
}
$this->elementEnd('fieldset');
@ -205,7 +232,7 @@ class TwittersettingsAction extends ConnectSettingsAction
if ($this->arg('save')) {
$this->savePreferences();
} else if ($this->arg('remove')) {
} else if ($this->arg('disconnect')) {
$this->removeTwitterAccount();
} else {
$this->showForm(_m('Unexpected form submission.'));
@ -231,7 +258,7 @@ class TwittersettingsAction extends ConnectSettingsAction
return;
}
$this->showForm(_m('Twitter account removed.'), true);
$this->showForm(_m('Twitter account disconnected.'), true);
}
/**

View File

@ -86,7 +86,7 @@ class User_flag_profile extends Memcached_DataObject
function keys()
{
return array('profile_id' => 'N', 'user_id' => 'N');
return array('profile_id' => 'K', 'user_id' => 'K');
}
/**
@ -130,6 +130,15 @@ class User_flag_profile extends Memcached_DataObject
return !empty($ufp);
}
/**
* Create a new flag
*
* @param integer $user_id ID of user who's flagging
* @param integer $profile_id ID of profile being flagged
*
* @return boolean success flag
*/
static function create($user_id, $profile_id)
{
$ufp = new User_flag_profile();

View File

@ -2,37 +2,46 @@
*
* @package StatusNet
* @author Sarven Capadisli <csarven@status.net>
* @copyright 2009 StatusNet, Inc.
* @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/
*/
Location of key paths and files under theme/:
== Location of key paths and files ==
<pre><nowiki>
base/css/
base/css/display.css #layout, typography rules
base/images/ #common icons, illustrations
base/images/icons/icons-01.png #main icons file (combined into a single file)
./base/css/
./base/css/display.css
./base/images/
default/css/
default/css/display.css #imports the base stylesheet for layout and adds background images and colour rules
default/logo.png #default site logo for this theme
default/mobilelogo.png #default logo for the mobile output
default/default-avatar-mini.png #24x24 default avatar for minilists
default/default-avatar-stream.png #48x48 default avatar for notice timelines
default/default-avatar-profile.png #96x96 default avatar for the profile page
</nowiki></pre>
./default/css/
./default/css/display.css
./default/images/
./base/display.css contains layout, typography rules:
Only alter this file if you want to change the layout of the site. Please note that, any updates to this in future statusnet releases may not be compatible with your version.
== How to create your own theme ==
./default/css/display.css contains only the background images and colour rules:
This file is a good basis for creating your own theme.
Let's create a theme:
You probably want to do one of the following:
1. To start off, copy over the default theme:
cp -r default mytheme
2. Edit your mytheme stylesheet:
nano mytheme/css/display.css
* If you just want to change the text, link, background, content, sidebar colours, background image:
** Do this from the Admin->Design settings (recommended!). You could also create a directory and a file structure like the default theme, search and replace with your own values. This is more work, but, you can do this if you plan to make additional *minimal* changes.
a) Search and replace your colours and background images, or
b) Create your own layout either importing a separate stylesheet (e.g., change to @import url(base.css);) or simply place it before the rest of the rules.
4. Set /config.php to load 'mytheme':
$config['site']['theme'] = 'mytheme';
* If you want to change the background images and colours:
# Create a directory and a file structure like the default theme.
# Have your stylesheet import base/css/display.css and add your own styles below. It is okay to add *minimal* changes here.
* If you want to create a different layout, typography, background images and colours:
** Create your own theme directory (base or default) with stylesheets and images like.
Finally, enable your theme by selecting it from the Admin->Design interface. You can set site's logo from here as well.

View File

@ -1039,12 +1039,13 @@ overflow:visible;
#showstream .notice div.entry-content {
margin-left:0;
}
#shownotice .notice .entry-title,
#shownotice .notice div.entry-content {
margin-left:110px;
}
#shownotice .notice .entry-title {
margin-left:110px;
font-size:2.2em;
min-height:123px;
}
#shownotice .notice div.entry-content {
margin-left:0;
}
.notice p.entry-content {