Merge branch 'master' into FeedPoller

This commit is contained in:
Mikael Nordfeldth 2014-03-06 01:15:19 +01:00
commit c0f65f6ea7
86 changed files with 386 additions and 1046 deletions

View File

@ -182,6 +182,8 @@ sending out SMS email or XMPP messages, for off-line processing. See
'Queues and daemons' above for how to set this up.
enabled: Whether to uses queues. Defaults to false.
daemon: Wather to use queuedaemon. Defaults to false, which means
you'll use OpportunisticQM plugin.
subsystem: Which kind of queueserver to use. Values include "db" for
our hacked-together database queuing (no other server
required) and "stomp" for a stomp server.
@ -532,32 +534,6 @@ welcome: nickname of a user account that sends welcome messages to new
If either of these special user accounts are specified, the users should
be created before the configuration is updated.
snapshot
--------
The software will, by default, send statistical snapshots about the
local installation to a stats server on the status.net Web site. This
data is used by the developers to prioritize development decisions. No
identifying data about users or organizations is collected. The data
is available to the public for review. Participating in this survey
helps StatusNet developers take your needs into account when updating
the software.
run: string indicating when to run the statistics. Values can be 'web'
(run occasionally at Web time), 'cron' (run from a cron script),
or 'never' (don't ever run). If you set it to 'cron', remember to
schedule the script to run on a regular basis.
frequency: if run value is 'web', how often to report statistics.
Measured in Web hits; depends on how active your site is.
Default is 10000 -- that is, one report every 10000 Web hits,
on average.
reporturl: URL to post statistics to. Defaults to StatusNet developers'
report system, but if they go evil or disappear you may
need to update this to another value. Note: if you
don't want to report stats, it's much better to
set 'run' to 'never' than to set this value to something
nonsensical.
attachments
-----------

View File

@ -966,6 +966,9 @@ StartShowNoticeForm: before showing the notice form (before <form>)
EndShowNoticeForm: after showing the notice form (after <form>)
- $action: action being executed
StartShowEntryForms: microapp entry form tab data
- &$tabs: tab assoc array with 'tag' => (title, href to create new entry)
StartGrantRole: when a role is being assigned
- $profile: profile that will have the role
- $role: string name of the role

44
INSTALL
View File

@ -368,12 +368,41 @@ Queues and daemons
------------------
Some activities that StatusNet needs to do, like broadcast OStatus, SMS,
and XMPP messages, can be 'queued' and done by off-line bots instead.
For this to work, you must be able to run long-running offline
processes, either on your main Web server or on another server you
control. (Your other server will still need all the above
prerequisites, with the exception of Apache.) Installing on a separate
server is probably a good idea for high-volume sites.
XMPP messages and TwitterBridge operations, can be 'queued' and done by
off-line bots instead.
Two mechanisms are available to achieve offline operations:
* New embedded OpportunisticQM plugin, which is enabled by default
* Legacy queuedaemon script, which can be enabled via config file.
### OpportunisticQM plugin
This plugin is enabled by default. It tries its best to do background
job during regular HTTP requests, like API or HTML pages calls.
Since queueing system is enabled by default, notices to be broadcasted
will be stored, by default, into DB (table queue_item).
Each time it can, OpportunisticQM will try to handle some of them.
This is a good solution whether you:
* have no access to command line (shared hosting)
* do not want to deal with long-running PHP processes
* run a low traffic GNU social instance
In other case, you really should consider enabling the queuedaemon for
performance reasons. Background daemons are necessary anyway if you wish
to use the Instant Messaging features such as communicating via XMPP.
### queuedaemon
If you want to use legacy queuedaemon, you must be able to run
long-running offline processes, either on your main Web server or on
another server you control. (Your other server will still need all the
above prerequisites, with the exception of Apache.) Installing on a
separate server is probably a good idea for high-volume sites.
1. You'll need the "CLI" (command-line interface) version of PHP
installed on whatever server you use.
@ -399,6 +428,7 @@ server is probably a good idea for high-volume sites.
server!), set the following variable:
$config['queue']['enabled'] = true;
$config['queue']['daemon'] = true;
You may also want to look at the 'daemon' section of this file for
more daemon options. Note that if you set the 'user' and/or 'group'
@ -412,7 +442,7 @@ This will run the queue handlers:
* queuedaemon.php - polls for queued items for inbox processing and
pushing out to OStatus, SMS, XMPP, etc.
* imdaemon.php - if an IM plugin is enabled (like XMPP)
* other daemons that you may have enabled
* other daemons, like TwitterBridge ones, that you may have enabled
These daemons will automatically restart in most cases of failure
including memory leaks (if a memory_limit is set), but may still die

View File

@ -30,18 +30,12 @@
* @author Sarven Capadisli <csarven@status.net>
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
* @author Zach Copley <zach@status.net>
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/
* @link http://status.net
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/personalgroupnav.php';
require_once INSTALLDIR.'/lib/noticelist.php';
require_once INSTALLDIR.'/lib/feedlist.php';
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
class AllAction extends ProfileAction
{
@ -52,16 +46,16 @@ class AllAction extends ProfileAction
return true;
}
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
$user = common_current_user();
if (!empty($user) && $user->streamModeOnly()) {
$stream = new InboxNoticeStream($this->user, Profile::current());
$stream = new InboxNoticeStream($this->target, $this->scoped);
} else {
$stream = new ThreadingInboxNoticeStream($this->user, Profile::current());
$stream = new ThreadingInboxNoticeStream($this->target, $this->scoped);
}
$this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
@ -75,14 +69,13 @@ class AllAction extends ProfileAction
return true;
}
function handle($args)
protected function handle()
{
parent::handle($args);
parent::handle();
if (!$this->user) {
if (!$this->target instanceof Profile) {
// TRANS: Client error when user not found for an action.
$this->clientError(_('No such user.'));
return;
}
$this->showPage();
@ -90,15 +83,13 @@ class AllAction extends ProfileAction
function title()
{
$user = common_current_user();
if (!empty($user) && $user->id == $this->user->id) {
if (!empty($this->scoped) && $this->scoped->id == $this->target->id) {
// TRANS: Title of a user's own start page.
return _('Home timeline');
} else {
$profile = $this->user->getProfile();
// TRANS: Title of another user's start page.
// TRANS: %s is the other user's name.
return sprintf(_("%s's home timeline"), $profile->getBestName());
return sprintf(_("%s's home timeline"), $this->target->getBestName());
}
}
@ -109,60 +100,59 @@ class AllAction extends ProfileAction
common_local_url(
'ApiTimelineFriends', array(
'format' => 'as',
'id' => $this->user->nickname
'id' => $this->target->nickname
)
),
// TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->user->nickname)),
sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->nickname)),
new Feed(Feed::RSS1,
common_local_url(
'allrss', array(
'nickname' =>
$this->user->nickname)
$this->target->nickname)
),
// TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->target->nickname)),
new Feed(Feed::RSS2,
common_local_url(
'ApiTimelineFriends', array(
'format' => 'rss',
'id' => $this->user->nickname
'id' => $this->target->nickname
)
),
// TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->nickname)),
new Feed(Feed::ATOM,
common_local_url(
'ApiTimelineFriends', array(
'format' => 'atom',
'id' => $this->user->nickname
'id' => $this->target->nickname
)
),
// TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))
sprintf(_('Feed for friends of %s (Atom)'), $this->target->nickname))
);
}
function showEmptyListMessage()
{
// TRANS: Empty list message. %s is a user nickname.
$message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->user->nickname) . ' ';
$message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->target->nickname) . ' ';
if (common_logged_in()) {
$current_user = common_current_user();
if ($this->user->id === $current_user->id) {
if ($this->target->id === $this->scoped->id) {
// TRANS: Encouragement displayed on logged in user's empty timeline.
// TRANS: This message contains Markdown links. Keep "](" together.
$message .= _('Try subscribing to more people, [join a group](%%action.groups%%) or post something yourself.');
} else {
// TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@".
// TRANS: This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->target->nickname, $this->target->nickname, '@' . $this->target->nickname);
}
} else {
// TRANS: Encouragement displayed on empty timeline user pages for anonymous users.
// TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->user->nickname);
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->nickname);
}
$this->elementStart('div', 'guide');
@ -196,7 +186,7 @@ class AllAction extends ProfileAction
$this->pagination(
$this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'all', array('nickname' => $this->user->nickname)
$this->page, 'all', array('nickname' => $this->target->nickname)
);
Event::handle('EndShowAllContent', array($this));
@ -225,7 +215,7 @@ class AllAction extends ProfileAction
if (!common_config('performance', 'high')) {
$pop = new PopularNoticeSection($this, Profile::current());
$pop->show();
$pop = new InboxTagCloudSection($this, $this->user);
$pop = new InboxTagCloudSection($this, $this->target);
$pop->show();
}
}

View File

@ -72,7 +72,7 @@ class ApiGroupJoinAction extends ApiAuthAction
/**
* Handle the request
*
* Save the new message
* Join the authenticated user to the group
*
* @return void
*/
@ -80,7 +80,7 @@ class ApiGroupJoinAction extends ApiAuthAction
{
parent::handle();
if (empty($this->user)) {
if (empty($this->scoped)) {
// TRANS: Client error displayed when trying to have a non-existing user join a group.
$this->clientError(_('No such user.'), 404);
}
@ -90,23 +90,23 @@ class ApiGroupJoinAction extends ApiAuthAction
$this->clientError(_('Group not found.'), 404);
}
if ($this->user->isMember($this->group)) {
if ($this->scoped->isMember($this->group)) {
// TRANS: Server error displayed when trying to join a group the user is already a member of.
$this->clientError(_('You are already a member of that group.'), 403);
}
if (Group_block::isBlocked($this->group, $this->user->getProfile())) {
if (Group_block::isBlocked($this->group, $this->scoped)) {
// TRANS: Server error displayed when trying to join a group the user is blocked from joining.
$this->clientError(_('You have been blocked from that group by the admin.'), 403);
}
try {
$this->user->joinGroup($this->group);
$this->scoped->joinGroup($this->group);
} catch (Exception $e) {
// TRANS: Server error displayed when joining a group failed in the database.
// TRANS: %1$s is the joining user's nickname, $2$s is the group nickname for which the join failed.
$this->serverError(sprintf(_('Could not join user %1$s to group %2$s.'),
$cur->nickname, $this->group->nickname));
$this->scoped->nickname, $this->group->nickname));
}
switch($this->format) {

View File

@ -119,8 +119,8 @@ class ApiTimelineMentionsAction extends ApiBareAuthAction
// TRANS: Subtitle for timeline of most recent mentions of a user.
// TRANS: %1$s is the StatusNet sitename, %2$s is a user nickname,
// TRANS: %3$s is a user's full name.
_('%1$s updates that reply to updates from %2$s / %3$s.'),
$sitename, $this->target->getBestName(), $this->target->nickname
_('%1$s updates that reply to updates from %3$s / %2$s.'),
$sitename, $this->target->nickname, $this->target->getBestName()
);
switch($this->format) {

View File

@ -49,7 +49,7 @@ class BlockedfromgroupAction extends GroupAction
return true;
}
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
@ -109,9 +109,9 @@ class BlockedfromgroupAction extends GroupAction
}
}
function handle($args)
protected function handle()
{
parent::handle($args);
parent::handle();
$this->showPage();
}

