forked from GNUsocial/gnu-social
Merge branch '1.0.x' of gitorious.org:statusnet/mainline into 1.0.x
This commit is contained in:
commit
e7bbe6c1b6
@ -78,7 +78,13 @@ class Memcached_DataObject extends Safe_DataObject
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
foreach ($kv as $k => $v) {
|
foreach ($kv as $k => $v) {
|
||||||
$i->$k = $v;
|
if (is_null($v)) {
|
||||||
|
// XXX: possible SQL injection...? Don't
|
||||||
|
// pass keys from the browser, eh.
|
||||||
|
$i->whereAdd("$k is null");
|
||||||
|
} else {
|
||||||
|
$i->$k = $v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($i->find(true)) {
|
if ($i->find(true)) {
|
||||||
$i->encache();
|
$i->encache();
|
||||||
|
2
js/util.min.js
vendored
2
js/util.min.js
vendored
File diff suppressed because one or more lines are too long
@ -20,7 +20,7 @@
|
|||||||
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
define('STATUSNET_BASE_VERSION', '1.0.0');
|
define('STATUSNET_BASE_VERSION', '1.0.0');
|
||||||
define('STATUSNET_LIFECYCLE', 'alpha5'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
|
define('STATUSNET_LIFECYCLE', 'beta1'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
|
||||||
define('STATUSNET_VERSION', STATUSNET_BASE_VERSION . STATUSNET_LIFECYCLE);
|
define('STATUSNET_VERSION', STATUSNET_BASE_VERSION . STATUSNET_LIFECYCLE);
|
||||||
|
|
||||||
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
|
define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* StatusNet, the distributed open-source microblogging tool
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
*
|
*
|
||||||
* Plugin to do "real time" updates using Comet/Bayeux
|
* Plugin to do "real time" updates using Meteor
|
||||||
*
|
*
|
||||||
* PHP version 5
|
* PHP version 5
|
||||||
*
|
*
|
||||||
@ -156,7 +156,7 @@ class MeteorPlugin extends RealtimePlugin
|
|||||||
'homepage' => 'http://status.net/wiki/Plugin:Meteor',
|
'homepage' => 'http://status.net/wiki/Plugin:Meteor',
|
||||||
'rawdescription' =>
|
'rawdescription' =>
|
||||||
// TRANS: Plugin description.
|
// TRANS: Plugin description.
|
||||||
_m('Plugin to do "real time" updates using Comet/Bayeux.'));
|
_m('Plugin to do "real time" updates using Meteor.'));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,57 @@ class RealtimePlugin extends Plugin
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onCheckSchema()
|
||||||
|
{
|
||||||
|
$schema = Schema::get();
|
||||||
|
$schema->ensureTable('realtime_channel', Realtime_channel::schemaDef());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAutoload($cls)
|
||||||
|
{
|
||||||
|
$dir = dirname(__FILE__);
|
||||||
|
|
||||||
|
switch ($cls)
|
||||||
|
{
|
||||||
|
case 'KeepalivechannelAction':
|
||||||
|
case 'ClosechannelAction':
|
||||||
|
include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
|
||||||
|
return false;
|
||||||
|
case 'Realtime_channel':
|
||||||
|
include_once $dir.'/'.$cls.'.php';
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook for RouterInitialized event.
|
||||||
|
*
|
||||||
|
* @param Net_URL_Mapper $m path-to-action mapper
|
||||||
|
* @return boolean hook return
|
||||||
|
*/
|
||||||
|
function onRouterInitialized($m)
|
||||||
|
{
|
||||||
|
$m->connect('main/channel/:channelkey/keepalive',
|
||||||
|
array('action' => 'keepalivechannel'),
|
||||||
|
array('channelkey' => '[a-z0-9]{32}'));
|
||||||
|
$m->connect('main/channel/:channelkey/close',
|
||||||
|
array('action' => 'closechannel'),
|
||||||
|
array('channelkey' => '[a-z0-9]{32}'));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function onEndShowScripts($action)
|
function onEndShowScripts($action)
|
||||||
{
|
{
|
||||||
$timeline = $this->_getTimeline($action);
|
$channel = $this->_getChannel($action);
|
||||||
|
|
||||||
|
if (empty($channel)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeline = $this->_pathToChannel(array($channel->channel_key));
|
||||||
|
|
||||||
// If there's not a timeline on this page,
|
// If there's not a timeline on this page,
|
||||||
// just return true
|
// just return true
|
||||||
@ -97,12 +145,14 @@ class RealtimePlugin extends Plugin
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$pluginPath = common_path('plugins/Realtime/');
|
$pluginPath = common_path('plugins/Realtime/');
|
||||||
$realtimeUI = ' RealtimeUpdate.initActions("'.$url.'", "'.$timeline.'", "'. $pluginPath .'");';
|
$keepalive = common_local_url('keepalivechannel', array('channelkey' => $channel->channel_key));
|
||||||
|
$close = common_local_url('closechannel', array('channelkey' => $channel->channel_key));
|
||||||
|
$realtimeUI = ' RealtimeUpdate.initActions("'.$url.'", "'.$timeline.'", "'. $pluginPath .'", "'.$keepalive.'", "'.$close.'"); ';
|
||||||
}
|
}
|
||||||
|
|
||||||
$script = ' $(document).ready(function() { '.
|
$script = ' $(document).ready(function() { '.
|
||||||
$realtimeUI.
|
$realtimeUI.
|
||||||
$this->_updateInitialize($timeline, $user_id).
|
$this->_updateInitialize($timeline, $user_id).
|
||||||
'}); ';
|
'}); ';
|
||||||
$action->inlineScript($script);
|
$action->inlineScript($script);
|
||||||
|
|
||||||
@ -126,14 +176,14 @@ class RealtimePlugin extends Plugin
|
|||||||
$user = User::staticGet('id', $notice->profile_id);
|
$user = User::staticGet('id', $notice->profile_id);
|
||||||
|
|
||||||
if (!empty($user)) {
|
if (!empty($user)) {
|
||||||
$paths[] = array('showstream', $user->nickname);
|
$paths[] = array('showstream', $user->nickname, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to the public timeline
|
// Add to the public timeline
|
||||||
|
|
||||||
if ($notice->is_local == Notice::LOCAL_PUBLIC ||
|
if ($notice->is_local == Notice::LOCAL_PUBLIC ||
|
||||||
($notice->is_local == Notice::REMOTE_OMB && !common_config('public', 'localonly'))) {
|
($notice->is_local == Notice::REMOTE_OMB && !common_config('public', 'localonly'))) {
|
||||||
$paths[] = array('public');
|
$paths[] = array('public', null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to the tags timeline
|
// Add to the tags timeline
|
||||||
@ -142,7 +192,7 @@ class RealtimePlugin extends Plugin
|
|||||||
|
|
||||||
if (!empty($tags)) {
|
if (!empty($tags)) {
|
||||||
foreach ($tags as $tag) {
|
foreach ($tags as $tag) {
|
||||||
$paths[] = array('tag', $tag);
|
$paths[] = array('tag', $tag, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +203,7 @@ class RealtimePlugin extends Plugin
|
|||||||
|
|
||||||
foreach (array_keys($ni) as $user_id) {
|
foreach (array_keys($ni) as $user_id) {
|
||||||
$user = User::staticGet('id', $user_id);
|
$user = User::staticGet('id', $user_id);
|
||||||
$paths[] = array('all', $user->nickname);
|
$paths[] = array('all', $user->nickname, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to the replies timeline
|
// Add to the replies timeline
|
||||||
@ -165,7 +215,7 @@ class RealtimePlugin extends Plugin
|
|||||||
while ($reply->fetch()) {
|
while ($reply->fetch()) {
|
||||||
$user = User::staticGet('id', $reply->profile_id);
|
$user = User::staticGet('id', $reply->profile_id);
|
||||||
if (!empty($user)) {
|
if (!empty($user)) {
|
||||||
$paths[] = array('replies', $user->nickname);
|
$paths[] = array('replies', $user->nickname, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,7 +229,7 @@ class RealtimePlugin extends Plugin
|
|||||||
if ($gi->find()) {
|
if ($gi->find()) {
|
||||||
while ($gi->fetch()) {
|
while ($gi->fetch()) {
|
||||||
$ug = User_group::staticGet('id', $gi->group_id);
|
$ug = User_group::staticGet('id', $gi->group_id);
|
||||||
$paths[] = array('showgroup', $ug->nickname);
|
$paths[] = array('showgroup', $ug->nickname, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,9 +239,30 @@ class RealtimePlugin extends Plugin
|
|||||||
|
|
||||||
$this->_connect();
|
$this->_connect();
|
||||||
|
|
||||||
|
// XXX: We should probably fan-out here and do a
|
||||||
|
// new queue item for each path
|
||||||
|
|
||||||
foreach ($paths as $path) {
|
foreach ($paths as $path) {
|
||||||
$timeline = $this->_pathToChannel($path);
|
|
||||||
$this->_publish($timeline, $json);
|
list($action, $arg1, $arg2) = $path;
|
||||||
|
|
||||||
|
$channels = Realtime_channel::getAllChannels($action, $arg1, $arg2);
|
||||||
|
|
||||||
|
foreach ($channels as $channel) {
|
||||||
|
|
||||||
|
// XXX: We should probably fan-out here and do a
|
||||||
|
// new queue item for each user/path combo
|
||||||
|
|
||||||
|
if (is_null($channel->user_id)) {
|
||||||
|
$profile = null;
|
||||||
|
} else {
|
||||||
|
$profile = Profile::staticGet('id', $channel->user_id);
|
||||||
|
}
|
||||||
|
if ($notice->inScope($profile)) {
|
||||||
|
$timeline = $this->_pathToChannel(array($channel->channel_key));
|
||||||
|
$this->_publish($timeline, $json);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_disconnect();
|
$this->_disconnect();
|
||||||
@ -370,21 +441,40 @@ class RealtimePlugin extends Plugin
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function _getTimeline($action)
|
function _getTimeline($action)
|
||||||
{
|
{
|
||||||
$path = null;
|
$channel = $this->_getChannel($action);
|
||||||
|
if (empty($channel)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->_pathToChannel(array($channel->channel_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getChannel($action)
|
||||||
|
{
|
||||||
$timeline = null;
|
$timeline = null;
|
||||||
|
$arg1 = null;
|
||||||
|
$arg2 = null;
|
||||||
|
|
||||||
$action_name = $action->trimmed('action');
|
$action_name = $action->trimmed('action');
|
||||||
|
|
||||||
|
// FIXME: lists
|
||||||
|
// FIXME: search (!)
|
||||||
|
// FIXME: profile + tag
|
||||||
|
|
||||||
switch ($action_name) {
|
switch ($action_name) {
|
||||||
case 'public':
|
case 'public':
|
||||||
$path = array('public');
|
// no arguments
|
||||||
break;
|
break;
|
||||||
case 'tag':
|
case 'tag':
|
||||||
$tag = $action->trimmed('tag');
|
$tag = $action->trimmed('tag');
|
||||||
if (!empty($tag)) {
|
if (empty($tag)) {
|
||||||
$path = array('tag', $tag);
|
$arg1 = $tag;
|
||||||
|
} else {
|
||||||
|
$this->log(LOG_NOTICE, "Unexpected 'tag' action without tag argument");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'showstream':
|
case 'showstream':
|
||||||
@ -393,17 +483,31 @@ class RealtimePlugin extends Plugin
|
|||||||
case 'showgroup':
|
case 'showgroup':
|
||||||
$nickname = common_canonical_nickname($action->trimmed('nickname'));
|
$nickname = common_canonical_nickname($action->trimmed('nickname'));
|
||||||
if (!empty($nickname)) {
|
if (!empty($nickname)) {
|
||||||
$path = array($action_name, $nickname);
|
$arg1 = $nickname;
|
||||||
|
} else {
|
||||||
|
$this->log(LOG_NOTICE, "Unexpected $action_name action without nickname argument.");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($path)) {
|
$user = common_current_user();
|
||||||
$timeline = $this->_pathToChannel($path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $timeline;
|
$user_id = (!empty($user)) ? $user->id : null;
|
||||||
|
|
||||||
|
$channel = Realtime_channel::getChannel($user_id,
|
||||||
|
$action_name,
|
||||||
|
$arg1,
|
||||||
|
$arg2);
|
||||||
|
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStartReadWriteTables(&$alwaysRW, &$rwdb)
|
||||||
|
{
|
||||||
|
$alwaysRW[] = 'realtime_channel';
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
275
plugins/Realtime/Realtime_channel.php
Normal file
275
plugins/Realtime/Realtime_channel.php
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2011, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* A channel for real-time browser data
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* 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 Realtime
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2011 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A channel for real-time browser data
|
||||||
|
*
|
||||||
|
* For each user currently browsing the site, we want to know which page they're on
|
||||||
|
* so we can send real-time updates to their browser.
|
||||||
|
*
|
||||||
|
* @category Realtime
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
|
||||||
|
* @link http://status.net/
|
||||||
|
*
|
||||||
|
* @see DB_DataObject
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Realtime_channel extends Managed_DataObject
|
||||||
|
{
|
||||||
|
const TIMEOUT = 1800; // 30 minutes
|
||||||
|
|
||||||
|
public $__table = 'realtime_channel'; // table name
|
||||||
|
|
||||||
|
public $user_id; // int -> user.id, can be null
|
||||||
|
public $action; // string
|
||||||
|
public $arg1; // argument
|
||||||
|
public $arg2; // argument, usually null
|
||||||
|
public $channel_key; // 128-bit shared secret key
|
||||||
|
public $audience; // listener count
|
||||||
|
public $created; // created date
|
||||||
|
public $modified; // modified date
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instance by key
|
||||||
|
*
|
||||||
|
* This is a utility method to get a single instance with a given key value.
|
||||||
|
*
|
||||||
|
* @param string $k Key to use to lookup (usually 'user_id' for this class)
|
||||||
|
* @param mixed $v Value to lookup
|
||||||
|
*
|
||||||
|
* @return Realtime_channel object found, or null for no hits
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function staticGet($k, $v=null)
|
||||||
|
{
|
||||||
|
return Managed_DataObject::staticGet('Realtime_channel', $k, $v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instance by compound key
|
||||||
|
*
|
||||||
|
* @param array $kv array of key-value mappings
|
||||||
|
*
|
||||||
|
* @return Realtime_channel object found, or null for no hits
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function pkeyGet($kv)
|
||||||
|
{
|
||||||
|
return Managed_DataObject::pkeyGet('Realtime_channel', $kv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The One True Thingy that must be defined and declared.
|
||||||
|
*/
|
||||||
|
public static function schemaDef()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'description' => 'A channel of realtime notice data',
|
||||||
|
'fields' => array(
|
||||||
|
'user_id' => array('type' => 'int',
|
||||||
|
'not null' => false,
|
||||||
|
'description' => 'user viewing page; can be null'),
|
||||||
|
'action' => array('type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'not null' => true,
|
||||||
|
'description' => 'page being viewed'),
|
||||||
|
'arg1' => array('type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'not null' => false,
|
||||||
|
'description' => 'page argument, like username or tag'),
|
||||||
|
'arg2' => array('type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'not null' => false,
|
||||||
|
'description' => 'second page argument, like tag for showstream'),
|
||||||
|
'channel_key' => array('type' => 'varchar',
|
||||||
|
'length' => 32,
|
||||||
|
'not null' => true,
|
||||||
|
'description' => 'shared secret key for this channel'),
|
||||||
|
'audience' => array('type' => 'integer',
|
||||||
|
'not null' => true,
|
||||||
|
'default' => 0,
|
||||||
|
'description' => 'reference count'),
|
||||||
|
'created' => array('type' => 'datetime',
|
||||||
|
'not null' => true,
|
||||||
|
'description' => 'date this record was created'),
|
||||||
|
'modified' => array('type' => 'datetime',
|
||||||
|
'not null' => true,
|
||||||
|
'description' => 'date this record was modified'),
|
||||||
|
),
|
||||||
|
'primary key' => array('channel_key'),
|
||||||
|
'unique keys' => array('realtime_channel_user_page_idx' => array('user_id', 'action', 'arg1', 'arg2')),
|
||||||
|
'foreign keys' => array(
|
||||||
|
'realtime_channel_user_id_fkey' => array('user', array('user_id' => 'id')),
|
||||||
|
),
|
||||||
|
'indexes' => array(
|
||||||
|
'realtime_channel_modified_idx' => array('modified'),
|
||||||
|
'realtime_channel_page_idx' => array('action', 'arg1', 'arg2')
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function saveNew($user_id, $action, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
$channel = new Realtime_channel();
|
||||||
|
|
||||||
|
$channel->user_id = $user_id;
|
||||||
|
$channel->action = $action;
|
||||||
|
$channel->arg1 = $arg1;
|
||||||
|
$channel->arg2 = $arg2;
|
||||||
|
$channel->audience = 1;
|
||||||
|
|
||||||
|
$channel->channel_key = common_good_rand(16); // 128-bit key, 32 hex chars
|
||||||
|
|
||||||
|
$channel->created = common_sql_now();
|
||||||
|
$channel->modified = $channel->created;
|
||||||
|
|
||||||
|
$channel->insert();
|
||||||
|
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getChannel($user_id, $action, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
$channel = self::fetchChannel($user_id, $action, $arg1, $arg2);
|
||||||
|
|
||||||
|
// Ignore (and delete!) old channels
|
||||||
|
|
||||||
|
if (!empty($channel)) {
|
||||||
|
$modTime = strtotime($channel->modified);
|
||||||
|
if ((time() - $modTime) > self::TIMEOUT) {
|
||||||
|
$channel->delete();
|
||||||
|
$channel = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($channel)) {
|
||||||
|
$channel = self::saveNew($user_id, $action, $arg1, $arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getAllChannels($action, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
$channel = new Realtime_channel();
|
||||||
|
|
||||||
|
$channel->action = $action;
|
||||||
|
|
||||||
|
if (is_null($arg1)) {
|
||||||
|
$channel->whereAdd('arg1 is null');
|
||||||
|
} else {
|
||||||
|
$channel->arg1 = $arg1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($arg2)) {
|
||||||
|
$channel->whereAdd('arg2 is null');
|
||||||
|
} else {
|
||||||
|
$channel->arg2 = $arg2;
|
||||||
|
}
|
||||||
|
|
||||||
|
$channel->whereAdd('modified > "' . common_sql_date(time() - self::TIMEOUT) . '"');
|
||||||
|
|
||||||
|
$channels = array();
|
||||||
|
|
||||||
|
if ($channel->find()) {
|
||||||
|
$channels = $channel->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function fetchChannel($user_id, $action, $arg1, $arg2)
|
||||||
|
{
|
||||||
|
$channel = new Realtime_channel();
|
||||||
|
|
||||||
|
if (is_null($user_id)) {
|
||||||
|
$channel->whereAdd('user_id is null');
|
||||||
|
} else {
|
||||||
|
$channel->user_id = $user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$channel->action = $action;
|
||||||
|
|
||||||
|
if (is_null($arg1)) {
|
||||||
|
$channel->whereAdd('arg1 is null');
|
||||||
|
} else {
|
||||||
|
$channel->arg1 = $arg1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($arg2)) {
|
||||||
|
$channel->whereAdd('arg2 is null');
|
||||||
|
} else {
|
||||||
|
$channel->arg2 = $arg2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($channel->find(true)) {
|
||||||
|
$channel->increment();
|
||||||
|
return $channel;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function increment()
|
||||||
|
{
|
||||||
|
// XXX: race
|
||||||
|
$orig = clone($this);
|
||||||
|
$this->audience++;
|
||||||
|
$this->modified = common_sql_now();
|
||||||
|
$this->update($orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
function touch()
|
||||||
|
{
|
||||||
|
// XXX: race
|
||||||
|
$orig = clone($this);
|
||||||
|
$this->modified = common_sql_now();
|
||||||
|
$this->update($orig);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrement()
|
||||||
|
{
|
||||||
|
// XXX: race
|
||||||
|
if ($this->audience == 1) {
|
||||||
|
$this->delete();
|
||||||
|
} else {
|
||||||
|
$orig = clone($this);
|
||||||
|
$this->audience--;
|
||||||
|
$this->modified = common_sql_now();
|
||||||
|
$this->update($orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
plugins/Realtime/cleanupchannels.php
Normal file
71
plugins/Realtime/cleanupchannels.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* StatusNet - a distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2011, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* Script to print out current version of the software
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
|
||||||
|
|
||||||
|
$shortoptions = 'u';
|
||||||
|
$longoptions = array('universe');
|
||||||
|
|
||||||
|
$helptext = <<<END_OF_CLEANUPCHANNELS_HELP
|
||||||
|
cleanupchannels.php [options]
|
||||||
|
Garbage-collects old realtime channels
|
||||||
|
|
||||||
|
-u --universe Do all sites
|
||||||
|
|
||||||
|
END_OF_CLEANUPCHANNELS_HELP;
|
||||||
|
|
||||||
|
require_once INSTALLDIR.'/scripts/commandline.inc';
|
||||||
|
|
||||||
|
function cleanupChannels()
|
||||||
|
{
|
||||||
|
$rc = new Realtime_channel();
|
||||||
|
|
||||||
|
$rc->selectAdd();
|
||||||
|
$rc->selectAdd('channel_key');
|
||||||
|
|
||||||
|
$rc->whereAdd('modified < "' . common_sql_date(time() - Realtime_channel::TIMEOUT) . '"');
|
||||||
|
|
||||||
|
if ($rc->find()) {
|
||||||
|
$keys = $rc->fetchAll();
|
||||||
|
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$rc = Realtime_channel::staticGet('channel_key', $key);
|
||||||
|
if (!empty($rc)) {
|
||||||
|
printfv("Deleting realtime channel '$key'\n");
|
||||||
|
$rc->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_option('u', 'universe')) {
|
||||||
|
$sn = new Status_network();
|
||||||
|
if ($sn->find()) {
|
||||||
|
while ($sn->fetch()) {
|
||||||
|
$server = $sn->getServerName();
|
||||||
|
StatusNet::init($server);
|
||||||
|
cleanupChannels();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cleanupChannels();
|
||||||
|
}
|
115
plugins/Realtime/closechannel.php
Normal file
115
plugins/Realtime/closechannel.php
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2011, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* action to close a channel
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* 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 Realtime
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2011 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
// This check helps protect against security problems;
|
||||||
|
// your code file can't be executed directly from the web.
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to close a channel
|
||||||
|
*
|
||||||
|
* @category Realtime
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2011 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ClosechannelAction extends Action
|
||||||
|
{
|
||||||
|
protected $channelKey = null;
|
||||||
|
protected $channel = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For initializing members of the class.
|
||||||
|
*
|
||||||
|
* @param array $argarray misc. arguments
|
||||||
|
*
|
||||||
|
* @return boolean true
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($argarray)
|
||||||
|
{
|
||||||
|
parent::prepare($argarray);
|
||||||
|
|
||||||
|
if (!$this->isPost()) {
|
||||||
|
throw new ClientException(_m('You have to POST it.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->channelKey = $this->trimmed('channelkey');
|
||||||
|
|
||||||
|
if (empty($this->channelKey)) {
|
||||||
|
throw new ClientException(_m('No channel key argument.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->channel = Realtime_channel::staticGet('channel_key', $this->channelKey);
|
||||||
|
|
||||||
|
if (empty($this->channel)) {
|
||||||
|
throw new ClientException(_m('No such channel.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler method
|
||||||
|
*
|
||||||
|
* @param array $argarray is ignored since it's now passed in in prepare()
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($argarray=null)
|
||||||
|
{
|
||||||
|
$this->channel->decrement();
|
||||||
|
|
||||||
|
header('HTTP/1.1 204 No Content');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if read only.
|
||||||
|
*
|
||||||
|
* MAY override
|
||||||
|
*
|
||||||
|
* @param array $args other arguments
|
||||||
|
*
|
||||||
|
* @return boolean is read only action?
|
||||||
|
*/
|
||||||
|
|
||||||
|
function isReadOnly($args)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
115
plugins/Realtime/keepalivechannel.php
Normal file
115
plugins/Realtime/keepalivechannel.php
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2011, StatusNet, Inc.
|
||||||
|
*
|
||||||
|
* action periodically pinged by a page to keep a channel alive
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* 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 Realtime
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2011 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
// This check helps protect against security problems;
|
||||||
|
// your code file can't be executed directly from the web.
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action periodically pinged by a page to keep a channel alive
|
||||||
|
*
|
||||||
|
* @category Realtime
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Evan Prodromou <evan@status.net>
|
||||||
|
* @copyright 2011 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class KeepalivechannelAction extends Action
|
||||||
|
{
|
||||||
|
protected $channelKey = null;
|
||||||
|
protected $channel = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For initializing members of the class.
|
||||||
|
*
|
||||||
|
* @param array $argarray misc. arguments
|
||||||
|
*
|
||||||
|
* @return boolean true
|
||||||
|
*/
|
||||||
|
|
||||||
|
function prepare($argarray)
|
||||||
|
{
|
||||||
|
parent::prepare($argarray);
|
||||||
|
|
||||||
|
if (!$this->isPost()) {
|
||||||
|
throw new ClientException(_m('You have to POST it.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->channelKey = $this->trimmed('channelkey');
|
||||||
|
|
||||||
|
if (empty($this->channelKey)) {
|
||||||
|
throw new ClientException(_m('No channel key argument.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->channel = Realtime_channel::staticGet('channel_key', $this->channelKey);
|
||||||
|
|
||||||
|
if (empty($this->channel)) {
|
||||||
|
throw new ClientException(_m('No such channel.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler method
|
||||||
|
*
|
||||||
|
* @param array $argarray is ignored since it's now passed in in prepare()
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
function handle($argarray=null)
|
||||||
|
{
|
||||||
|
$this->channel->touch();
|
||||||
|
|
||||||
|
header('HTTP/1.1 204 No Content');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if read only.
|
||||||
|
*
|
||||||
|
* MAY override
|
||||||
|
*
|
||||||
|
* @param array $args other arguments
|
||||||
|
*
|
||||||
|
* @return boolean is read only action?
|
||||||
|
*/
|
||||||
|
|
||||||
|
function isReadOnly($args)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* StatusNet - a distributed open-source microblogging tool
|
* StatusNet - a distributed open-source microblogging tool
|
||||||
* Copyright (C) 2008, StatusNet, Inc.
|
* Copyright (C) 2009-2011, StatusNet, Inc.
|
||||||
*
|
*
|
||||||
* Add a notice encoded as JSON into the current timeline
|
* Add a notice encoded as JSON into the current timeline
|
||||||
*
|
*
|
||||||
@ -21,7 +21,7 @@
|
|||||||
* @package StatusNet
|
* @package StatusNet
|
||||||
* @author Evan Prodromou <evan@status.net>
|
* @author Evan Prodromou <evan@status.net>
|
||||||
* @author Sarven Capadisli <csarven@status.net>
|
* @author Sarven Capadisli <csarven@status.net>
|
||||||
* @copyright 2009 StatusNet, Inc.
|
* @copyright 2009-2011 StatusNet, Inc.
|
||||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
* @link http://status.net/
|
* @link http://status.net/
|
||||||
*/
|
*/
|
||||||
@ -45,6 +45,8 @@
|
|||||||
RealtimeUpdate = {
|
RealtimeUpdate = {
|
||||||
_userid: 0,
|
_userid: 0,
|
||||||
_showurl: '',
|
_showurl: '',
|
||||||
|
_keepaliveurl: '',
|
||||||
|
_closeurl: '',
|
||||||
_updatecounter: 0,
|
_updatecounter: 0,
|
||||||
_maxnotices: 50,
|
_maxnotices: 50,
|
||||||
_windowhasfocus: true,
|
_windowhasfocus: true,
|
||||||
@ -390,11 +392,28 @@ RealtimeUpdate = {
|
|||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
initActions: function(url, timeline, path)
|
initActions: function(url, timeline, path, keepaliveurl, closeurl)
|
||||||
{
|
{
|
||||||
$('#notices_primary').prepend('<ul id="realtime_actions"><li id="realtime_playpause"></li><li id="realtime_timeline"></li></ul>');
|
$('#notices_primary').prepend('<ul id="realtime_actions"><li id="realtime_playpause"></li><li id="realtime_timeline"></li></ul>');
|
||||||
|
|
||||||
RealtimeUpdate._pluginPath = path;
|
RealtimeUpdate._pluginPath = path;
|
||||||
|
RealtimeUpdate._keepaliveurl = keepaliveurl;
|
||||||
|
RealtimeUpdate._closeurl = closeurl;
|
||||||
|
|
||||||
|
|
||||||
|
// On unload, let the server know we're no longer listening
|
||||||
|
$(window).unload(function() {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: RealtimeUpdate._closeurl});
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(function() {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: RealtimeUpdate._keepaliveurl});
|
||||||
|
|
||||||
|
}, 15 * 60 * 1000 ); // every 15 min; timeout in 30 min
|
||||||
|
|
||||||
RealtimeUpdate.initPlayPause();
|
RealtimeUpdate.initPlayPause();
|
||||||
RealtimeUpdate.initAddPopup(url, timeline, RealtimeUpdate._pluginPath);
|
RealtimeUpdate.initAddPopup(url, timeline, RealtimeUpdate._pluginPath);
|
||||||
|
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