[REALTIME] Reviewed both the superclass and its dist plugins

This commit is contained in:
Diogo Cordeiro 2019-11-03 15:37:49 +00:00 committed by Diogo Peralta Cordeiro
parent aab3584f93
commit 3b01aa31d3
80 changed files with 349 additions and 315 deletions

View File

@ -1,35 +1,31 @@
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
/**
* StatusNet, the distributed open-source microblogging tool
*
* Superclass for plugins that do "real time" updates of timelines using Ajax
*
* 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 Plugin
* @package StatusNet
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2009 StatusNet, Inc.
* @copyright 2014 Free Software Foundation, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @copyright 2009-2019 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
if (!defined('GNUSOCIAL')) { exit(1); }
defined('GNUSOCIAL') || die();
/**
* Superclass for plugin to do realtime updates
@ -37,13 +33,12 @@ if (!defined('GNUSOCIAL')) { exit(1); }
* Based on experience with the Comet and Meteor plugins,
* this superclass extracts out some of the common functionality
*
* Currently depends on Favorite plugin.
* Currently depends on the Favorite module.
*
* @category Plugin
* @package StatusNet
* @package GNUsocial
* @author Evan Prodromou <evan@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/
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class RealtimePlugin extends Plugin
{
@ -53,15 +48,17 @@ class RealtimePlugin extends Plugin
* When it's time to initialize the plugin, calculate and
* pass the URLs we need.
*/
function onInitializePlugin()
public function onInitializePlugin()
{
// FIXME: need to find a better way to pass this pattern in
$this->showurl = common_local_url('shownotice',
array('notice' => '0000000000'));
$this->showurl = common_local_url(
'shownotice',
['notice' => '0000000000']
);
return true;
}
function onCheckSchema()
public function onCheckSchema()
{
$schema = Schema::get();
$schema->ensureTable('realtime_channel', Realtime_channel::schemaDef());
@ -72,20 +69,25 @@ class RealtimePlugin extends Plugin
* Hook for RouterInitialized event.
*
* @param URLMapper $m path-to-action mapper
* @return boolean hook return
* @return bool hook return
* @throws Exception
*/
public function onRouterInitialized(URLMapper $m)
{
$m->connect('main/channel/:channelkey/keepalive',
$m->connect(
'main/channel/:channelkey/keepalive',
['action' => 'keepalivechannel'],
['channelkey' => '[a-z0-9]{32}']);
$m->connect('main/channel/:channelkey/close',
['channelkey' => '[a-z0-9]{32}']
);
$m->connect(
'main/channel/:channelkey/close',
['action' => 'closechannel'],
['channelkey' => '[a-z0-9]{32}']);
['channelkey' => '[a-z0-9]{32}']
);
return true;
}
function onEndShowScripts($action)
public function onEndShowScripts(Action $action)
{
$channel = $this->_getChannel($action);
@ -93,7 +95,7 @@ class RealtimePlugin extends Plugin
return true;
}
$timeline = $this->_pathToChannel(array($channel->channel_key));
$timeline = $this->_pathToChannel([$channel->channel_key]);
// If there's not a timeline on this page,
// just return true
@ -125,11 +127,10 @@ class RealtimePlugin extends Plugin
if ($action->boolean('realtime')) {
$realtimeUI = ' RealtimeUpdate.initPopupWindow();';
}
else {
} else {
$pluginPath = common_path('plugins/Realtime/');
$keepalive = common_local_url('keepalivechannel', array('channelkey' => $channel->channel_key));
$close = common_local_url('closechannel', array('channelkey' => $channel->channel_key));
$keepalive = common_local_url('keepalivechannel', ['channelkey' => $channel->channel_key]);
$close = common_local_url('closechannel', ['channelkey' => $channel->channel_key]);
$realtimeUI = ' RealtimeUpdate.initActions('.json_encode($url).', '.json_encode($timeline).', '.json_encode($pluginPath).', '.json_encode($keepalive).', '.json_encode($close).'); ';
}
@ -144,15 +145,17 @@ class RealtimePlugin extends Plugin
public function onEndShowStylesheets(Action $action)
{
$urlpath = self::staticPath(str_replace('Plugin','',__CLASS__),
'css/realtimeupdate.css');
$urlpath = self::staticPath(
str_replace('Plugin', '', __CLASS__),
'css/realtimeupdate.css'
);
$action->cssLink($urlpath, null, 'screen, projection, tv');
return true;
}
public function onHandleQueuedNotice(Notice $notice)
{
$paths = array();
$paths = [];
// Add to the author's timeline
@ -165,7 +168,7 @@ class RealtimePlugin extends Plugin
try {
$user = $profile->getUser();
$paths[] = array('showstream', $user->nickname, null);
$paths[] = ['showstream', $user->nickname, null];
} catch (NoSuchUserException $e) {
// We really should handle the remote profile views too
$user = null;
@ -176,7 +179,7 @@ class RealtimePlugin extends Plugin
$is_local = intval($notice->is_local);
if ($is_local === Notice::LOCAL_PUBLIC ||
($is_local === Notice::REMOTE && !common_config('public', 'localonly'))) {
$paths[] = array('public', null, null);
$paths[] = ['public', null, null];
}
// Add to the tags timeline
@ -185,7 +188,7 @@ class RealtimePlugin extends Plugin
if (!empty($tags)) {
foreach ($tags as $tag) {
$paths[] = array('tag', $tag, null);
$paths[] = ['tag', $tag, null];
}
}
@ -196,7 +199,7 @@ class RealtimePlugin extends Plugin
foreach (array_keys($ni) as $user_id) {
$user = User::getKV('id', $user_id);
$paths[] = array('all', $user->nickname, null);
$paths[] = ['all', $user->getNickname(), null];
}
// Add to the replies timeline
@ -208,7 +211,7 @@ class RealtimePlugin extends Plugin
while ($reply->fetch()) {
$user = User::getKV('id', $reply->profile_id);
if (!empty($user)) {
$paths[] = array('replies', $user->nickname, null);
$paths[] = ['replies', $user->getNickname(), null];
}
}
}
@ -222,12 +225,11 @@ class RealtimePlugin extends Plugin
if ($gi->find()) {
while ($gi->fetch()) {
$ug = User_group::getKV('id', $gi->group_id);
$paths[] = array('showgroup', $ug->nickname, null);
$paths[] = ['showgroup', $ug->getNickname(), null];
}
}
if (count($paths) > 0) {
$json = $this->noticeAsJson($notice);
$this->_connect();
@ -236,13 +238,14 @@ class RealtimePlugin extends Plugin
// new queue item for each path
foreach ($paths as $path) {
list($action, $arg1, $arg2) = $path;
$channels = Realtime_channel::getAllChannels($action, $arg1, $arg2);
$this->log(LOG_INFO, sprintf(_("%d candidate channels for notice %d"),
$this->log(LOG_INFO, sprintf(
_("%d candidate channels for notice %d"),
count($channels),
$notice->id));
$notice->id
));
foreach ($channels as $channel) {
@ -255,14 +258,18 @@ class RealtimePlugin extends Plugin
$profile = Profile::getKV('id', $channel->user_id);
}
if ($notice->inScope($profile)) {
$this->log(LOG_INFO,
sprintf(_("Delivering notice %d to channel (%s, %s, %s) for user '%s'"),
$this->log(
LOG_INFO,
sprintf(
_m("Delivering notice %d to channel (%s, %s, %s) for user '%s'"),
$notice->id,
$channel->action,
$channel->arg1,
$channel->arg2,
($profile) ? ($profile->nickname) : "<public>"));
$timeline = $this->_pathToChannel(array($channel->channel_key));
($profile ? $profile->getNickname() : '<public>')
)
);
$timeline = $this->_pathToChannel([$channel->channel_key]);
$this->_publish($timeline, $json);
}
}
@ -274,18 +281,23 @@ class RealtimePlugin extends Plugin
return true;
}
function onStartShowBody($action)
public function onStartShowBody(Action $action)
{
$realtime = $action->boolean('realtime');
if (!$realtime) {
return true;
}
$action->elementStart('body',
(common_current_user()) ? array('id' => $action->trimmed('action'),
'class' => 'user_in realtime-popup')
: array('id' => $action->trimmed('action'),
'class'=> 'realtime-popup'));
$action->elementStart(
'body',
(common_current_user() ? [
'id' => $action->trimmed('action'),
'class' => 'user_in realtime-popup',
] : [
'id' => $action->trimmed('action'),
'class'=> 'realtime-popup',
])
);
// XXX hack to deal with JS that tries to get the
// root url from page output
@ -294,14 +306,17 @@ class RealtimePlugin extends Plugin
if (common_config('singleuser', 'enabled')) {
$user = User::singleUser();
$url = common_local_url('showstream', array('nickname' => $user->nickname));
$url = common_local_url('showstream', ['nickname' => $user->nickname]);
} else {
$url = common_local_url('public');
}
$action->element('a', array('class' => 'url',
'href' => $url),
'');
$action->element(
'a',
['class' => 'url',
'href' => $url],
''
);
$action->elementEnd('address');
@ -311,7 +326,7 @@ class RealtimePlugin extends Plugin
return false; // No default processing
}
function noticeAsJson(Notice $notice)
public function noticeAsJson(Notice $notice)
{
// FIXME: this code should be abstracted to a neutral third
// party, like Notice::asJson(). I'm not sure of the ethics
@ -347,7 +362,7 @@ class RealtimePlugin extends Plugin
return $arr;
}
function getNoticeTags(Notice $notice)
public function getNoticeTags(Notice $notice)
{
$tags = null;
@ -355,7 +370,7 @@ class RealtimePlugin extends Plugin
$nt->notice_id = $notice->id;
if ($nt->find()) {
$tags = array();
$tags = [];
while ($nt->fetch()) {
$tags[] = $nt->tag;
}
@ -367,11 +382,13 @@ class RealtimePlugin extends Plugin
return $tags;
}
function _getScripts()
public function _getScripts(): array
{
$urlpath = self::staticPath(str_replace('Plugin','',__CLASS__),
'js/realtimeupdate.js');
return array($urlpath);
$urlpath = self::staticPath(
str_replace('Plugin', '', __CLASS__),
'js/realtimeupdate.js'
);
return [$urlpath];
}
/**
@ -380,9 +397,10 @@ class RealtimePlugin extends Plugin
* @param Action $action
* @param array $messages
*
* @return boolean hook return value
* @return bool hook return value
* @throws Exception
*/
function onEndScriptMessages($action, &$messages)
public function onEndScriptMessages(Action $action, array &$messages)
{
// TRANS: Text label for realtime view "play" button, usually replaced by an icon.
$messages['realtime_play'] = _m('BUTTON', 'Play');
@ -400,40 +418,40 @@ class RealtimePlugin extends Plugin
return true;
}
function _updateInitialize($timeline, $user_id)
public function _updateInitialize($timeline, int $user_id)
{
return "RealtimeUpdate.init($user_id, \"$this->showurl\"); ";
}
function _connect()
public function _connect()
{
}
function _publish($timeline, $json)
public function _publish($timeline, $json)
{
}
function _disconnect()
public function _disconnect()
{
}
function _pathToChannel($path)
public function _pathToChannel(array $path): string
{
return '';
}
function _getTimeline($action)
public function _getTimeline(Action $action)
{
$channel = $this->_getChannel($action);
if (empty($channel)) {
return null;
}
return $this->_pathToChannel(array($channel->channel_key));
return $this->_pathToChannel([$channel->channel_key]);
}
function _getChannel($action)
public function _getChannel(Action $action)
{
$timeline = null;
$arg1 = null;
@ -478,15 +496,17 @@ class RealtimePlugin extends Plugin
$user_id = (!empty($user)) ? $user->id : null;
$channel = Realtime_channel::getChannel($user_id,
$channel = Realtime_channel::getChannel(
$user_id,
$action_name,
$arg1,
$arg2);
$arg2
);
return $channel;
}
function onStartReadWriteTables(&$alwaysRW, &$rwdb)
public function onStartReadWriteTables(&$alwaysRW, &$rwdb)
{
$alwaysRW[] = 'realtime_channel';
return true;

View File

@ -1,48 +1,38 @@
<?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);
}
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
/**
* Action to close a channel
*
* @category Realtime
* @package StatusNet
* @package GNUsocial
* @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/
* @copyright 2011-2019 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
defined('GNUSOCIAL') || die();
/**
* Action to close a channel
*
* @category Realtime
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class ClosechannelAction extends Action
{
@ -57,7 +47,7 @@ class ClosechannelAction extends Action
* @return boolean true
* @throws ClientException
*/
function prepare(array $args = [])
public function prepare(array $args = [])
{
parent::prepare($args);
@ -88,7 +78,7 @@ class ClosechannelAction extends Action
*
* @return void
*/
function handle()
public function handle(): void
{
$this->channel->decrement();
@ -104,9 +94,9 @@ class ClosechannelAction extends Action
*
* @param array $args other arguments
*
* @return boolean is read only action?
* @return bool is read only action?
*/
function isReadOnly($args)
public function isReadOnly($args): bool
{
return false;
}

View File

@ -54,10 +54,10 @@ class KeepalivechannelAction extends Action
*
* @param array $args misc. arguments
*
* @return boolean true
* @return bool true
* @throws ClientException
*/
function prepare(array $args = [])
public function prepare(array $args = []): bool
{
parent::prepare($args);
@ -88,7 +88,7 @@ class KeepalivechannelAction extends Action
*
* @return void
*/
function handle()
public function handle(): void
{
$this->channel->touch();
@ -104,9 +104,9 @@ class KeepalivechannelAction extends Action
*
* @param array $args other arguments
*
* @return boolean is read only action?
* @return bool is read only action?
*/
function isReadOnly($args)
public function isReadOnly($args): bool
{
return false;
}

View File

@ -23,7 +23,7 @@
* @category Realtime
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @copyright 2011-2019 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
@ -32,7 +32,6 @@ defined('GNUSOCIAL') || die();
/**
* A channel for real-time browser data
*
* @copyright 2011 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*
* @see DB_DataObject
@ -57,52 +56,52 @@ class Realtime_channel extends Managed_DataObject
*/
public static function schemaDef()
{
return array(
return [
'description' => 'A channel of realtime notice data',
'fields' => array(
'user_id' => array('type' => 'int',
'fields' => [
'user_id' => ['type' => 'int',
'not null' => false,
'description' => 'user viewing page; can be null'),
'action' => array('type' => 'varchar',
'description' => 'user viewing page; can be null'],
'action' => ['type' => 'varchar',
'length' => 191,
'not null' => true,
'description' => 'page being viewed'),
'arg1' => array('type' => 'varchar',
'description' => 'page being viewed'],
'arg1' => ['type' => 'varchar',
'length' => 191,
'not null' => false,
'description' => 'page argument, like username or tag'),
'arg2' => array('type' => 'varchar',
'description' => 'page argument, like username or tag'],
'arg2' => ['type' => 'varchar',
'length' => 191,
'not null' => false,
'description' => 'second page argument, like tag for showstream'),
'channel_key' => array('type' => 'varchar',
'description' => 'second page argument, like tag for showstream'],
'channel_key' => ['type' => 'varchar',
'length' => 32,
'not null' => true,
'description' => 'shared secret key for this channel'),
'audience' => array('type' => 'int',
'description' => 'shared secret key for this channel'],
'audience' => ['type' => 'int',
'not null' => true,
'default' => 0,
'description' => 'reference count'),
'created' => array('type' => 'datetime',
'description' => 'reference count'],
'created' => ['type' => 'datetime',
'not null' => true,
'description' => 'date this record was created'),
'modified' => array('type' => 'datetime',
'description' => 'date this record was created'],
'modified' => ['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')
),
);
'description' => 'date this record was modified'],
],
'primary key' => ['channel_key'],
'unique keys' => ['realtime_channel_user_page_idx' => ['user_id', 'action', 'arg1', 'arg2']],
'foreign keys' => [
'realtime_channel_user_id_fkey' => ['user', ['user_id' => 'id']],
],
'indexes' => [
'realtime_channel_modified_idx' => ['modified'],
'realtime_channel_page_idx' => ['action', 'arg1', 'arg2']
],
];
}
public static function saveNew($user_id, $action, $arg1, $arg2)
public static function saveNew(int $user_id, Action $action, $arg1, $arg2): Realtime_channel
{
$channel = new Realtime_channel();
@ -122,7 +121,7 @@ class Realtime_channel extends Managed_DataObject
return $channel;
}
public static function getChannel($user_id, $action, $arg1, $arg2)
public static function getChannel(int $user_id, Action $action, $arg1, $arg2): Realtime_channel
{
$channel = self::fetchChannel($user_id, $action, $arg1, $arg2);
@ -143,7 +142,7 @@ class Realtime_channel extends Managed_DataObject
return $channel;
}
public static function getAllChannels($action, $arg1, $arg2)
public static function getAllChannels(Action $action, $arg1, $arg2): array
{
$channel = new Realtime_channel();
@ -172,7 +171,7 @@ class Realtime_channel extends Managed_DataObject
return $channels;
}
public static function fetchChannel($user_id, $action, $arg1, $arg2)
public static function fetchChannel(int $user_id, Action $action, $arg1, $arg2): ?Realtime_channel
{
$channel = new Realtime_channel();
@ -204,7 +203,7 @@ class Realtime_channel extends Managed_DataObject
}
}
public function increment()
public function increment(): void
{
// XXX: race
$orig = clone($this);
@ -213,7 +212,7 @@ class Realtime_channel extends Managed_DataObject
$this->update($orig);
}
public function touch()
public function touch(): void
{
// XXX: race
$orig = clone($this);
@ -221,7 +220,7 @@ class Realtime_channel extends Managed_DataObject
$this->update($orig);
}
public function decrement()
public function decrement(): void
{
// XXX: race
if ($this->audience == 1) {

View File

@ -20,7 +20,7 @@
*
* @package Realtime
* @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2011 StatusNet, Inc.
* @copyright 2011-2019 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
@ -28,7 +28,7 @@ define('INSTALLDIR', dirname(__DIR__, 3));
define('PUBLICDIR', INSTALLDIR . DIRECTORY_SEPARATOR . 'public');
$shortoptions = 'u';
$longoptions = array('universe');
$longoptions = ['universe'];
$helptext = <<<END_OF_CLEANUPCHANNELS_HELP
cleanupchannels.php [options]

View File

@ -26,6 +26,17 @@
defined('GNUSOCIAL') || die();
/**
* Class TheFreeNetworkModule
* This module ensures that multiple protocols serving the same purpose won't result in duplicated data.
* This class is not to be extended but a developer implementing a new protocol should be aware of it and notify the
* StartTFNCensus event.
*
* @category Module
* @package GNUsocial
* @author Bruno Casteleiro <brunoccast@fc.up.pt>
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class TheFreeNetworkModule extends Module
{
const MODULE_VERSION = '0.1.0alpha0';

View File

@ -46,6 +46,8 @@ const ACTIVITYPUB_HTTP_CLIENT_HEADERS = [
];
/**
* Adds ActivityPub support to GNU social when enabled
*
* @category Plugin
* @package GNUsocial
* @author Diogo Cordeiro <diogo@fc.up.pt>

View File

@ -31,7 +31,7 @@ if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR.'/plugins/Realtime/RealtimePlugin.php';
require_once INSTALLDIR . DIRECTORY_SEPARATOR . 'lib/modules/Realtime/RealtimePlugin.php';
/**
* Plugin to do realtime updates using Comet
@ -52,8 +52,12 @@ class CometPlugin extends RealtimePlugin
public $prefix = null;
protected $bay = null;
function __construct($server=null, $username=null, $password=null, $prefix=null)
{
public function __construct(
?string $server = null,
?string $username = null,
?string $password = null,
?string $prefix = null
) {
$this->server = $server;
$this->username = $username;
$this->password = $password;
@ -62,11 +66,11 @@ class CometPlugin extends RealtimePlugin
parent::__construct();
}
function _getScripts()
public function _getScripts(): array
{
$scripts = parent::_getScripts();
$ours = array('js/jquery.comet.js', 'js/cometupdate.js');
$ours = ['js/jquery.comet.js', 'js/cometupdate.js'];
foreach ($ours as $script) {
$scripts[] = $this->path($script);
@ -75,30 +79,30 @@ class CometPlugin extends RealtimePlugin
return $scripts;
}
function _updateInitialize($timeline, $user_id)
public function _updateInitialize($timeline, int $user_id)
{
$script = parent::_updateInitialize($timeline, $user_id);
return $script." CometUpdate.init(\"$this->server\", \"$timeline\", $user_id, \"$this->replyurl\", \"$this->favorurl\", \"$this->deleteurl\");";
}
function _connect()
public function _connect(): void
{
require_once INSTALLDIR.'/plugins/Comet/extlib/Bayeux/Bayeux.class.php';
require_once __DIR__. DIRECTORY_SEPARATOR . 'extlib/Bayeux/Bayeux.class.php';
// Bayeux? Comet? Huh? These terms confuse me
$this->bay = new Bayeux($this->server, $this->user, $this->password);
}
function _publish($timeline, $json)
public function _publish($timeline, $json): void
{
$this->bay->publish($timeline, $json);
}
function _disconnect()
public function _disconnect(): void
{
unset($this->bay);
}
function _pathToChannel($path)
public function _pathToChannel(array $path): string
{
if (!empty($this->prefix)) {
array_unshift($path, $this->prefix);
@ -108,14 +112,16 @@ class CometPlugin extends RealtimePlugin
public function onPluginVersion(array &$versions): bool
{
$versions[] = array('name' => 'Comet',
$versions[] = [
'name' => 'Comet',
'version' => self::PLUGIN_VERSION,
'author' => 'Evan Prodromou',
'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Comet',
'rawdescription' =>
// TRANS: Plugin description message. Bayeux is a protocol for transporting asynchronous messages
// TRANS: and Comet is a web application model.
_m('Plugin to make updates using Comet and Bayeux.'));
_m('Plugin to make updates using Comet and Bayeux.')
];
return true;
}
}

View File

@ -30,36 +30,36 @@ class Bayeux
public $sUrl = '';
function __construct($sUrl, $sUser='', $sPassword='')
public function __construct($sUrl, $sUser='', $sPassword='')
{
$this->sUrl = $sUrl;
$this->oCurl = curl_init();
$aHeaders = array();
$aHeaders = [];
$aHeaders[] = 'Connection: Keep-Alive';
curl_setopt($this->oCurl, CURLOPT_URL, $sUrl);
curl_setopt($this->oCurl, CURLOPT_HTTPHEADER, $aHeaders);
curl_setopt($this->oCurl, CURLOPT_HEADER, 0);
curl_setopt($this->oCurl, CURLOPT_POST, 1);
curl_setopt($this->oCurl, CURLOPT_RETURNTRANSFER,1);
curl_setopt($this->oCurl, CURLOPT_RETURNTRANSFER, 1);
if (!is_null($sUser) && mb_strlen($sUser) > 0) {
curl_setopt($this->oCurl, CURLOPT_USERPWD,"$sUser:$sPassword");
curl_setopt($this->oCurl, CURLOPT_USERPWD, "$sUser:$sPassword");
}
$this->handShake();
}
function __destruct()
public function __destruct()
{
$this->disconnect();
}
function handShake()
public function handShake()
{
$msgHandshake = array();
$msgHandshake = [];
$msgHandshake['channel'] = '/meta/handshake';
$msgHandshake['version'] = "1.0";
$msgHandshake['minimumVersion'] = "0.9";
@ -70,8 +70,9 @@ class Bayeux
$data = curl_exec($this->oCurl);
if(curl_errno($this->oCurl))
if (curl_errno($this->oCurl)) {
die("Error: " . curl_error($this->oCurl));
}
$oReturn = json_decode($data);
@ -81,8 +82,7 @@ class Bayeux
$bSuccessful = ($oReturn->successful) ? true : false;
if($bSuccessful)
{
if ($bSuccessful) {
$this->clientId = $oReturn->clientId;
$this->connect();
@ -101,9 +101,9 @@ class Bayeux
$data = curl_exec($this->oCurl);
}
function disconnect()
public function disconnect()
{
$msgHandshake = array();
$msgHandshake = [];
$msgHandshake['channel'] = '/meta/disconnect';
$msgHandshake['id'] = $this->nNextId++;
$msgHandshake['clientId'] = $this->clientId;
@ -115,10 +115,11 @@ class Bayeux
public function publish($sChannel, $oData)
{
if(!$sChannel || !$oData)
if (!$sChannel || !$oData) {
return;
}
$aMsg = array();
$aMsg = [];
$aMsg['channel'] = $sChannel;
$aMsg['id'] = $this->nNextId++;

View File

@ -1,46 +1,40 @@
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
/**
* StatusNet, the distributed open-source microblogging tool
*
* Plugin to do "real time" updates using Meteor
*
* 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 Plugin
* @package StatusNet
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @copyright 2009 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/
* @copyright 2010-2019 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
exit(1);
}
defined('GNUSOCIAL') || die();
require_once INSTALLDIR.'/plugins/Realtime/RealtimePlugin.php';
require_once INSTALLDIR . DIRECTORY_SEPARATOR . 'lib/modules/Realtime/RealtimePlugin.php';
/**
* Plugin to do realtime updates using Meteor
*
* @category Plugin
* @package StatusNet
* @package GNUsocial
* @author Evan Prodromou <evan@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/
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class MeteorPlugin extends RealtimePlugin
{
@ -55,8 +49,14 @@ class MeteorPlugin extends RealtimePlugin
public $persistent = true;
protected $_socket = null;
function __construct($webserver=null, $webport=4670, $controlport=4671, $controlserver=null, $channelbase='', $protocol='http')
{
public function __construct(
?string $webserver = null,
int $webport = 4670,
int $controlport = 4671,
?string $controlserver = null,
string $channelbase = '',
string $protocol = 'http'
) {
global $config;
$this->webserver = (empty($webserver)) ? $config['site']['server'] : $webserver;
@ -72,14 +72,16 @@ class MeteorPlugin extends RealtimePlugin
/**
* Pull settings from config file/database if set.
*/
function initialize()
public function initialize()
{
$settings = array('webserver',
$settings = [
'webserver',
'webport',
'controlport',
'controlserver',
'channelbase',
'protocol');
'protocol',
];
foreach ($settings as $name) {
$val = common_config('meteor', $name);
if ($val !== false) {
@ -90,47 +92,57 @@ class MeteorPlugin extends RealtimePlugin
return parent::initialize();
}
function _getScripts()
public function _getScripts()
{
$scripts = parent::_getScripts();
if ($this->protocol == 'https') {
$scripts[] = 'https://'.$this->webserver.(($this->webport == 443) ? '':':'.$this->webport).'/meteor.js';
$scripts[] = 'https://' . $this->webserver . (($this->webport == 443) ? '' : ':' . $this->webport) . '/meteor.js';
} else {
$scripts[] = 'http://'.$this->webserver.(($this->webport == 80) ? '':':'.$this->webport).'/meteor.js';
$scripts[] = 'http://' . $this->webserver . (($this->webport == 80) ? '' : ':' . $this->webport) . '/meteor.js';
}
$scripts[] = $this->path('js/meteorupdater.js');
return $scripts;
}
function _updateInitialize($timeline, $user_id)
public function _updateInitialize($timeline, int $user_id)
{
$script = parent::_updateInitialize($timeline, $user_id);
$ours = sprintf("MeteorUpdater.init(%s, %s, %s, %s);",
$ours = sprintf(
"MeteorUpdater.init(%s, %s, %s, %s);",
json_encode($this->webserver),
json_encode($this->webport),
json_encode($this->protocol),
json_encode($timeline));
json_encode($timeline)
);
return $script." ".$ours;
}
function _connect()
public function _connect()
{
$controlserver = (empty($this->controlserver)) ? $this->webserver : $this->controlserver;
$errno = $errstr = null;
$timeout = 5;
$flags = STREAM_CLIENT_CONNECT;
if ($this->persistent) $flags |= STREAM_CLIENT_PERSISTENT;
if ($this->persistent) {
$flags |= STREAM_CLIENT_PERSISTENT;
}
// May throw an exception.
$this->_socket = stream_socket_client("tcp://{$controlserver}:{$this->controlport}", $errno, $errstr, $timeout, $flags);
$this->_socket = stream_socket_client(
"tcp://{$controlserver}:{$this->controlport}",
$errno,
$errstr,
$timeout,
$flags
);
if (!$this->_socket) {
// TRANS: Exception. %1$s is the control server, %2$s is the control port.
throw new Exception(sprintf(_m('Could not connect to %1$s on %2$s.'),$controlserver,$this->controlport));
throw new Exception(sprintf(_m('Could not connect to %1$s on %2$s.'), $controlserver, $this->controlport));
}
}
function _publish($channel, $message)
public function _publish($channel, $message)
{
$message = json_encode($message);
$message = addslashes($message);
@ -139,12 +151,12 @@ class MeteorPlugin extends RealtimePlugin
$result = fgets($this->_socket);
if (preg_match('/^ERR (.*)$/', $result, $matches)) {
// TRANS: Exception. %s is the Meteor message that could not be added.
throw new Exception(sprintf(_m('Error adding meteor message "%s".'),$matches[1]));
throw new Exception(sprintf(_m('Error adding meteor message "%s".'), $matches[1]));
}
// TODO: parse and deal with result
}
function _disconnect()
public function _disconnect()
{
if (!$this->persistent) {
$cnt = fwrite($this->_socket, "QUIT\n");
@ -154,7 +166,7 @@ class MeteorPlugin extends RealtimePlugin
// Meteord flips out with default '/' separator
function _pathToChannel($path)
public function _pathToChannel(array $path): string
{
if (!empty($this->channelbase)) {
array_unshift($path, $this->channelbase);
@ -164,13 +176,15 @@ class MeteorPlugin extends RealtimePlugin
public function onPluginVersion(array &$versions): bool
{
$versions[] = array('name' => 'Meteor',
$versions[] = [
'name' => 'Meteor',
'version' => self::PLUGIN_VERSION,
'author' => 'Evan Prodromou',
'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Meteor',
'rawdescription' =>
// TRANS: Plugin description.
_m('Plugin to do "real time" updates using Meteor.'));
_m('Plugin to do "real time" updates using Meteor.')
];
return true;
}
}

View File

@ -4,7 +4,7 @@ FriendFeed's "real time" news.
It requires a meteor server.
http://meteorserver.org/
https://github.com/visitsb/meteorserver/
Note that the controller interface needs to be accessible by the Web server, and
the subscriber interface needs to be accessible by your Web users. You MUST
@ -13,7 +13,7 @@ push any message to your subscribers. Not good!
You can enable the plugin with this line in config.php:
addPlugin('Meteor', array('webserver' => 'meteor server address'));
addPlugin('Meteor', ['webserver' => 'meteor server address']);
Available parameters:
* webserver: Web server address. Defaults to site server.

View File

@ -1,9 +0,0 @@
.fake: all clean
all: realtimeupdate.min.js
clean:
rm -f js/realtimeupdate.min.js
realtimeupdate.min.js: js/realtimeupdate.js
yui-compressor js/realtimeupdate.js > js/realtimeupdate.min.js