View File

@ -59,7 +59,7 @@ class EditgroupAction extends GroupAction
* Prepare to run
*/
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
@ -119,13 +119,11 @@ class EditgroupAction extends GroupAction
*
* On GET, show the form. On POST, try to save the group.
*
* @param array $args unused
*
* @return void
*/
function handle($args)
protected function handle()
{
parent::handle($args);
parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->trySave();
} else {
@ -177,7 +175,7 @@ class EditgroupAction extends GroupAction
$nickname = Nickname::normalize($nickname, true);
} catch (NicknameTakenException $e) {
// Abort only if the nickname is occupied by _another_ group
if ($e->profile->id != $this->group->id) {
if ($e->profile->id != $this->group->profile_id) {
$this->showForm($e->getMessage());
return;
}

View File

@ -58,54 +58,45 @@ class GroupblockAction extends RedirectingAction
if (!common_logged_in()) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user.
$this->clientError(_('Not logged in.'));
return false;
}
$token = $this->trimmed('token');
if (empty($token) || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->clientError(_('There was a problem with your session token. Try again, please.'));
return;
}
$id = $this->trimmed('blockto');
if (empty($id)) {
// TRANS: Client error displayed trying to block a user from a group while not specifying a to be blocked user profile.
$this->clientError(_('No profile specified.'));
return false;
}
$this->profile = Profile::getKV('id', $id);
if (empty($this->profile)) {
// TRANS: Client error displayed trying to block a user from a group while specifying a non-existing profile.
$this->clientError(_('No profile with that ID.'));
return false;
}
$group_id = $this->trimmed('blockgroup');
if (empty($group_id)) {
// TRANS: Client error displayed trying to block a user from a group while not specifying a group to block a profile from.
$this->clientError(_('No group specified.'));
return false;
}
$this->group = User_group::getKV('id', $group_id);
if (empty($this->group)) {
// TRANS: Client error displayed trying to block a user from a group while specifying a non-existing group.
$this->clientError(_('No such group.'));
return false;
}
$user = common_current_user();
if (!$user->isAdmin($this->group)) {
// TRANS: Client error displayed trying to block a user from a group while not being an admin user.
$this->clientError(_('Only an admin can block group members.'), 401);
return false;
}
if (Group_block::isBlocked($this->group, $this->profile)) {
// TRANS: Client error displayed trying to block a user from a group while user is already blocked from the given group.
$this->clientError(_('User is already blocked from group.'));
return false;
}
// XXX: could have proactive blocks, but we don't have UI for it.
if (!$this->profile->isMember($this->group)) {
// TRANS: Client error displayed trying to block a user from a group while user is not a member of given group.
$this->clientError(_('User is not a member of group.'));
return false;
}
return true;
}

View File

@ -60,7 +60,7 @@ class GrouplogoAction extends GroupAction
/**
* Prepare to run
*/
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
@ -115,9 +115,9 @@ class GrouplogoAction extends GroupAction
return true;
}
function handle($args)
protected function handle()
{
parent::handle($args);
parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePost();
} else {

View File

@ -52,7 +52,7 @@ class GroupmembersAction extends GroupAction
return true;
}
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
@ -77,9 +77,9 @@ class GroupmembersAction extends GroupAction
}
}
function handle($args)
protected function handle()
{
parent::handle($args);
parent::handle();
$this->showPage();
}

View File

@ -53,7 +53,7 @@ class GroupqueueAction extends GroupAction
}
// @todo FIXME: most of this belongs in a base class, sounds common to most group actions?
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
@ -119,9 +119,9 @@ class GroupqueueAction extends GroupAction
}
}
function handle($args)
protected function handle()
{
parent::handle($args);
parent::handle();
$this->showPage();
}

View File

@ -91,7 +91,7 @@ class ShowgroupAction extends GroupAction
*
* @return boolean success flag
*/
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
@ -123,8 +123,9 @@ class ShowgroupAction extends GroupAction
*
* @return void
*/
function handle($args)
protected function handle()
{
parent::handle();
$this->showPage();
}

View File

@ -70,7 +70,7 @@ class ShownoticeAction extends Action
*
* @return success flag
*/
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
if ($this->boolean('ajax')) {
@ -117,16 +117,16 @@ class ShownoticeAction extends Action
*
* @return Notice
*/
function getNotice()
protected function getNotice()
{
$id = $this->arg('notice');
$notice = Notice::getKV('id', $id);
if (empty($notice)) {
if (!$notice instanceof Notice) {
// Did we used to have it, and it got deleted?
$deleted = Deleted_notice::getKV($id);
if (!empty($deleted)) {
if ($deleted instanceof Deleted_notice) {
// TRANS: Client error displayed trying to show a deleted notice.
$this->clientError(_('Notice deleted.'), 410);
} else {
@ -211,9 +211,9 @@ class ShownoticeAction extends Action
*
* @return void
*/
function handle($args)
protected function handle()
{
parent::handle($args);
parent::handle();
if ($this->boolean('ajax')) {
$this->showAjax();

View File

@ -1,255 +0,0 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Snapshots administration panel
*
* 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 Settings
* @package StatusNet
* @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')) {
exit(1);
}
/**
* Manage snapshots
*
* @category Admin
* @package StatusNet
* @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/
*/
class SnapshotadminpanelAction extends AdminPanelAction
{
/**
* Returns the page title
*
* @return string page title
*/
function title()
{
// TRANS: Title for admin panel to configure snapshots.
return _m('TITLE','Snapshots');
}
/**
* Instructions for using this form.
*
* @return string instructions
*/
function getInstructions()
{
// TRANS: Instructions for admin panel to configure snapshots.
return _('Manage snapshot configuration');
}
/**
* Show the snapshots admin panel form
*
* @return void
*/
function showForm()
{
$form = new SnapshotAdminPanelForm($this);
$form->show();
return;
}
/**
* Save settings from the form
*
* @return void
*/
function saveSettings()
{
static $settings = array(
'snapshot' => array('run', 'reporturl', 'frequency')
);
$values = array();
foreach ($settings as $section => $parts) {
foreach ($parts as $setting) {
$values[$section][$setting] = $this->trimmed($setting);
}
}
// This throws an exception on validation errors
$this->validate($values);
// assert(all values are valid);
$config = new Config();
$config->query('BEGIN');
foreach ($settings as $section => $parts) {
foreach ($parts as $setting) {
Config::save($section, $setting, $values[$section][$setting]);
}
}
$config->query('COMMIT');
return;
}
function validate(&$values)
{
// Validate snapshot run value
if (!in_array($values['snapshot']['run'], array('web', 'cron', 'never'))) {
// TRANS: Client error displayed on admin panel for snapshots when providing an invalid run value.
$this->clientError(_('Invalid snapshot run value.'));
}
// Validate snapshot frequency value
if (!Validate::number($values['snapshot']['frequency'])) {
// TRANS: Client error displayed on admin panel for snapshots when providing an invalid value for frequency.
$this->clientError(_('Snapshot frequency must be a number.'));
}
// Validate report URL
if (!is_null($values['snapshot']['reporturl'])
&& !common_valid_http_url($values['snapshot']['reporturl'])) {
// TRANS: Client error displayed on admin panel for snapshots when providing an invalid report URL.
$this->clientError(_('Invalid snapshot report URL.'));
}
}
}
// @todo FIXME: add documentation
class SnapshotAdminPanelForm extends AdminForm
{
/**
* ID of the form
*
* @return int ID of the form
*/
function id()
{
return 'form_snapshot_admin_panel';
}
/**
* class of the form
*
* @return string class of the form
*/
function formClass()
{
return 'form_settings';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
return common_local_url('snapshotadminpanel');
}
/**
* Data elements of the form
*
* @return void
*/
function formData()
{
$this->out->elementStart(
'fieldset',
array('id' => 'settings_admin_snapshots')
);
// TRANS: Fieldset legend on admin panel for snapshots.
$this->out->element('legend', null, _m('LEGEND','Snapshots'));
$this->out->elementStart('ul', 'form_data');
$this->li();
$snapshot = array(
// TRANS: Option in dropdown for snapshot method in admin panel for snapshots.
'web' => _('Randomly during web hit'),
// TRANS: Option in dropdown for snapshot method in admin panel for snapshots.
'cron' => _('In a scheduled job'),
// TRANS: Option in dropdown for snapshot method in admin panel for snapshots.
'never' => _('Never')
);
$this->out->dropdown(
'run',
// TRANS: Dropdown label for snapshot method in admin panel for snapshots.
_('Data snapshots'),
$snapshot,
// TRANS: Dropdown title for snapshot method in admin panel for snapshots.
_('When to send statistical data to status.net servers.'),
false,
$this->value('run', 'snapshot')
);
$this->unli();
$this->li();
$this->input(
'frequency',
// TRANS: Input field label for snapshot frequency in admin panel for snapshots.
_('Frequency'),
// TRANS: Input field title for snapshot frequency in admin panel for snapshots.
_('Snapshots will be sent once every N web hits.'),
'snapshot'
);
$this->unli();
$this->li();
$this->input(
'reporturl',
// TRANS: Input field label for snapshot report URL in admin panel for snapshots.
_('Report URL'),
// TRANS: Input field title for snapshot report URL in admin panel for snapshots.
_('Snapshots will be sent to this URL.'),
'snapshot'
);
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
$this->out->submit(
'submit',
// TRANS: Button text to save snapshot settings.
_m('BUTTON','Save'),
'submit',
null,
// TRANS: Button title to save snapshot settings.
_('Save snapshot settings.')
);
}
}

View File

@ -22,32 +22,28 @@
* @category Data
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2010 StatusNet Inc.
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
if (!defined('GNUSOCIAL')) { exit(1); }
class Conversation extends Managed_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'conversation'; // table name
public $__table = 'conversation'; // table name
public $id; // int(4) primary_key not_null
public $uri; // varchar(255) unique_key
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
public static function schemaDef()
{
return array(
'fields' => array(
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'unique identifier'),
'id' => array('type' => 'int', 'not null' => true, 'description' => 'should be set from root notice id (since 2014-03-01 commit)'),
'uri' => array('type' => 'varchar', 'length' => 255, 'description' => 'URI of the conversation'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
@ -64,25 +60,20 @@ class Conversation extends Managed_DataObject
*
* @return Conversation the new conversation DO
*/
static function create()
static function create(Notice $notice)
{
if (empty($notice->id)) {
throw new ServerException(_('Tried to create conversation for not yet inserted notice'));
}
$conv = new Conversation();
$conv->created = common_sql_now();
$id = $conv->insert();
$conv->id = $notice->id;
$conv->uri = common_local_url('conversation', array('id' => $notice->id), null, null, false);
$result = $conv->insert();
if (empty($id)) {
if ($result === false) {
common_log_db_error($conv, 'INSERT', __FILE__);
return null;
}
$orig = clone($conv);
$orig->uri = common_local_url('conversation', array('id' => $id),
null, null, false);
$result = $orig->update($conv);
if (empty($result)) {
common_log_db_error($conv, 'UPDATE', __FILE__);
return null;
throw new ServerException(_('Failed to create conversation for notice'));
}
return $conv;

View File

@ -116,7 +116,9 @@ class File extends Managed_DataObject
} catch (Exception $e) {
return false;
}
if ($oembed_data === false) {
return false;
}
$fo = File_oembed::getKV('file_id', $this->id);
if ($fo instanceof File_oembed) {

View File

@ -142,20 +142,20 @@ class Notice extends Managed_DataObject
const FOLLOWER_SCOPE = 8;
protected $_profile = -1;
public function getProfile()
{
if ($this->_profile === -1) {
$this->_setProfile(Profile::getKV('id', $this->profile_id));
}
if (!$this->_profile instanceof Profile) {
throw new NoProfileException($this->profile_id);
}
return $this->_profile;
}
function _setProfile(Profile $profile)
public function _setProfile(Profile $profile=null)
{
if (!$profile instanceof Profile) {
throw new NoProfileException($this->profile_id);
}
$this->_profile = $profile;
}
@ -216,6 +216,16 @@ class Notice extends Managed_DataObject
return $this->url ?: $this->uri;
}
public static function getByUri($uri)
{
$notice = new Notice();
$notice->uri = $uri;
if (!$notice->find(true)) {
throw new NoResultException($notice);
}
return $notice;
}
/**
* Extract #hashtags from this notice's content and save them to the database.
*/
@ -577,13 +587,13 @@ class Notice extends Managed_DataObject
// the beginning of a new conversation.
if (empty($notice->conversation)) {
$conv = Conversation::create();
$conv = Conversation::create($notice);
$notice->conversation = $conv->id;
$changed = true;
}
if ($changed) {
if (!$notice->update($orig)) {
if ($notice->update($orig) === false) {
common_log_db_error($notice, 'UPDATE', __FILE__);
// TRANS: Server exception thrown when a notice cannot be updated.
throw new ServerException(_('Problem saving notice.'));
@ -2500,10 +2510,15 @@ class Notice extends Managed_DataObject
{
$map = self::getProfiles($notices);
foreach ($notices as $notice) {
if (array_key_exists($notice->profile_id, $map)) {
$notice->_setProfile($map[$notice->profile_id]);
}
foreach ($notices as $entry=>$notice) {
try {
if (array_key_exists($notice->profile_id, $map)) {
$notice->_setProfile($map[$notice->profile_id]);
}
} catch (NoProfileException $e) {
common_log(LOG_WARNING, "Failed to fill profile in Notice with non-existing entry for profile_id: {$e->id}");
unset($notices[$entry]);
}
}
return array_values($map);

View File

@ -256,7 +256,7 @@ class Profile extends Managed_DataObject
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function isMember($group)
function isMember(User_group $group)
{
$groups = $this->getGroups(0, null);
while ($groups instanceof User_group && $groups->fetch()) {
@ -267,7 +267,7 @@ class Profile extends Managed_DataObject
return false;
}
function isAdmin($group)
function isAdmin(User_group $group)
{
$gm = Group_member::pkeyGet(array('profile_id' => $this->id,
'group_id' => $group->id));
@ -745,7 +745,7 @@ class Profile extends Managed_DataObject
* @param Profile $other
* @return boolean
*/
function isSubscribed($other)
function isSubscribed(Profile $other)
{
return Subscription::exists($this, $other);
}

View File

@ -134,7 +134,7 @@ class User extends Managed_DataObject
return $this->_profile;
}
function isSubscribed($other)
function isSubscribed(Profile $other)
{
return $this->getProfile()->isSubscribed($other);
}
@ -616,12 +616,12 @@ class User extends Managed_DataObject
return true;
}
function isMember($group)
function isMember(User_group $group)
{
return $this->getProfile()->isMember($group);
}
function isAdmin($group)
function isAdmin(User_group $group)
{
return $this->getProfile()->isAdmin($group);
}

View File

@ -242,8 +242,6 @@ function main()
}
global $user, $action;
Snapshot::check();
if (!_have_config()) {
$msg = sprintf(
// TRANS: Error message displayed when there is no StatusNet configuration file.

View File

@ -118,7 +118,7 @@ class WebInstaller extends Installer
function main()
{
if (!$this->checkPrereqs()) {
$this->showForm();
$this->warning(_('Please fix the above stated problems and refresh this page to continue installing.'));
return;
}
@ -239,12 +239,12 @@ class WebInstaller extends Installer
<li>
<label for="admin_nickname">Administrator nickname</label>
<input type="text" id="admin_nickname" name="admin_nickname" value="{$post->value('admin_nickname')}" />
<p class="form_guide">Nickname for the initial StatusNet user (administrator)</p>
<p class="form_guide">Nickname for the initial user (administrator)</p>
</li>
<li>
<label for="admin_password">Administrator password</label>
<input type="password" id="admin_password" name="admin_password" value="{$post->value('admin_password')}" />
<p class="form_guide">Password for the initial StatusNet user (administrator)</p>
<p class="form_guide">Password for the initial user (administrator)</p>
</li>
<li>
<label for="admin_password2">Confirm password</label>
@ -253,7 +253,7 @@ class WebInstaller extends Installer
<li>
<label for="admin_email">Administrator e-mail</label>
<input id="admin_email" name="admin_email" value="{$post->value('admin_email')}" />
<p class="form_guide">Optional email address for the initial StatusNet user (administrator)</p>
<p class="form_guide">Optional email address for the initial user (administrator)</p>
</li>
<li>
<label for="admin_updates">Subscribe to announcements</label>
@ -375,14 +375,15 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
<head>
<title>Install StatusNet</title>
<link rel="shortcut icon" href="favicon.ico"/>
<title>Install GNU social</title>
<link rel="shortcut icon" href="favicon.ico"/>
<link rel="stylesheet" type="text/css" href="theme/base/css/display.css" media="screen, projection, tv"/>
<link rel="stylesheet" type="text/css" href="theme/neo/css/display.css" media="screen, projection, tv"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css" /><![endif]-->
<!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css" /><![endif]-->
<!--[if lte IE 7]><link rel="stylesheet" type="text/css" theme/base/css/ie7.css" /><![endif]-->
<script src="js/extlib/jquery.min.js"></script>
<script src="js/extlib/jquery.js"></script>
<script src="js/install.js"></script>
</head>
<body id="install">
@ -390,8 +391,8 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
<div id="header">
<address id="site_contact" class="vcard">
<a class="url home bookmark" href=".">
<img class="logo photo" src="theme/neo/logo.png" alt="StatusNet"/>
<span class="fn org">StatusNet</span>
<img class="logo photo" src="theme/neo/logo.png" alt="GNU social"/>
<span class="fn org">GNU social</span>
</a>
</address>
<div id="site_nav_global_primary"></div>
@ -404,7 +405,7 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
<div id="content">
<div id="content_inner">
<h1>Install StatusNet</h1>
<h1>Install GNU social</h1>
<?php
$installer = new WebInstaller();
$installer->main();

View File

@ -1,16 +1,12 @@
.fake: all clean
TARGETS=util.min.js extlib/json2.min.js extlib/jquery.cookie.min.js
UTIL_SOURCES=util.js xbImportNode.js geometa.js
all: $(TARGETS)
clean:
rm -f $(TARGETS)
util.min.js: $(UTIL_SOURCES)
cat $+ | yui-compressor --type js > $@
extlib/json2.min.js: extlib/json2.js
yui-compressor $+ > $@

File diff suppressed because one or more lines are too long

View File

@ -1,8 +0,0 @@
/*
* jQuery Cookie Plugin v1.3.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{a(jQuery)}}(function(c){var a=/\+/g;function d(f){if(b.raw){return f}try{return decodeURIComponent(f.replace(a," "))}catch(g){}}function e(f){if(f.indexOf('"')===0){f=f.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\")}f=d(f);try{return b.json?JSON.parse(f):f}catch(g){}}var b=c.cookie=function(n,m,r){if(m!==undefined){r=c.extend({},b.defaults,r);if(typeof r.expires==="number"){var o=r.expires,q=r.expires=new Date();q.setDate(q.getDate()+o)}m=b.json?JSON.stringify(m):String(m);return(document.cookie=[b.raw?n:encodeURIComponent(n),"=",b.raw?m:encodeURIComponent(m),r.expires?"; expires="+r.expires.toUTCString():"",r.path?"; path="+r.path:"",r.domain?"; domain="+r.domain:"",r.secure?"; secure":""].join(""))}var s=n?undefined:{};var p=document.cookie?document.cookie.split("; "):[];for(var k=0,h=p.length;k<h;k++){var j=p[k].split("=");var f=d(j.shift());var g=j.join("=");if(n&&n===f){s=e(g);break}if(!n&&(g=e(g))!==undefined){s[f]=g}}return s};b.defaults={};c.removeCookie=function(g,f){if(c.cookie(g)!==undefined){c.cookie(g,"",c.extend({},f,{expires:-1}));return true}return false}}));

File diff suppressed because one or more lines are too long

View File

@ -1,10 +0,0 @@
/**
* In-Field Label jQuery Plugin v0.2.1
* http://fuelyourcoding.com/scripts/infield.html
* http://github.com/streetpc/jquery-infieldlabels
*
* Copyright (c) 2009 Doug Neiner, Adrien Lavoillotte
* Dual licensed under the MIT and GPL licenses, see:
* http://docs.jquery.com/License
*/
(function(f){var d=0,a=1,b=2,g=/^(?:text|password|search|number|tel|url|email|date(?:time(?:-local)?)?|time|month|week)?$/,c=function(i,h){return(i<<3)|h},e={};e[c(a,d)]=function(h){h.fadeTo(1)};e[c(b,d)]=function(h){h.$label.css({opacity:1}).show();h.emptied(true)};e[c(d,a)]=function(h){h.fadeTo(h.options.fadeOpacity)};e[c(b,a)]=function(h){h.$label.css({opacity:h.options.fadeOpacity}).show();h.emptied(true)};e[c(d,b)]=function(h){h.$label.hide();h.emptied(false)};e[c(a,b)]=e[c(d,b)];f.InFieldLabels=function(i,k,h){var j=this;j.$label=f(i);j.label=i;j.$field=f(k);j.field=k;j.$label.data("InFieldLabels",j);j.state=d;j.init=function(){j.options=f.extend({},f.InFieldLabels.defaultOptions,h);if(j.options.labelClass){j.$label.addClass(j.options.labelClass)}if(j.options.disableAutocomplete){j.$field.attr("autocomplete","off")}j.$field.bind("blur focus change keyup.infield cut",j.updateState).bind("paste",function(l){j.setState(b)});j.updateState()};j.emptied=function(l){if(!j.options.emptyWatch){if(l){j.$field.bind("keyup.infield",j.updateState)}else{j.$field.unbind("keyup.infield",j.updateState)}}};j.fadeTo=function(l){if(!j.options.fadeDuration){j.$label.css({opacity:l})}else{j.$label.stop().animate({opacity:l},j.options.fadeDuration)}};j.updateState=function(o,m){var n=b;if(j.field.value===""){var l=o&&o.type;if(l==="focus"||l==="keyup"){l=true}else{if(l==="blur"||l==="change"){l=false}else{l=j.$field.is(":focus")}}n=l?a:d}j.setState(n,m)};j.setState=function(m,l){if(m===j.state){return}var n=e[c(j.state,m)];if(typeof n==="function"){n(j);j.state=m}else{l||j.updateState(null,true)}};j.init()};f.InFieldLabels.defaultOptions={emptyWatch:true,disableAutocomplete:true,fadeOpacity:0.5,fadeDuration:300,labelClass:"in-field"};f.fn.inFieldLabels=function(h){return this.each(function(){if(this.tagName!=="LABEL"){return}var k=this.getAttribute("for")||this.htmlFor,j,i=true;if(!k){return}j=document.getElementById(k);if(!j){return}if(j.tagName==="INPUT"){i=g.test(j.type.toLowerCase())}else{if(j.tagName!=="TEXTAREA"){i=false}}i=i&&!j.getAttribute("placeholder");if(!i){return}(new f.InFieldLabels(this,j,h))})}}(jQuery));

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
if(typeof JSON!=="object"){JSON={}}(function(){function f(n){return n<10?"0"+n:n}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()}}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==="string"?c:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+string+'"'}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==="object"&&typeof value.toJSON==="function"){value=value.toJSON(key)}if(typeof rep==="function"){value=rep.call(holder,key,value)}switch(typeof value){case"string":return quote(value);case"number":return isFinite(value)?String(value):"null";case"boolean":case"null":return String(value);case"object":if(!value){return"null"}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==="[object Array]"){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||"null"}v=partial.length===0?"[]":gap?"[\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"]":"["+partial.join(",")+"]";gap=mind;return v}if(rep&&typeof rep==="object"){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]==="string"){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?": ":":")+v)}}}}v=partial.length===0?"{}":gap?"{\n"+gap+partial.join(",\n"+gap)+"\n"+mind+"}":"{"+partial.join(",")+"}";gap=mind;return v}}if(typeof JSON.stringify!=="function"){JSON.stringify=function(value,replacer,space){var i;gap="";indent="";if(typeof space==="number"){for(i=0;i<space;i+=1){indent+=" "}}else{if(typeof space==="string"){indent=space}}rep=replacer;if(replacer&&typeof replacer!=="function"&&(typeof replacer!=="object"||typeof replacer.length!=="number")){throw new Error("JSON.stringify")}return str("",{"":value})}}if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());

View File

@ -1414,6 +1414,8 @@ var SN = { // StatusNet
SN.Init.NoticeFormSetup(form);
})
.find('.notice_data-text').focus();
return false;
},
showMoreMenuItems: function (menuid) {

1
js/util.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -370,32 +370,20 @@ class Action extends HTMLOutputter // lawsuit
{
if (Event::handle('StartShowScripts', array($this))) {
if (Event::handle('StartShowJQueryScripts', array($this))) {
if (common_config('site', 'minify')) {
$this->script('extlib/jquery.min.js');
$this->script('extlib/jquery.form.min.js');
$this->script('extlib/jquery-ui/jquery-ui.min.js');
$this->script('extlib/jquery.cookie.min.js');
$this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.min.js', StatusNet::isHTTPS()).'"); }');
$this->script('extlib/jquery.infieldlabel.min.js');
} else {
$this->script('extlib/jquery.js');
$this->script('extlib/jquery.form.js');
$this->script('extlib/jquery-ui/jquery-ui.js');
$this->script('extlib/jquery.cookie.js');
$this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', StatusNet::isHTTPS()).'"); }');
$this->script('extlib/jquery.infieldlabel.js');
}
$this->script('extlib/jquery.js');
$this->script('extlib/jquery.form.js');
$this->script('extlib/jquery-ui/jquery-ui.js');
$this->script('extlib/jquery.cookie.js');
$this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', StatusNet::isHTTPS()).'"); }');
$this->script('extlib/jquery.infieldlabel.js');
Event::handle('EndShowJQueryScripts', array($this));
}
if (Event::handle('StartShowStatusNetScripts', array($this))) {
if (common_config('site', 'minify')) {
$this->script('util.min.js');
} else {
$this->script('util.js');
$this->script('xbImportNode.js');
$this->script('geometa.js');
}
$this->script('util.js');
$this->script('xbImportNode.js');
$this->script('geometa.js');
// This route isn't available in single-user mode.
// Not sure why, but it causes errors here.
$this->inlineScript('var _peopletagAC = "' .
@ -693,7 +681,8 @@ class Action extends HTMLOutputter // lawsuit
function showNoticeForm()
{
// TRANS: Tab on the notice form.
$tabs = array('status' => _m('TAB','Status'));
$tabs = array('status' => array('title' => _m('TAB','Status'),
'href' => common_local_url('newnotice')));
$this->elementStart('div', 'input_forms');
@ -701,7 +690,8 @@ class Action extends HTMLOutputter // lawsuit
$this->elementStart('ul', array('class' => 'nav',
'id' => 'input_form_nav'));
foreach ($tabs as $tag => $title) {
foreach ($tabs as $tag => $data) {
$tag = htmlspecialchars($tag);
$attrs = array('id' => 'input_form_nav_'.$tag,
'class' => 'input_form_nav_tab');
@ -714,8 +704,9 @@ class Action extends HTMLOutputter // lawsuit
$this->elementStart('li', $attrs);
$this->element('a',
array('href' => 'javascript:SN.U.switchInputFormTab("'.$tag.'")'),
$title);
array('onclick' => 'return SN.U.switchInputFormTab("'.$tag.'");',
'href' => $data['href']),
$data['title']);
$this->elementEnd('li');
}
@ -728,7 +719,7 @@ class Action extends HTMLOutputter // lawsuit
$form->show();
$this->elementEnd('div');
foreach ($tabs as $tag => $title) {
foreach ($tabs as $tag => $data) {
$attrs = array('class' => 'input_form',
'id' => 'input_form_'.$tag);

View File

@ -134,14 +134,6 @@ class AdminPanelNav extends Menu
$menu_title, $action_name == 'sitenoticeadminpanel', 'nav_sitenotice_admin_panel');
}
if (AdminPanelAction::canAdmin('snapshot')) {
// TRANS: Menu item title in administrator navigation panel.
$menu_title = _('Snapshots configuration');
// TRANS: Menu item in administrator navigation panel.
$this->out->menuItem(common_local_url('snapshotadminpanel'), _m('MENU','Snapshots'),
$menu_title, $action_name == 'snapshotadminpanel', 'nav_snapshot_admin_panel');
}
if (AdminPanelAction::canAdmin('license')) {
// TRANS: Menu item title in administrator navigation panel.
$menu_title = _('Set site license');

View File

@ -215,6 +215,8 @@ class ApiAction extends Action
// TODO: avatar url template (example.com/user/avatar?size={x}x{y})
$twitter_user['profile_image_url'] = Avatar::urlByProfile($profile, AVATAR_STREAM_SIZE);
$twitter_user['profile_image_url_https'] = $twitter_user['profile_image_url'];
// START introduced by qvitter API, not necessary for StatusNet API
$twitter_user['profile_image_url_profile_size'] = Avatar::urlByProfile($profile, AVATAR_PROFILE_SIZE);
try {
@ -1466,7 +1468,7 @@ class ApiAction extends Action
} else if ($this->arg('screen_name')) {
$nickname = common_canonical_nickname($this->arg('screen_name'));
$user = User::getKV('nickname', $nickname);
return $user ? $user->getProfile() : null;
return $user instanceof User ? $user->getProfile() : null;
} else {
// Fall back to trying the currently authenticated user
return $this->scoped;

View File

@ -30,7 +30,7 @@ class ApiGNUsocialOAuthDataStore extends OAuthDataStore
{
$con = Consumer::getKV('consumer_key', $consumerKey);
if (!$con) {
if (!$con instanceof Consumer) {
// Create an anon consumer and anon application if one
// doesn't exist already

View File

@ -28,7 +28,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
exit(1);
}
@ -67,12 +67,12 @@ class AttachmentList extends Widget
}
/**
* show the list of notices
* show the list of attachments
*
* "Uses up" the stream by looping through it. So, probably can't
* be called twice on the same list.
*
* @return int count of notices listed.
* @return int count of items listed.
*/
function show()
{
@ -101,16 +101,13 @@ class AttachmentList extends Widget
}
/**
* returns a new list item for the current notice
* returns a new list item for the current attachment
*
* Recipe (factory?) method; overridden by sub-classes to give
* a different list item class.
* @param File $attachment the current attachment
*
* @param Notice $notice the current notice
*
* @return NoticeListItem a list item for displaying the notice
* @return AttachmentListItem a list item for displaying the attachment
*/
function newListItem($attachment)
function newListItem(File $attachment)
{
return new AttachmentListItem($attachment, $this->out);
}
@ -142,13 +139,9 @@ class AttachmentListItem extends Widget
var $oembed = null;
/**
* constructor
*
* Also initializes the profile attribute.
*
* @param Notice $notice The notice we'll display
* @param File $attachment the attachment we will display
*/
function __construct($attachment, $out=null)
function __construct(File $attachment, $out=null)
{
parent::__construct($out);
$this->attachment = $attachment;

View File

@ -62,7 +62,6 @@ $default =
'use_x_sendfile' => false,
'notice' => null, // site wide notice text
'build' => 1, // build number, for code-dependent cache
'minify' => false, // true to use the minified versions of JS files; false to use orig files. Can aid during development
),
'db' =>
array('database' => null, // must be set
@ -86,6 +85,7 @@ $default =
'facility' => LOG_USER),
'queue' =>
array('enabled' => true,
'daemon' => false, # Use queuedaemon. Default to false
'subsystem' => 'db', # default to database, or 'stomp'
'stomp_server' => null,
'queue_basename' => '/queue/statusnet/',
@ -207,10 +207,6 @@ $default =
'newuser' =>
array('default' => null,
'welcome' => null),
'snapshot' =>
array('run' => 'web',
'frequency' => 10000,
'reporturl' => 'http://status.net/stats/report'),
'attachments' =>
array('server' => null,
'dir' => INSTALLDIR . '/file/',

View File

@ -46,7 +46,7 @@ class GroupAction extends Action
{
protected $group;
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);

View File

@ -34,7 +34,7 @@
* @author Tom Adams <tom@holizz.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc http://status.net
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/
* @version 1.0.x
* @link http://status.net
@ -96,25 +96,12 @@ abstract class Installer
}
}
if (version_compare(PHP_VERSION, '5.2.3', '<')) {
$this->warning('Require PHP version 5.2.3 or greater.');
if (version_compare(PHP_VERSION, '5.3.2', '<')) {
$this->warning('Require PHP version 5.3.2 or greater.');
$pass = false;
}
// Look for known library bugs
$str = "abcdefghijklmnopqrstuvwxyz";
$replaced = preg_replace('/[\p{Cc}\p{Cs}]/u', '*', $str);
if ($str != $replaced) {
$this->warning('PHP is linked to a version of the PCRE library ' .
'that does not support Unicode properties. ' .
'If you are running Red Hat Enterprise Linux / ' .
'CentOS 5.4 or earlier, see <a href="' .
'http://status.net/wiki/Red_Hat_Enterprise_Linux#PCRE_library' .
'">our documentation page</a> on fixing this.');
$pass = false;
}
$reqs = array('gd', 'curl',
$reqs = array('gd', 'curl', 'json',
'xmlwriter', 'mbstring', 'xml', 'dom', 'simplexml');
foreach ($reqs as $req) {
@ -227,7 +214,7 @@ abstract class Installer
$fail = false;
if (empty($this->adminNick)) {
$this->updateStatus("No initial StatusNet user nickname specified.", true);
$this->updateStatus("No initial user nickname specified.", true);
$fail = true;
}
if ($this->adminNick && !preg_match('/^[0-9a-z]{1,64}$/', $this->adminNick)) {
@ -245,7 +232,7 @@ abstract class Installer
}
if (empty($this->adminPass)) {
$this->updateStatus("No initial StatusNet user password specified.", true);
$this->updateStatus("No initial user password specified.", true);
$fail = true;
}
@ -301,7 +288,7 @@ abstract class Installer
} else if ($this->dbtype == 'pgsql') {
$record = $conn->getRow('SHOW server_encoding');
if ($record->server_encoding != 'UTF8') {
$this->updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
$this->updateStatus("GNU social requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
return false;
}
}
@ -508,7 +495,7 @@ abstract class Installer
/**
* Create the initial admin user account.
* Side effect: may load portions of StatusNet framework.
* Side effect: may load portions of GNU social framework.
* Side effect: outputs program info
*/
function registerInitialUser()
@ -612,7 +599,7 @@ abstract class Installer
);
} else {
$this->updateStatus(
"Could not create initial GNU social user.",
"Could not create initial user account.",
true
);
return false;
@ -637,9 +624,9 @@ abstract class Installer
$scheme = $this->ssl === 'always' ? 'https' : 'http';
$link = "{$scheme}://{$this->server}/{$this->path}";
$this->updateStatus("StatusNet has been installed at $link");
$this->updateStatus("GNU social has been installed at $link");
$this->updateStatus(
"<strong>DONE!</strong> You can visit your <a href='$link'>new StatusNet site</a> (login as '$this->adminNick'). If this is your first StatusNet install, you may want to poke around our <a href='http://status.net/wiki/Getting_started'>Getting Started guide</a>."
'<strong>DONE!</strong> You can visit your <a href="'.htmlspecialchars($link).'">new GNU social site</a> (log in as "'.htmlspecialchars($this->adminNick).'"). If this is your first GNU social install, make your experience the best possible by visiting our resource site to join the mailing list and <a href="http://gnu.io/resources/">good documentation</a>.'
);
return true;

View File

@ -50,9 +50,7 @@ function mail_backend()
if (!$backend) {
$backend = Mail::factory(common_config('mail', 'backend'),
(common_config('mail', 'params')) ?
common_config('mail', 'params') :
array());
common_config('mail', 'params') ?: array());
if (PEAR::isError($backend)) {
common_server_error($backend->getMessage(), 500);
}

View File

@ -162,6 +162,14 @@ abstract class MicroAppPlugin extends Plugin
*/
abstract function deleteRelated($notice);
/**
*
*/
public function newFormAction() {
// such as 'newbookmark' or 'newevent' route
return 'new'.$this->tag();
}
/**
* Check if a given notice object should be handled by this micro-app
* plugin.
@ -536,7 +544,9 @@ abstract class MicroAppPlugin extends Plugin
function onStartShowEntryForms(&$tabs)
{
$tabs[$this->tag()] = $this->appTitle();
$tabs[$this->tag()] = array('title' => $this->appTitle(),
'href' => common_local_url($this->newFormAction()),
);
return true;
}

View File

@ -28,13 +28,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/favorform.php';
require_once INSTALLDIR.'/lib/disfavorform.php';
require_once INSTALLDIR.'/lib/attachmentlist.php';
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* widget for displaying a list of notices
@ -136,8 +130,7 @@ class NoticeList extends Widget
$profiles = Notice::fillProfiles($notices);
$p = Profile::current();
if (!empty($p)) {
if ($p instanceof Profile) {
$ids = array();

View File

@ -99,7 +99,11 @@ class Plugin
*/
public function onAutoload($cls) {
$cls = basename($cls);
$basedir = INSTALLDIR . '/plugins/' . mb_substr(get_called_class(), 0, -6);
$basedir = INSTALLDIR . '/local/plugins/' . mb_substr(get_called_class(), 0, -6);
if (!file_exists($basedir)) {
$basedir = INSTALLDIR . '/plugins/' . mb_substr(get_called_class(), 0, -6);
}
$file = null;
if (preg_match('/^(\w+)(Action|Form)$/', $cls, $type)) {

View File

@ -180,7 +180,8 @@ abstract class QueueManager extends IoManager
$object = unserialize($frame);
// If it is a string, we really store a JSON object in there
if (is_string($object)) {
// except if it begins with '<', because then it is XML.
if (is_string($object) && substr($object, 0, 1) != '<') {
$json = json_decode($object);
if ($json === null) {
throw new Exception('Bad frame in queue item');

View File

@ -397,6 +397,15 @@ class Router
$m->connect('api/statuses/replies.:format',
array('action' => 'ApiTimelineMentions',
'format' => '(xml|json|rss|atom|as)'));
$m->connect('api/statuses/mentions_timeline/:id.:format',
array('action' => 'ApiTimelineMentions',
'id' => Nickname::INPUT_FMT,
'format' => '(xml|json|rss|atom|as)'));
$m->connect('api/statuses/mentions_timeline.:format',
array('action' => 'ApiTimelineMentions',
'format' => '(xml|json|rss|atom|as)'));
$m->connect('api/statuses/retweeted_by_me.:format',
array('action' => 'ApiTimelineRetweetedByMe',
@ -604,6 +613,18 @@ class Router
// favorites
$m->connect('api/favorites/create.:format',
array('action' => 'ApiFavoriteCreate',
'format' => '(xml|json)'));
$m->connect('api/favorites/destroy.:format',
array('action' => 'ApiFavoriteDestroy',
'format' => '(xml|json)'));
$m->connect('api/favorites/list.:format',
array('action' => 'ApiTimelineFavorites',
'format' => '(xml|json|rss|atom|as)'));
$m->connect('api/favorites/:id.:format',
array('action' => 'ApiTimelineFavorites',
'id' => Nickname::INPUT_FMT,
@ -622,6 +643,7 @@ class Router
array('action' => 'ApiFavoriteDestroy',
'id' => '[0-9]+',
'format' => '(xml|json)'));
// blocks
$m->connect('api/blocks/create/:id.:format',
@ -690,11 +712,11 @@ class Router
$m->connect('api/statusnet/groups/join/:id.:format',
array('action' => 'ApiGroupJoin',
'id' => Nickname::INPUT_FMT,
'format' => '(xml|json)'));
$m->connect('api/statusnet/groups/join.:format',
array('action' => 'ApiGroupJoin',
'id' => Nickname::INPUT_FMT,
'format' => '(xml|json)'));
$m->connect('api/statusnet/groups/leave/:id.:format',
@ -747,6 +769,9 @@ class Router
'format' => '(xml|json|rss|atom|as)'));
// Lists (people tags)
$m->connect('api/lists/list.:format',
array('action' => 'ApiListSubscriptions',
'format' => '(xml|json)'));
$m->connect('api/lists/memberships.:format',
array('action' => 'ApiListMemberships',
@ -824,6 +849,10 @@ class Router
'api/statusnet/media/upload',
array('action' => 'ApiMediaUpload')
);
$m->connect(
'api/statuses/update_with_media.json',
array('action' => 'ApiMediaUpload')
);
// search
$m->connect('api/search.atom', array('action' => 'ApiSearchAtom'));
@ -843,11 +872,10 @@ class Router
$m->connect('panel/site', array('action' => 'siteadminpanel'));
$m->connect('panel/user', array('action' => 'useradminpanel'));
$m->connect('panel/access', array('action' => 'accessadminpanel'));
$m->connect('panel/access', array('action' => 'accessadminpanel'));
$m->connect('panel/paths', array('action' => 'pathsadminpanel'));
$m->connect('panel/sessions', array('action' => 'sessionsadminpanel'));
$m->connect('panel/sitenotice', array('action' => 'sitenoticeadminpanel'));
$m->connect('panel/snapshot', array('action' => 'snapshotadminpanel'));
$m->connect('panel/license', array('action' => 'licenseadminpanel'));
$m->connect('panel/plugins', array('action' => 'pluginsadminpanel'));

View File

@ -1,213 +0,0 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* A snapshot of site stats that can report itself to headquarters
*
* 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 Stats
* @package StatusNet
* @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/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* A snapshot of site stats that can report itself to headquarters
*
* This class will collect statistics on the site and report them to
* a statistics server of the admin's choice. (Default is the big one
* at status.net.)
*
* It can either be called from a cron job, or run occasionally by the
* Web site.
*
* @category Stats
* @package StatusNet
* @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/
*
*/
class Snapshot
{
var $stats = null;
/**
* Constructor for a snapshot
*/
function __construct()
{
}
/**
* Static function for reporting statistics
*
* This function checks whether it should report statistics, based on
* the current configuation settings. If it should, it creates a new
* Snapshot object, takes a snapshot, and reports it to headquarters.
*
* @return void
*/
static function check()
{
switch (common_config('snapshot', 'run')) {
case 'web':
// skip if we're not running on the Web.
if (!isset($_SERVER) || !array_key_exists('REQUEST_METHOD', $_SERVER)) {
break;
}
// Run once every frequency hits
// XXX: do frequency by time (once a week, etc.) rather than
// hits
if (rand() % common_config('snapshot', 'frequency') == 0) {
$snapshot = new Snapshot();
$snapshot->take();
$snapshot->report();
}
break;
case 'cron':
// skip if we're running on the Web
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
break;
}
common_log(LOG_INFO, 'Running snapshot from cron job');
// We're running from the command line; assume
$snapshot = new Snapshot();
$snapshot->take();
common_log(LOG_INFO, count($snapshot->stats) . " statistics being uploaded.");
$snapshot->report();
break;
case 'never':
break;
default:
common_log(LOG_WARNING, "Unrecognized value for snapshot run config.");
}
}
/**
* Take a snapshot of the server
*
* Builds an array of statistical and configuration data based
* on the local database and config files. We avoid grabbing any
* information that could be personal or private.
*
* @return void
*/
function take()
{
$this->stats = array();
// Some basic identification stuff
$this->stats['version'] = GNUSOCIAL_VERSION;
$this->stats['phpversion'] = phpversion();
$this->stats['name'] = common_config('site', 'name');
$this->stats['root'] = common_root_url();
// non-identifying stats on various tables. Primary
// interest is size and rate of activity of service.
$tables = array('user',
'notice',
'subscription',
'user_group');
foreach ($tables as $table) {
$this->tableStats($table);
}
// stats on some important config options
$this->stats['theme'] = common_config('site', 'theme');
$this->stats['dbtype'] = common_config('db', 'type');
$this->stats['xmpp'] = common_config('xmpp', 'enabled');
$this->stats['inboxes'] = common_config('inboxes', 'enabled');
$this->stats['queue'] = common_config('queue', 'enabled');
$this->stats['license'] = common_config('license', 'url');
$this->stats['fancy'] = common_config('site', 'fancy');
$this->stats['private'] = common_config('site', 'private');
$this->stats['closed'] = common_config('site', 'closed');
$this->stats['memcached'] = common_config('memcached', 'enabled');
$this->stats['language'] = common_config('site', 'language');
$this->stats['timezone'] = common_config('site', 'timezone');
}
/**
* Reports statistics to headquarters
*
* Posts statistics to a reporting server.
*
* @return void
*/
function report()
{
// XXX: Use OICU2 and OAuth to make authorized requests
$reporturl = common_config('snapshot', 'reporturl');
try {
$request = HTTPClient::start();
$request->post($reporturl, null, $this->stats);
} catch (Exception $e) {
common_log(LOG_WARNING, "Error in snapshot: " . $e->getMessage());
}
}
/**
* Updates statistics for a single table
*
* Determines the size of a table and its oldest and newest rows.
* Goal here is to see how active a site is. Note that it
* fills up the instance stats variable.
*
* @param string $table name of table to check
*
* @return void
*/
function tableStats($table)
{
$inst = DB_DataObject::factory($table);
$inst->selectAdd();
$inst->selectAdd('count(*) as cnt, '.
'min(created) as first, '.
'max(created) as last');
if ($inst->find(true)) {
$this->stats[$table.'count'] = $inst->cnt;
$this->stats[$table.'first'] = $inst->first;
$this->stats[$table.'last'] = $inst->last;
}
$inst->free();
unset($inst);
}
}

View File

@ -27,9 +27,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* widget for displaying a list of notices

View File

@ -832,7 +832,7 @@ function common_find_mentions_raw($text)
function common_render_text($text)
{
$r = htmlspecialchars($text);
$r = nl2br(htmlspecialchars($text));
$r = preg_replace('/[\x{0}-\x{8}\x{b}-\x{c}\x{e}-\x{19}]/', '', $r);
$r = common_replace_urls_callback($r, 'common_linkify');

View File

@ -2,7 +2,7 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
* List users for autocompletion
* List profiles and groups for autocompletion
*
* PHP version 5
*
@ -29,7 +29,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
exit(1);
}
@ -62,8 +62,8 @@ class AutocompleteAction extends Action
function lastModified()
{
$max=0;
foreach($this->users as $user){
$max = max($max,strtotime($user->modified),strtotime($user->getProfile()->modified));
foreach($this->profiles as $profile){
$max = max($max,strtotime($user->modified),strtotime($profile->modified));
}
foreach($this->groups as $group){
$max = max($max,strtotime($group->modified));
@ -103,19 +103,22 @@ class AutocompleteAction extends Action
}
$this->groups=array();
$this->users=array();
$this->profiles=array();
$term = $this->arg('term');
$limit = $this->arg('limit');
if($limit > 200) $limit=200; //prevent DOS attacks
if(substr($term,0,1)=='@'){
//user search
//profile search
$term=substr($term,1);
$user = new User();
$user->limit($limit);
$user->whereAdd('nickname like \'' . trim($user->escape($term), '\'') . '%\'');
if($user->find()){
while($user->fetch()) {
$this->users[]=clone($user);
$profile = new Profile();
$profile->limit($limit);
$profile->whereAdd('nickname like \'' . trim($profile->escape($term), '\'') . '%\'');
$profile->whereAdd(sprintf('id in (SELECT id FROM user) OR '
. 'id in (SELECT subscribed from subscription'
. ' where subscriber = %d)', $cur->id));
if ($profile->find()) {
while($profile->fetch()) {
$this->profiles[]=clone($profile);
}
}
}
@ -139,8 +142,7 @@ class AutocompleteAction extends Action
parent::handle();
$results = array();
foreach($this->users as $user){
$profile = $user->getProfile();
foreach($this->profiles as $profile){
$avatarUrl = $profile->avatarUrl(AVATAR_MINI_SIZE);
$results[] = array(
'value' => '@'.$profile->nickname,

View File

@ -27,7 +27,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
exit(1);
}
@ -64,10 +64,10 @@ class CometPlugin extends RealtimePlugin
{
$scripts = parent::_getScripts();
$ours = array('jquery.comet.js', 'cometupdate.js');
$ours = array('js/jquery.comet.js', 'js/cometupdate.js');
foreach ($ours as $script) {
$scripts[] = 'plugins/Comet/'.$script;
$scripts[] = $this->path($script);
}
return $scripts;
@ -81,7 +81,7 @@ class CometPlugin extends RealtimePlugin
function _connect()
{
require_once INSTALLDIR.'/plugins/Comet/bayeux.class.inc.php';
require_once INSTALLDIR.'/plugins/Comet/extlib/Bayeux/Bayeux.class.php';
// Bayeux? Comet? Huh? These terms confuse me
$this->bay = new Bayeux($this->server, $this->user, $this->password);
}

View File

@ -52,12 +52,7 @@ class LinkPreviewPlugin extends Plugin
{
$user = common_current_user();
if ($user && common_config('attachments', 'process_links')) {
if (common_config('site', 'minify')) {
$js = 'linkpreview.min.js';
} else {
$js = 'linkpreview.js';
}
$action->script($this->path($js));
$action->script($this->path('js/linkpreview.js'));
$data = json_encode(array(
'api' => common_local_url('oembedproxy'),
'width' => common_config('attachments', 'thumbwidth'),

View File

@ -1 +0,0 @@
(function(){var b={api:"https://noembed.com/embed",width:100,height:75,cache:{},callbacks:{},lookup:function(d,e){if(typeof b.cache[d]=="object"){e(b.cache[d])}else{if(typeof b.callbacks[d]=="undefined"){b.callbacks[d]=[e];b.rawLookup(d,function(h){b.cache[d]=h;var g=b.callbacks[d];b.callbacks[d]=undefined;for(var f=0;f<g.length;f++){g[f](h)}})}else{b.callbacks[d].push(e)}}},rawLookup:function(d,f){var e={url:d,format:"json",maxwidth:b.width,maxheight:b.height,token:$("#token").val()};$.ajax({url:b.api,data:e,dataType:"json",success:function(g,h){f(g)},error:function(h,i,g){f(null)}})}};SN.Init.LinkPreview=function(d){if(d.api){b.api=d.api}if(d.width){b.width=d.width}if(d.height){b.height=d.height}};var c=SN.U.Counter;SN.U.Counter=function(d){var e=d.data("LinkPreview");if(e){e.previewLinks(d.find(".notice_data-text:first").val())}return c(d)};var a=SN.Init.NoticeFormSetup;SN.Init.NoticeFormSetup=function(d){a(d);d.bind("reset",function(){e.clear()});var e={links:[],state:[],refresh:[],findLinks:function(i){var g=/(?:^| )(https?:\/\/.+?\/.+?)(?= |$)/mg;var f=[];var h;while((h=g.exec(i))!==null){f.push(h[1])}return f},ensureArea:function(){if(d.find(".link-preview").length<1){d.append('<div class="notice-status link-preview thumbnails"></div>')}},prepLinkPreview:function(g){var h="link-preview-"+g;var f=e.links[g];e.refresh[g]=false;e.markLoading(g);b.lookup(f,function(l){var i=null;var j=100;if(l&&typeof l.thumbnail_url=="string"){i=l.thumbnail_url;if(typeof l.thumbnail_width!=="undefined"){if(l.thumbnail_width<j){j=l.thumbnail_width}}}else{if(l&&l.type=="photo"&&typeof l.url=="string"){i=l.url;if(typeof l.width!=="undefined"){if(l.width<j){j=l.width}}}}if(i){e.ensureArea();var k=$('<span class="inline-attachment"><a><img/></a></span>');k.find("a").attr("href",f).attr("target","_blank").last().find("img").attr("src",i).attr("width",j).attr("title",l.title||l.url||f);d.find("."+h).empty().append(k)}else{e.clearLink(g)}if(e.refresh[g]){e.prepLinkPreview(g)}else{e.markDone(g)}})},previewLinks:function(j){var h;var f=e.links;var g=e.findLinks(j);e.links=g;for(h=0;h<f.length&&h<g.length;h++){if(g[h]!=f[h]){if(e.state[h]=="loading"){e.refresh[h]=true}else{e.prepLinkPreview(h)}}}if(g.length>f.length){for(h=f.length;h<g.length;h++){e.addPreviewArea(h);e.prepLinkPreview(h)}}else{if(f.length>g.length){for(h=g.length;h<f.length;h++){e.clearLink(h)}}}if(g.length==0){e.clear()}},addPreviewArea:function(f){e.ensureArea();var g="link-preview-"+f;if(d.find("."+g).length<1){d.find(".link-preview").append('<span class="'+g+'"></span>')}},clearLink:function(f){var g="link-preview-"+f;d.find("."+g).html("")},markLoading:function(f){e.state[f]="loading";var g="link-preview-"+f;d.find("."+g).attr("style","opacity: 0.5")},markDone:function(f){e.state[f]="done";var g="link-preview-"+f;d.find("."+g).removeAttr("style")},clear:function(){e.links=[];d.find(".link-preview").remove()}};d.data("LinkPreview",e)}})();

View File

@ -27,7 +27,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
exit(1);
}
@ -96,7 +96,7 @@ class MeteorPlugin extends RealtimePlugin
} else {
$scripts[] = 'http://'.$this->webserver.(($this->webport == 80) ? '':':'.$this->webport).'/meteor.js';
}
$scripts[] = $this->path('meteorupdater.min.js');
$scripts[] = $this->path('js/meteorupdater.js');
return $scripts;
}

View File

@ -1 +0,0 @@
var MeteorUpdater=function(){return{init:function(c,a,b){Meteor.callbacks.process=function(d){RealtimeUpdate.receive(JSON.parse(d))};Meteor.host=c;Meteor.port=a;Meteor.joinChannel(b,0);Meteor.connect()}}}();

View File

@ -332,7 +332,7 @@ class OStatusPlugin extends Plugin
* Find any explicit remote mentions. Accepted forms:
* Webfinger: @user@example.com
* Profile link: @example.com/mublog/user
* @param Profile $sender (os user?)
* @param Profile $sender
* @param string $text input markup text
* @param array &$mention in/out param: set of found mentions
* @return boolean hook return value
@ -354,6 +354,7 @@ class OStatusPlugin extends Plugin
if ($oprofile && !$oprofile->isGroup()) {
$profile = $oprofile->localProfile();
$matches[$pos] = array('mentioned' => array($profile),
'type' => 'mention',
'text' => $target,
'position' => $pos,
'url' => $profile->profileurl);
@ -380,6 +381,7 @@ class OStatusPlugin extends Plugin
if ($oprofile && !$oprofile->isGroup()) {
$profile = $oprofile->localProfile();
$matches[$pos] = array('mentioned' => array($profile),
'type' => 'mention',
'text' => $target,
'position' => $pos,
'url' => $profile->profileurl);

View File

@ -218,7 +218,7 @@ class OStatusInitAction extends Action
}
} else if ($this->group) {
$group = Local_group::getKV('nickname', $this->group);
if ($group) {
if ($group instanceof Local_group) {
return common_local_url('groupbyid', array('id' => $group->group_id));
} else {
// TRANS: Client error.

View File

@ -22,9 +22,7 @@
* @maintainer Brion Vibber <brion@status.net>
*/
if (!defined('STATUSNET')) {
exit(1);
}
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* Key UI methods:
@ -42,16 +40,44 @@ class OStatusSubAction extends Action
protected $profile_uri; // provided acct: or URI of remote entity
protected $oprofile; // Ostatus_profile of remote entity, if valid
protected function prepare(array $args=array())
{
parent::prepare($args);
if (!common_logged_in()) {
// XXX: selfURL() didn't work. :<
common_set_returnto($_SERVER['REQUEST_URI']);
if (Event::handle('RedirectToLogin', array($this, null))) {
common_redirect(common_local_url('login'), 303);
}
return false;
}
if ($this->pullRemoteProfile()) {
$this->validateRemoteProfile();
}
return true;
}
/**
* Handle the submission.
*/
protected function handle()
{
parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePost();
} else {
$this->showForm();
}
}
/**
* Show the initial form, when we haven't yet been given a valid
* remote profile.
*/
function showInputForm()
{
$user = common_current_user();
$profile = $user->getProfile();
$this->elementStart('form', array('method' => 'post',
'id' => 'form_ostatus_sub',
'class' => 'form_settings',
@ -132,8 +158,7 @@ class OStatusSubAction extends Action
$oprofile = $this->oprofile;
$profile = $oprofile->localProfile();
$cur = common_current_user();
if ($cur->isSubscribed($profile)) {
if ($this->scoped->isSubscribed($profile)) {
$this->element('div', array('class' => 'error'),
// TRANS: Extra paragraph in remote profile view when already subscribed.
_m('You are already subscribed to this user.'));
@ -203,8 +228,7 @@ class OStatusSubAction extends Action
*/
function success()
{
$cur = common_current_user();
$url = common_local_url('subscriptions', array('nickname' => $cur->nickname));
$url = common_local_url('subscriptions', array('nickname' => $this->scoped->nickname));
common_redirect($url, 303);
}
@ -288,12 +312,11 @@ class OStatusSubAction extends Action
function saveFeed()
{
// And subscribe the current user to the local profile
$user = common_current_user();
$local = $this->oprofile->localProfile();
if ($user->isSubscribed($local)) {
if ($this->scoped->isSubscribed($local)) {
// TRANS: OStatus remote subscription dialog error.
$this->showForm(_m('Already subscribed!'));
} elseif (Subscription::start($user->getProfile(), $local)) {
} elseif (Subscription::start($this->scoped, $local)) {
$this->success();
} else {
// TRANS: OStatus remote subscription dialog error.
@ -301,38 +324,6 @@ class OStatusSubAction extends Action
}
}
function prepare($args)
{
parent::prepare($args);
if (!common_logged_in()) {
// XXX: selfURL() didn't work. :<
common_set_returnto($_SERVER['REQUEST_URI']);
if (Event::handle('RedirectToLogin', array($this, null))) {
common_redirect(common_local_url('login'), 303);
}
return false;
}
if ($this->pullRemoteProfile()) {
$this->validateRemoteProfile();
}
return true;
}
/**
* Handle the submission.
*/
function handle($args)
{
parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePost();
} else {
$this->showForm();
}
}
/**
* Handle posts to this form
*

View File

@ -52,7 +52,7 @@ class PushCallbackAction extends Action
}
$feedsub = FeedSub::getKV('id', $feedid);
if (!$feedsub) {
if (!$feedsub instanceof FeedSub) {
// TRANS: Server exception. %s is a feed ID.
throw new ServerException(sprintf(_m('Unknown PuSH feed id %s'),$feedid), 400);
}

View File

@ -261,11 +261,16 @@ class FeedSub extends Managed_DataObject
}
/**
* Setting to subscribe means it is _waiting_ to become active. This
* cannot be done in a transaction because there is a chance that the
* remote script we're calling (as in the case of PuSHpress) performs
* the lookup _while_ we're POSTing data, which means the transaction
* never completes (PushcallbackAction gets an 'inactive' state).
*
* @return boolean true on successful sub/unsub, false on failure
*/
protected function doSubscribe($mode)
{
$this->query('BEGIN');
$orig = clone($this);
if ($mode == 'subscribe') {
$this->secret = common_random_hexstr(32);
@ -302,7 +307,6 @@ class FeedSub extends Managed_DataObject
$response = $client->post($hub, $headers, $post);
$status = $response->getStatus();
if ($status == 202) {
$this->query('COMMIT');
common_log(LOG_INFO, __METHOD__ . ': sub req ok, awaiting verification callback');
return true;
} else if ($status >= 200 && $status < 300) {
@ -310,9 +314,7 @@ class FeedSub extends Managed_DataObject
} else {
common_log(LOG_ERR, __METHOD__ . ": sub req failed with HTTP $status: " . $response->getBody());
}
$this->query('ROLLBACK');
} catch (Exception $e) {
$this->query('ROLLBACK');
// wtf!
common_log(LOG_ERR, __METHOD__ . ": error \"{$e->getMessage()}\" hitting hub $this->huburi subscribing to $this->uri");

View File

@ -91,13 +91,15 @@ class Magicsig extends Managed_DataObject
static function getKV($k, $v=null)
{
$obj = parent::getKV($k, $v);
if (!empty($obj)) {
if ($obj instanceof Magicsig) {
// Please note we're replacing the $obj
// FIXME: There should be an import-key that modifies the fetched $obj
$obj = Magicsig::fromString($obj->keypair);
// Double check keys: Crypt_RSA did not
// consistently generate good keypairs.
// We've also moved to 1024 bit keys.
if (strlen($obj->publicKey->modulus->toBits()) != 1024) {
// Never allow less than 1024 bit keys.
// The only case these show up in would be imported or
// legacy very-old-StatusNet generated keypairs.
if (strlen($obj->publicKey->modulus->toBits()) < 1024) {
$obj->delete();
return false;
}
@ -144,11 +146,11 @@ class Magicsig extends Managed_DataObject
*
* @param int $user_id id of local user we're creating a key for
*/
public function generate($user_id)
public function generate($user_id, $bits=1024)
{
$rsa = new Crypt_RSA();
$keypair = $rsa->createKey();
$keypair = $rsa->createKey($bits);
$rsa->loadKey($keypair['privatekey']);

View File

@ -27,7 +27,7 @@
* @link http://laconi.ca/
*/
if (!defined('LACONICA')) {
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) {
exit(1);
}
@ -76,9 +76,9 @@ class OrbitedPlugin extends RealtimePlugin
$root = 'http://'.$server.(($port == 80) ? '':':'.$port);
$scripts[] = $root.'/static/Orbited.js';
$scripts[] = 'plugins/Orbited/orbitedextra.js';
$scripts[] = $this->path('js/orbitedextra.js');
$scripts[] = $root.'/static/protocols/stomp/stomp.js';
$scripts[] = 'plugins/Orbited/orbitedupdater.js';
$scripts[] = $this->path('js/orbitedupdater.js');
return $scripts;
}

View File

@ -65,6 +65,10 @@ class QnAPlugin extends MicroAppPlugin
return true;
}
public function newFormAction() {
return 'qnanewquestion';
}
/**
* Map URLs to actions
*

View File

@ -142,9 +142,9 @@ class RealtimePlugin extends Plugin
public function onEndShowStylesheets(Action $action)
{
$action->cssLink(Plugin::staticPath('Realtime', 'realtimeupdate.css'),
null,
'screen, projection, tv');
$urlpath = self::staticPath(str_replace('Plugin','',__CLASS__),
'css/realtimeupdate.css');
$action->cssLink($urlpath, null, 'screen, projection, tv');
return true;
}
@ -391,12 +391,9 @@ class RealtimePlugin extends Plugin
function _getScripts()
{
if (common_config('site', 'minify')) {
$js = 'realtimeupdate.min.js';
} else {
$js = 'realtimeupdate.js';
}
return array(Plugin::staticPath('Realtime', $js));
$urlpath = self::staticPath(str_replace('Plugin','',__CLASS__),
'js/realtimeupdate.js');
return array($urlpath);
}
/**

File diff suppressed because one or more lines are too long

View File

@ -26,9 +26,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* Takes parameters:
@ -56,7 +54,7 @@ class AddMirrorAction extends BaseMirrorAction
*
* @return boolean success flag
*/
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
$feedurl = $this->getFeedUrl();
@ -71,17 +69,13 @@ class AddMirrorAction extends BaseMirrorAction
switch ($provider) {
case 'feed':
return $this->trimmed('feedurl');
case 'twitter':
$screenie = $this->trimmed('screen_name');
$base = 'http://api.twitter.com/1/statuses/user_timeline.atom?screen_name=';
return $base . urlencode($screenie);
default:
// TRANS: Exception thrown when a feed provider could not be recognised.
throw new Exception(_m('Internal form error: Unrecognized feed provider.'));
}
}
function saveMirror()
protected function saveMirror()
{
if ($this->oprofile->subscribe()) {
SubMirror::saveMirror($this->user, $this->profile);

View File

@ -26,9 +26,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* Takes parameters:
@ -57,7 +55,7 @@ abstract class BaseMirrorAction extends Action
*
* @return boolean success flag
*/
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
return $this->sharedBoilerplate();
@ -101,8 +99,8 @@ abstract class BaseMirrorAction extends Action
$oprofile = Ostatus_profile::ensureFeedURL($url);
}
if ($oprofile->isGroup()) {
// TRANS: Client error displayed when trying to mirror a StatusNet group feed.
$this->clientError(_m('Cannot mirror a StatusNet group at this time.'));
// TRANS: Client error displayed when trying to mirror a GNU social group feed.
$this->clientError(_m('Cannot mirror a GNU social group at this time.'));
}
$this->oprofile = $oprofile; // @todo FIXME: ugly side effect :D
return $oprofile->localProfile();
@ -152,7 +150,7 @@ abstract class BaseMirrorAction extends Action
*
* @return void
*/
function handle($args)
protected function handle()
{
// Throws exception on error
$this->saveMirror();
@ -174,5 +172,5 @@ abstract class BaseMirrorAction extends Action
}
}
abstract function saveMirror();
abstract protected function saveMirror();
}

View File

@ -26,9 +26,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* Takes parameters:
@ -54,7 +52,7 @@ class EditMirrorAction extends BaseMirrorAction
*
* @return boolean success flag
*/
function prepare($args)
protected function prepare(array $args=array())
{
parent::prepare($args);
@ -63,7 +61,7 @@ class EditMirrorAction extends BaseMirrorAction
$this->mirror = SubMirror::pkeyGet(array('subscriber' => $this->user->id,
'subscribed' => $this->profile->id));
if (!$this->mirror) {
if (!$this->mirror instanceof SubMirror) {
// TRANS: Client error displayed when trying to edit an object that is not a feed mirror.
$this->clientError(_m('Requested invalid profile to edit.'));
}
@ -88,7 +86,7 @@ class EditMirrorAction extends BaseMirrorAction
}
}
function saveMirror()
protected function saveMirror()
{
$mirror = SubMirror::getMirror($this->user, $this->profile);
if (!$mirror) {

View File

@ -25,9 +25,7 @@
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
class MirrorSettingsAction extends SettingsAction
{
@ -52,7 +50,7 @@ class MirrorSettingsAction extends SettingsAction
{
// TRANS: Page instructions.
return _m('You can mirror updates from many RSS and Atom feeds ' .
'into your StatusNet timeline!');
'into your GNU social timeline!');
}
/**
@ -103,9 +101,6 @@ class MirrorSettingsAction extends SettingsAction
switch ($this->arg('provider')) {
case 'statusnet':
break;
case 'twitter':
$form = new AddTwitterMirrorForm($this);
break;
case 'wordpress':
break;
case 'linkedin':

View File

@ -1,62 +0,0 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
* 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/>.
*
* @package StatusNet
* @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);
}
class AddTwitterMirrorForm extends AddMirrorForm
{
/**
* Visible or invisible data elements
*
* Display the form fields that make up the data of the form.
* Sub-classes should overload this to show their data.
*
* @return void
*/
function formData()
{
$this->out->hidden('provider', 'twitter');
$this->out->elementStart('fieldset');
$this->out->elementStart('ul');
$this->li();
$this->doInput('addmirror-feedurl',
'screen_name',
// TRANS: Field label.
_m('Twitter username:'),
$this->out->trimmed('screen_name'));
$this->unli();
$this->li();
// TRANS: Button text for adding a Twitter feed mirror.
$this->out->submit('addmirror-save', _m('BUTTON','Add feed'));
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -64,18 +64,10 @@ class AddMirrorWizard extends Widget
// We could accept hostname & username combos here, or
// webfingery combinations as for remote users.
array(
'id' => 'statusnet',
'name' => _m('StatusNet'),
'id' => 'gnusocial',
'name' => _m('GNU social'),
),
*/
// Accepts a Twitter username and pulls their user timeline as a
// public Atom feed. Requires a working alternate hub which, one
// hopes, is getting timely updates.
array(
'id' => 'twitter',
// TRANS: Name for possible feed provider.
'name' => _m('Twitter'),
),
/*
// WordPress was on our list some whiles ago, but not sure
// what we can actually do here. Search on Wordpress.com hosted
@ -85,26 +77,6 @@ class AddMirrorWizard extends Widget
'name' => _m('WordPress'),
),
*/
/*
// In theory, Facebook lets you pull public updates over RSS,
// but the URLs for your own update feed that I can find from
// 2009-era websites no longer seem to work and there's no
// good current documentation. May not still be available...
// Mirroring from an FB account is probably better done with
// the dedicated plugin. (As of March 2011)
array(
'id' => 'facebook',
'name' => _m('Facebook'),
),
*/
/*
// LinkedIn doesn't currently seem to have public feeds
// for users or groups (March 2011)
array(
'id' => 'linkedin',
'name' => _m('LinkedIn'),
),
*/
array(
'id' => 'feed',
// TRANS: Name for possible feed provider.

View File

@ -165,12 +165,6 @@ class TwitterImport
}
}
if (empty($notice->conversation)) {
$conv = Conversation::create();
$notice->conversation = $conv->id;
common_log(LOG_INFO, "No known conversation for status {$statusId} so making a new one {$conv->id}.");
}
$notice->is_local = Notice::GATEWAY;
$notice->content = html_entity_decode($this->linkify($status, FALSE), ENT_QUOTES, 'UTF-8');
@ -180,11 +174,19 @@ class TwitterImport
$id = $notice->insert();
if (!$id) {
if ($id === false) {
common_log_db_error($notice, 'INSERT', __FILE__);
common_log(LOG_ERR, __METHOD__ . ' - Problem saving notice.');
}
if (empty($notice->conversation)) {
$orig = clone($notice);
$conv = Conversation::create($notice);
common_log(LOG_INFO, "No known conversation for status {$statusId} so a new one ({$conv->id}) was created.");
$notice->conversation = $conv->id;
$notice->update($orig);
}
Event::handle('EndNoticeSave', array($notice));
}
@ -519,7 +521,7 @@ class TwitterImport
static function tagLink($tag, $orig)
{
return "<a href='https://search.twitter.com/search?q=%23{$tag}' class='hashtag'>{$orig}</a>";
return "<a href='https://twitter.com/search?q=%23{$tag}' class='hashtag'>{$orig}</a>";
}
static function atLink($screenName, $fullName, $orig)

View File

@ -310,7 +310,7 @@ class TwitterOAuthClient extends OAuthClient
function statusesRetweet($id)
{
$url = "http://api.twitter.com/1.1/statuses/retweet/$id.json";
$url = "https://api.twitter.com/1.1/statuses/retweet/$id.json";
$response = $this->oAuthPost($url);
$status = json_decode($response);
return $status;
@ -326,7 +326,7 @@ class TwitterOAuthClient extends OAuthClient
function favoritesCreate($id)
{
$url = "http://api.twitter.com/1.1/favorites/create.json";
$url = "https://api.twitter.com/1.1/favorites/create.json";
$params=array();
$params['id'] = $id;
$response = $this->oAuthPost($url, $params);
@ -344,7 +344,7 @@ class TwitterOAuthClient extends OAuthClient
function favoritesDestroy($id)
{
$url = "http://api.twitter.com/1.1/favorites/destroy.json";
$url = "https://api.twitter.com/1.1/favorites/destroy.json";
$params=array();
$params['id'] = $id;
$response = $this->oAuthPost($url,$params);
@ -362,7 +362,7 @@ class TwitterOAuthClient extends OAuthClient
function statusesDestroy($id)
{
$url = "http://api.twitter.com/1.1/statuses/destroy/$id.json";
$url = "https://api.twitter.com/1.1/statuses/destroy/$id.json";
$response = $this->oAuthPost($url);
$status = json_decode($response);
return $status;

View File

@ -37,7 +37,9 @@ require_once INSTALLDIR.'/scripts/commandline.inc';
$daemons = array();
#$daemons[] = INSTALLDIR.'/scripts/queuedaemon.php';
if (common_config('queue', 'daemon')) {
$daemons[] = INSTALLDIR.'/scripts/queuedaemon.php';
}
if (Event::handle('GetValidDaemons', array(&$daemons))) {
foreach ($daemons as $daemon) {

View File

@ -1,30 +0,0 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 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/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
$helptext = <<<END_OF_SNAPSHOT_HELP
Batch script for sending snapshot information about this installation to devs.
END_OF_SNAPSHOT_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
Snapshot::check();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 990 B

After

Width:  |  Height:  |  Size: 1.3 KiB