code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 12:17+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/gnu-social/gnu-social/language/zh_TW/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Client error displayed when trying to log in using Twitter while
#. already logged in to StatusNet.
#: actions/twitterlogin.php:57
msgid "Already logged in."
msgstr "已登入"

#. TRANS: Title for login using Twitter page.
#: actions/twitterlogin.php:66
msgctxt "TITLE"
msgid "Twitter Login"
msgstr ""

#. TRANS: Instructions for login using Twitter page.
#: actions/twitterlogin.php:72
msgid "Login with your Twitter account"
msgstr ""

#. TRANS: Alternative text for "sign in with Twitter" image.
#: actions/twitterlogin.php:91
msgid "Sign in with Twitter"
msgstr ""

#. TRANS: Page title for Twitter administration panel.
#: actions/twitteradminpanel.php:53
msgctxt "TITLE"
msgid "Twitter"
msgstr ""

#. TRANS: Instructions for Twitter bridge administration page.
#: actions/twitteradminpanel.php:64
msgid "Twitter bridge settings"
msgstr ""

#. TRANS: Client error displayed when a consumer key is invalid because it is
#. too long.
#: actions/twitteradminpanel.php:153
msgid "Invalid consumer key. Maximum length is 255 characters."
msgstr ""

#. TRANS: Client error displayed when a consumer secret is invalid because it
#. is too long.
#: actions/twitteradminpanel.php:160
msgid "Invalid consumer secret. Maximum length is 255 characters."
msgstr ""

#. TRANS: Fieldset legend for Twitter application settings.
#: actions/twitteradminpanel.php:217
msgid "Twitter application settings"
msgstr ""

#. TRANS: Field label for Twitter assigned consumer key.
#: actions/twitteradminpanel.php:224
msgid "Consumer key"
msgstr ""

#. TRANS: Field title for Twitter assigned consumer key.
#: actions/twitteradminpanel.php:226
msgid "The consumer key assigned by Twitter."
msgstr ""

#. TRANS: Field label for Twitter assigned consumer secret.
#: actions/twitteradminpanel.php:235
msgid "Consumer secret"
msgstr ""

#. TRANS: Field title for Twitter assigned consumer secret.
#: actions/twitteradminpanel.php:237
msgid "The consumer secret assigned by Twitter."
msgstr ""

#. TRANS: Form guide displayed when two required fields have already been
#. provided.
#: actions/twitteradminpanel.php:248
msgid "Note: A global consumer key and secret are set."
msgstr ""

#. TRANS: Field label for Twitter application name.
#: actions/twitteradminpanel.php:256
msgid "Integration source"
msgstr ""

#. TRANS: Field title for Twitter application name.
#: actions/twitteradminpanel.php:258
msgid "The name of your Twitter application."
msgstr ""

#. TRANS: Fieldset legend for Twitter integration options.
#: actions/twitteradminpanel.php:271
msgid "Options"
msgstr ""

#. TRANS: Checkbox label for global setting.
#: actions/twitteradminpanel.php:279
msgid "Enable \"Sign-in with Twitter\""
msgstr ""

#. TRANS: Checkbox title.
#: actions/twitteradminpanel.php:282
msgid "This allow users to login with their Twitter credentials."
msgstr ""

#. TRANS: Checkbox label for global setting.
#: actions/twitteradminpanel.php:290
msgid "Enable Twitter import"
msgstr ""

#. TRANS: Checkbox title for global setting.
#: actions/twitteradminpanel.php:293
msgid ""
"Allow users to import their Twitter friends' timelines. Requires daemons to "
"be manually configured."
msgstr ""

#. TRANS: Button text for saving the administrative Twitter bridge settings.
#. TRANS: Button text for saving Twitter integration settings.
#: actions/twitteradminpanel.php:311 actions/twittersettings.php:210
msgctxt "BUTTON"
msgid "Save"
msgstr ""

#. TRANS: Button title for saving the administrative Twitter bridge settings.
#: actions/twitteradminpanel.php:313
msgid "Save the Twitter bridge settings."
msgstr ""

#. TRANS: Client error displayed when the session token does not match or is
#. not given.
#: actions/twitterauthorization.php:121 actions/twittersettings.php:238
msgid "There was a problem with your session token. Try again, please."
msgstr ""

#. TRANS: Form validation error displayed when the checkbox to agree to the
#. license has not been checked.
#: actions/twitterauthorization.php:128
msgid "You cannot register if you do not agree to the license."
msgstr ""

#. TRANS: Form validation error displayed when an unhandled error occurs.
#: actions/twitterauthorization.php:138
msgid "Something weird happened."
msgstr ""

#. TRANS: Server error displayed when linking to a Twitter account fails.
#: actions/twitterauthorization.php:183 actions/twitterauthorization.php:233
#: actions/twitterauthorization.php:305
msgid "Could not link your Twitter account."
msgstr ""

#. TRANS: Server error displayed when linking to a Twitter account fails
#. because of an incorrect oauth_token.
#: actions/twitterauthorization.php:204
msgid "Could not link your Twitter account: oauth_token mismatch."
msgstr ""

#. TRANS: Page instruction. %s is the StatusNet sitename.
#: actions/twitterauthorization.php:318
#, php-format
msgid ""
"This is the first time you have logged into %s so we must connect your "
"Twitter account to a local account. You can either create a new account, or "
"connect with your existing account, if you have one."
msgstr ""

#. TRANS: Page title.
#: actions/twitterauthorization.php:325
msgid "Twitter Account Setup"
msgstr ""

#. TRANS: Fieldset legend.
#: actions/twitterauthorization.php:359
msgid "Connection options"
msgstr ""

#. TRANS: Fieldset legend.
#: actions/twitterauthorization.php:373
msgid "Create new account"
msgstr ""

#. TRANS: Sub form introduction text.
#: actions/twitterauthorization.php:376
msgid "Create a new user with this nickname."
msgstr ""

#. TRANS: Field label.
#: actions/twitterauthorization.php:384
msgid "New nickname"
msgstr ""

#. TRANS: Field title for nickname field.
#: actions/twitterauthorization.php:387
msgid "1-64 lowercase letters or numbers, no punctuation or spaces."
msgstr ""

#. TRANS: Field label.
#: actions/twitterauthorization.php:391
msgctxt "LABEL"
msgid "Email"
msgstr "電子信箱"

#. TRANS: Field title for e-mail address field.
#: actions/twitterauthorization.php:393
msgid "Used only for updates, announcements, and password recovery"
msgstr ""

#. TRANS: Button text for creating a new StatusNet account in the Twitter
#. connect page.
#: actions/twitterauthorization.php:402
msgctxt "BUTTON"
msgid "Create"
msgstr ""

#. TRANS: Fieldset legend.
#: actions/twitterauthorization.php:409
msgid "Connect existing account"
msgstr ""

#. TRANS: Sub form introduction text.
#: actions/twitterauthorization.php:412
msgid ""
"If you already have an account, login with your username and password to "
"connect it to your Twitter account."
msgstr ""

#. TRANS: Field label.
#: actions/twitterauthorization.php:416
msgid "Existing nickname"
msgstr ""

#. TRANS: Field label.
#: actions/twitterauthorization.php:420
msgid "Password"
msgstr ""

#. TRANS: Fieldset legend.
#: actions/twitterauthorization.php:428
msgid "License"
msgstr ""

#. TRANS: Text for license agreement checkbox.
#. TRANS: %s is the license as configured for the StatusNet site.
#: actions/twitterauthorization.php:439
#, php-format
msgid ""
"My text and files are available under %s except this private data: password,"
" email address, IM address, and phone number."
msgstr ""

#. TRANS: Button text for connecting an existing StatusNet account in the
#. Twitter connect page..
#: actions/twitterauthorization.php:453
msgctxt "BUTTON"
msgid "Connect"
msgstr ""

#. TRANS: Client error displayed when trying to create a new user while
#. creating new users is not allowed.
#: actions/twitterauthorization.php:498 actions/twitterauthorization.php:507
msgid "Registration not allowed."
msgstr ""

#. TRANS: Client error displayed when trying to create a new user with an
#. invalid invitation code.
#: actions/twitterauthorization.php:514
msgid "Not a valid invitation code."
msgstr ""

#. TRANS: Server error displayed when creating a new user has failed.
#: actions/twitterauthorization.php:542
msgid "Error registering user."
msgstr ""

#. TRANS: Server error displayed when connecting a user to a Twitter user has
#. failed.
#. TRANS: Server error displayed connecting a user to a Twitter user has
#. failed.
#: actions/twitterauthorization.php:553 actions/twitterauthorization.php:594
#: actions/twitterauthorization.php:614
msgid "Error connecting user to Twitter."
msgstr ""

#. TRANS: Form validation error displayed when connecting an existing user to
#. a Twitter user fails because
#. TRANS: the provided username and/or password are incorrect.
#: actions/twitterauthorization.php:575
msgid "Invalid username or password."
msgstr "使用者名稱或密碼無效"

#. TRANS: Title for page with Twitter integration settings.
#: actions/twittersettings.php:58
msgid "Twitter settings"
msgstr ""

#. TRANS: Instructions for page with Twitter integration settings.
#: actions/twittersettings.php:70
msgid ""
"Connect your Twitter account to share your updates with your Twitter friends"
" and vice-versa."
msgstr ""

#. TRANS: Fieldset legend.
#: actions/twittersettings.php:119
msgid "Twitter account"
msgstr ""

#. TRANS: Form note when a Twitter account has been connected.
#: actions/twittersettings.php:125
msgid "Connected Twitter account"
msgstr ""

#. TRANS: Fieldset legend.
#: actions/twittersettings.php:131
msgid "Disconnect my account from Twitter"
msgstr ""

#. TRANS: Form guide. %s is a URL to the password settings.
#. TRANS: This message contains a Markdown link in the form
#. [description](link).
#: actions/twittersettings.php:137
#, php-format
msgid ""
"Disconnecting your Twitter account could make it impossible to log in! "
"Please [set a password](%s) first."
msgstr ""

#. TRANS: Form instructions. %1$s is the StatusNet sitename.
#: actions/twittersettings.php:146
#, php-format
msgid ""
"Keep your %1$s account but disconnect from Twitter. You can use your %1$s "
"password to log in."
msgstr ""

#. TRANS: Button text for disconnecting a Twitter account.
#: actions/twittersettings.php:154
msgctxt "BUTTON"
msgid "Disconnect"
msgstr ""

#. TRANS: Fieldset legend.
#: actions/twittersettings.php:162
msgid "Preferences"
msgstr ""

#. TRANS: Checkbox label.
#: actions/twittersettings.php:167
msgid "Automatically send my notices to Twitter."
msgstr ""

#. TRANS: Checkbox label.
#: actions/twittersettings.php:175
msgid "Send local \"@\" replies to Twitter."
msgstr ""

#. TRANS: Checkbox label.
#: actions/twittersettings.php:183
msgid "Subscribe to my Twitter friends here."
msgstr ""

#. TRANS: Checkbox label.
#: actions/twittersettings.php:193
msgid "Import my friends timeline."
msgstr ""

#. TRANS: Button text for adding Twitter integration.
#: actions/twittersettings.php:213
msgctxt "BUTTON"
msgid "Add"
msgstr ""

#. TRANS: Client error displayed when the submitted form contains unexpected
#. data.
#: actions/twittersettings.php:249
msgid "Unexpected form submission."
msgstr ""

#. TRANS: Client error displayed when trying to remove a connected Twitter
#. account when there isn't one connected.
#: actions/twittersettings.php:265
msgid "No Twitter connection to remove."
msgstr ""

#. TRANS: Server error displayed when trying to remove a connected Twitter
#. account fails.
#: actions/twittersettings.php:273
msgid "Could not remove Twitter user."
msgstr ""

#. TRANS: Success message displayed after disconnecting a Twitter account.
#: actions/twittersettings.php:277
msgid "Twitter account disconnected."
msgstr ""

#. TRANS: Server error displayed when saving Twitter integration preferences
#. fails.
#: actions/twittersettings.php:299 actions/twittersettings.php:312
msgid "Could not save Twitter preferences."
msgstr ""

#. TRANS: Success message after saving Twitter integration preferences.
#: actions/twittersettings.php:321
msgid "Twitter preferences saved."
msgstr ""

#. TRANS: Menu item in login navigation.
#. TRANS: Menu item in connection settings navigation.
#: TwitterBridgePlugin.php:154 TwitterBridgePlugin.php:179
msgctxt "MENU"
msgid "Twitter"
msgstr ""

#. TRANS: Title for menu item in login navigation.
#: TwitterBridgePlugin.php:156
msgid "Login or register using Twitter."
msgstr ""

#. TRANS: Title for menu item in connection settings navigation.
#: TwitterBridgePlugin.php:181
msgid "Twitter integration options"
msgstr ""

#. TRANS: Menu item in administrative panel that leads to the Twitter bridge
#. configuration.
#: TwitterBridgePlugin.php:282
msgid "Twitter"
msgstr ""

#. TRANS: Menu item title in administrative panel that leads to the Twitter
#. bridge configuration.
#: TwitterBridgePlugin.php:284
msgid "Twitter bridge configuration page."
msgstr ""

#. TRANS: Plugin description.
#: TwitterBridgePlugin.php:308
msgid ""
"The Twitter \"bridge\" plugin allows integration of a StatusNet instance "
"with Twitter."
msgstr ""

#. TRANS: Message used to repeat a notice. RT is the abbreviation of
#. 'retweet'.
#. TRANS: %1$s is the repeated user's name, %2$s is the repeated notice.
#: lib/twitterimport.php:115
#, php-format
msgid "RT @%1$s %2$s"
msgstr ""

#. TRANS: Server exception thrown when an invalid URL scheme is detected.
#: lib/jsonstreamreader.php:67
msgid "Invalid URL scheme for HTTP stream reader."
msgstr ""

#. TRANS: Exception thrown when input from an inexpected socket is
#. encountered.
#: lib/jsonstreamreader.php:185
msgid "Got input from unexpected socket!"
msgstr ""

#. TRANS: Exception thrown when an invalid state is encountered in handleLine.
#. TRANS: %s is the invalid state.
#: lib/jsonstreamreader.php:217
#, php-format
msgid "Invalid state in handleLine: %s."
msgstr ""

#. TRANS: Exception thrown when an invalid response line is encountered.
#. TRANS: %s is the invalid line.
#: lib/jsonstreamreader.php:231
#, php-format
msgid "Invalid HTTP response line: %s."
msgstr ""

#. TRANS: Exception thrown when an invalid response line part is encountered.
#. TRANS: %1$s is the chunk, %2$s is the line.
#: lib/jsonstreamreader.php:238
#, php-format
msgid "Invalid HTTP response line chunk \"%1$s\": %2$s."
msgstr ""

#. TRANS: Exception thrown when an invalid response code is encountered.
#. TRANS: %1$s is the response code, %2$s is the line.
#: lib/jsonstreamreader.php:243
#, php-format
msgid "Bad HTTP response code %1$s: %2$s."
msgstr ""

#. TRANS: Mail subject after forwarding notices to Twitter has stopped
#. working.
#: twitter.php:439
msgid "Your Twitter bridge has been disabled"
msgstr ""

#. TRANS: Mail body after forwarding notices to Twitter has stopped working.
#. TRANS: %1$ is the name of the user the mail is sent to, %2$s is a URL to
#. the
#. TRANS: Twitter settings, %3$s is the StatusNet sitename.
#: twitter.php:446
#, php-format
msgid ""
"Hi, %1$s. We're sorry to inform you that your link to Twitter has been disabled. We no longer seem to have permission to update your Twitter status. Did you maybe revoke %3$s's access?\n"
"\n"
"You can re-enable your Twitter bridge by visiting your Twitter settings page:\n"
"\n"
"\t%2$s\n"
"\n"
"Regards,\n"
"%3$s"
msgstr ""


code:
<?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/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Brion Vibber <brion@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/
 */

define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));

$shortoptions = 'n:';
$longoptions = array('nick=','import','all');

$helptext = <<<ENDOFHELP
USAGE: fakestream.php -n <username>

  -n --nick=<username> Local user whose Twitter timeline to watch
     --import          Experimental: run incoming messages through import
     --all             Experimental: run multiuser; requires nick be the app owner

Attempts a User Stream connection to Twitter as the given user, dumping
data as it comes.

ENDOFHELP;

require_once INSTALLDIR.'/scripts/commandline.inc';

if (have_option('n')) {
    $nickname = get_option_value('n');
} else if (have_option('nick')) {
    $nickname = get_option_value('nickname');
} else if (have_option('all')) {
    $nickname = null;
} else {
    show_help($helptext);
    exit(0);
}

/**
 *
 * @param User $user
 * @return TwitterOAuthClient
 */
function twitterAuthForUser(User $user)
{
    $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
    $token = TwitterOAuthClient::unpackToken($flink->credentials);
    if (!$token) {
        throw new ServerException("No Twitter OAuth credentials for this user.");
    }

    return new TwitterOAuthClient($token->key, $token->secret);
}

/**
 * Emulate the line-by-line output...
 *
 * @param Foreign_link $flink
 * @param mixed $data
 */
function dumpMessage($flink, $data)
{
    $msg = prepMessage($flink, $data);
    print json_encode($msg) . "\r\n";
}

function prepMessage($flink, $data)
{
    $msg->for_user = $flink->foreign_id;
    $msg->message = $data;
    return $msg;
}

if (have_option('all')) {
    $users = array();

    $flink = new Foreign_link();
    $flink->service = TWITTER_SERVICE;
    $flink->find();

    while ($flink->fetch()) {
        if (($flink->noticesync & FOREIGN_NOTICE_RECV) ==
            FOREIGN_NOTICE_RECV) {
            $users[] = $flink->user_id;
        }
    }
} else {
    $user = User::getKV('nickname', $nickname);
    $users = array($user->id);
}

$output = array();
foreach ($users as $id) {
    $user = User::getKV('id', $id);
    if (!$user) {
        throw new Exception("No user for id $id");
    }
    $auth = twitterAuthForUser($user);
    $flink = Foreign_link::getByUserID($user->id,
                                       TWITTER_SERVICE);

    $friends->friends = $auth->friendsIds();
    dumpMessage($flink, $friends);

    $timeline = $auth->statusesHomeTimeline();
    foreach ($timeline as $status) {
        $output[] = prepMessage($flink, $status);
    }
}

usort($output, function($a, $b) {
    if ($a->message->id < $b->message->id) {
        return -1;
    } else if ($a->message->id == $b->message->id) {
        return 0;
    } else {
        return 1;
    }
});

foreach ($output as $msg) {
    print json_encode($msg) . "\r\n";
}

license: mit


code:
#!/usr/bin/env php
<?php
/*
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010, 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 = <<<ENDOFHELP
USAGE: initialize_notice_to_status.php

Initializes the notice_to_status table with existing Twitter synch
data. Only necessary if you've had the Twitter bridge enabled before
version 0.9.5.

ENDOFHELP;

require_once INSTALLDIR.'/scripts/commandline.inc';

// We update any notices that may have come in from
// Twitter that we don't have a status_id for. Note that
// this won't catch notices that originated at this StatusNet site.

$n = new Notice();

$n->query('SELECT notice.id, notice.uri ' .
          'FROM notice LEFT JOIN notice_to_status ' .
          'ON notice.id = notice_to_status.notice_id ' .
          'WHERE notice.source = "twitter"' .
          'AND notice_to_status.status_id IS NULL');

while ($n->fetch()) {
    if (preg_match('/^http://twitter.com(/#!)?/[\w_.]+/status/(\d+)$/', $n->uri, $match)) {
        $status_id = $match[1];
        Notice_to_status::saveNew($n->id, $status_id);
    }
}


code:
<?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/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @author    Brion Vibber <brion@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/
 */

define('INSTALLDIR', realpath(dirname(__FILE__) . '/../../..'));

$shortoptions = 'n:';
$longoptions = array('nick=','import','all','apiroot=');

$helptext = <<<ENDOFHELP
USAGE: streamtest.php -n <username>

  -n --nick=<username> Local user whose Twitter timeline to watch
     --import          Experimental: run incoming messages through import
     --all             Experimental: run multiuser; requires nick be the app owner
     --apiroot=<url>   Provide alternate streaming API root URL

Attempts a User Stream connection to Twitter as the given user, dumping
data as it comes.

ENDOFHELP;

require_once INSTALLDIR.'/scripts/commandline.inc';
require_once dirname(dirname(__FILE__)) . '/lib/jsonstreamreader.php';
require_once dirname(dirname(__FILE__)) . '/lib/twitterstreamreader.php';

if (have_option('n')) {
    $nickname = get_option_value('n');
} else if (have_option('nick')) {
    $nickname = get_option_value('nickname');
} else {
    show_help($helptext);
    exit(0);
}

/**
 *
 * @param User $user
 * @return TwitterOAuthClient
 */
function twitterAuthForUser(User $user)
{
    $flink = Foreign_link::getByUserID($user->id, TWITTER_SERVICE);
    $token = TwitterOAuthClient::unpackToken($flink->credentials);
    if (!$token) {
        throw new ServerException("No Twitter OAuth credentials for this user.");
    }

    return new TwitterOAuthClient($token->key, $token->secret);
}

function homeStreamForUser(User $user)
{
    $auth = twitterAuthForUser($user);
    return new TwitterUserStream($auth);
}

function siteStreamForOwner(User $user)
{
    // The user we auth as must be the owner of the application.
    $auth = twitterAuthForUser($user);

    if (have_option('apiroot')) {
        $stream = new TwitterSiteStream($auth, get_option_value('apiroot'));
    } else {
        $stream = new TwitterSiteStream($auth);
    }

    // Pull Twitter user IDs for all users we want to pull data for
    $userIds = array();

    $flink = new Foreign_link();
    $flink->service = TWITTER_SERVICE;
    $flink->find();

    while ($flink->fetch()) {
        if (($flink->noticesync & FOREIGN_NOTICE_RECV) ==
            FOREIGN_NOTICE_RECV) {
            $userIds[] = $flink->foreign_id;
        }
    }

    $stream->followUsers($userIds);
    return $stream;
}

$user = User::getKV('nickname', $nickname);
global $myuser;
$myuser = $user;

if (have_option('all')) {
    $stream = siteStreamForOwner($user);
} else {
    $stream = homeStreamForUser($user);
}

$stream->hookEvent('raw', function($data, $context) {
    common_log(LOG_INFO, json_encode($data) . ' for ' . json_encode($context));
});
$stream->hookEvent('friends', function($data, $context) {
    printf("Friend list: %s\n", implode(', ', $data->friends));
});
$stream->hookEvent('favorite', function($data, $context) {
    printf("%s favorited %s's notice: %s\n",
            $data->source->screen_name,
            $data->target->screen_name,
            $data->target_object->text);
});
$stream->hookEvent('unfavorite', function($data, $context) {
    printf("%s unfavorited %s's notice: %s\n",
            $data->source->screen_name,
            $data->target->screen_name,
            $data->target_object->text);
});
$stream->hookEvent('follow', function($data, $context) {
    printf("%s friended %s\n",
            $data->source->screen_name,
            $data->target->screen_name);
});
$stream->hookEvent('unfollow', function($data, $context) {
    printf("%s unfriended %s\n",
            $data->source->screen_name,
            $data->target->screen_name);
});
$stream->hookEvent('delete', function($data, $context) {
    printf("Deleted status notification: %s\n",
            $data->status->id);
});
$stream->hookEvent('scrub_geo', function($data, $context) {
    printf("Req to scrub geo data for user id %s up to status ID %s\n",
            $data->user_id,
            $data->up_to_status_id);
});
$stream->hookEvent('status', function($data, $context) {
    printf("Received status update from %s: %s\n",
            $data->user->screen_name,
            $data->text);

    if (have_option('import')) {
        $importer = new TwitterImport();
        printf("\timporting...");
        $notice = $importer->importStatus($data);
        if (!$notice instanceof Notice) {
            printf(" FAIL\n");
        }
    }
});
$stream->hookEvent('direct_message', function($data) {
    printf("Direct message from %s to %s: %s\n",
            $data->sender->screen_name,
            $data->recipient->screen_name,
            $data->text);
});

class TwitterManager extends IoManager
{
    function __construct(TwitterStreamReader $stream)
    {
        $this->stream = $stream;
    }

    function getSockets()
    {
        return $this->stream->getSockets();
    }

    function handleInput($data)
    {
        $this->stream->handleInput($data);
        return true;
    }

    function start()
    {
        $this->stream->connect();
        return true;
    }

    function finish()
    {
        $this->stream->close();
        return true;
    }

    public static function get()
    {
        throw new Exception('not a singleton');
    }
}

class TwitterStreamMaster extends IoMaster
{
    function __construct($id, $ioManager)
    {
        parent::__construct($id);
        $this->ioManager = $ioManager;
    }

    /**
     * Initialize IoManagers which are appropriate to this instance.
     */
    function initManagers()
    {
        $this->instantiate($this->ioManager);
    }
}

$master = new TwitterStreamMaster('TwitterStream', new TwitterManager($stream));
$master->init();
$master->service();


code:
<?php
/*
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2010, StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }

require_once __DIR__ . '/twitter.php';

/**
 * Queue handler to deal with incoming Twitter status updates, as retrieved by
 * TwitterDaemon (twitterdaemon.php).
 *
 * The queue handler passes the status through TwitterImporter for import into the
 * local database (if necessary), then adds the imported notice to the local inbox
 * of the attached Twitter user.
 *
 * Warning: the way we do inbox distribution manually means that realtime, XMPP, etc
 * don't work on Twitter-borne messages. When TwitterImporter is changed to handle
 * that correctly, we'll only need to do this once...?
 */
class TweetCtlQueueHandler extends QueueHandler
{
    function transport()
    {
        return 'tweetctl';
    }

    function handle($data) : bool
    {
        // A user has activated or deactivated their Twitter bridge
        // import status.
        $action = $data['action'];
        $userId = $data['for_user'];

        $tm = TwitterManager::get();
        if ($action == 'start') {
            $tm->startTwitterUser($userId);
        } else if ($action == 'stop') {
            $tm->stopTwitterUser($userId);
        }

        return true;
    }
}


code:
<?php
/*
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2008-2011 StatusNet, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

if (!defined('STATUSNET') && !defined('LACONICA')) {
    exit(1);
}

define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1

function add_twitter_user($twitter_id, $screen_name)
{
    // Clear out any bad old foreign_users with the new user's legit URL
    // This can happen when users move around or fakester accounts get
    // repoed, and things like that.
    try {
        $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);
        $result = $fuser->delete();
        if ($result != false) {
            common_log(
                LOG_INFO,
                "Twitter bridge - removed old Twitter user: $screen_name ($twitter_id)."
            );
        }
    } catch (NoResultException $e) {
        // no old foreign users exist for this id
    }

    $fuser = new Foreign_user();

    $fuser->nickname = $screen_name;
    $fuser->uri = 'http://twitter.com/' . $screen_name;
    $fuser->id = $twitter_id;
    $fuser->service = TWITTER_SERVICE;
    $fuser->created = common_sql_now();
    $result = $fuser->insert();

    if ($result === false) {
        common_log(LOG_WARNING, "Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name.");
        common_log_db_error($fuser, 'INSERT', __FILE__);
    } else {
        common_log(LOG_INFO,
                   "Twitter bridge - Added new Twitter user: $screen_name ($twitter_id).");
    }

    return $result;
}

// Creates or Updates a Twitter user
function save_twitter_user($twitter_id, $screen_name)
{
    // Check to see whether the Twitter user is already in the system,
    // and update its screen name and uri if so.
    try {
        $fuser = Foreign_user::getForeignUser($twitter_id, TWITTER_SERVICE);

        // Delete old record if Twitter user changed screen name
        if ($fuser->nickname != $screen_name) {
            $oldname = $fuser->nickname;
            $fuser->delete();
            common_log(LOG_INFO, sprintf('Twitter bridge - Updated nickname (and URI) ' .
                                         'for Twitter user %1$d - %2$s, was %3$s.',
                                         $fuser->id,
                                         $screen_name,
                                         $oldname));
        }
    } catch (NoResultException $e) {
        // No old users exist for this id

        // Kill any old, invalid records for this screen name
        // XXX: Is this really only supposed to be run if the above getForeignUser fails?
        try {
            $fuser = Foreign_user::getByNickname($screen_name, TWITTER_SERVICE);
            $fuser->delete();
            common_log(
                LOG_INFO,
                sprintf(
                    'Twitter bridge - deteted old record for Twitter ' .
                    'screen name "%s" belonging to Twitter ID %d.',
                    $screen_name,
                    $fuser->id
                )
            );
        } catch (NoResultException $e) {
            // No old users exist for this screen_name
        }
    }

    return add_twitter_user($twitter_id, $screen_name);
}

function is_twitter_bound($notice, $flink) {

    // Don't send activity activities (at least for now)
    if ($notice->object_type == ActivityObject::ACTIVITY) {
        return false;
    }

    $allowedVerbs = array(ActivityVerb::POST);

    // Default behavior: always send repeats
    if (empty($flink))
        array_push($allowedVerbs, ActivityVerb::SHARE);
    // Otherwise, check to see if repeats are allowed
    else if (($flink->noticesync & FOREIGN_NOTICE_SEND_REPEAT) == FOREIGN_NOTICE_SEND_REPEAT)
        array_push($allowedVerbs, ActivityVerb::SHARE);

    // Don't send things that aren't posts or repeats (at least for now)
    if (!in_array($notice->verb, $allowedVerbs)) {
        return false;
    }

    // Check to see if notice should go to Twitter
    if (!empty($flink) && (($flink->noticesync & FOREIGN_NOTICE_SEND) == FOREIGN_NOTICE_SEND)) {

        // If it's not a Twitter-style reply, or if the user WANTS to send replies,
        // or if it's in reply to a twitter notice
        if ( (($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) == FOREIGN_NOTICE_SEND_REPLY) ||
               is_twitter_notice($notice->reply_to) || is_twitter_notice($notice->repeat_of) ||
             (empty($notice->reply_to) && !preg_match('/^@[a-zA-Z0-9_]{1,15}\b/u', $notice->content)) ){
            return true;
        }
    }

    return false;
}

function is_twitter_notice($id)
{
    $n2s = Notice_to_status::getKV('notice_id', $id);

    return (!empty($n2s));
}

/**
 * Pull the formatted status ID number from a Twitter status object
 * returned via JSON from Twitter API.
 *
 * Encapsulates checking for the id_str attribute, which is required
 * to read 64-bit "Snowflake" ID numbers on a 32-bit system -- the
 * integer id attribute gets corrupted into a double-precision float,
 * losing a few digits of precision.
 *
 * Warning: avoid performing arithmetic or direct comparisons with
 * this number, as it may get coerced back to a double on 32-bit.
 *
 * @param object $status
 * @param string $field base field name if not 'id'
 * @return mixed id number as int or string
 */
function twitter_id($status, $field='id')
{
    $field_str = "{$field}_str";
    if (isset($status->$field_str)) {
        // String version of the id -- required on 32-bit systems
        // since the 64-bit numbers get corrupted as ints.
        return $status->$field_str;
    } else {
        return $status->$field;
    }
}

/**
 * Check if we need to broadcast a notice over the Twitter bridge, and
 * do so if necessary. Will determine whether to do a straight post or
 * a repeat/retweet
 *
 * This function is meant to be called directly from TwitterQueueHandler.
 *
 * @param Notice $notice
 * @return boolean true if complete or successful, false if we should retry
 */
function broadcast_twitter($notice)
{
    try {
        $flink = Foreign_link::getByUserID($notice->profile_id, TWITTER_SERVICE);
    } catch (NoResultException $e) {
        // Alright so don't broadcast it then! (since there's no foreign link)
        return true;
    }

    // Don't bother with basic auth, since it's no longer allowed
    if (TwitterOAuthClient::isPackedToken($flink->credentials)) {
        if (is_twitter_bound($notice, $flink)) {
            if (!empty($notice->repeat_of) && is_twitter_notice($notice->repeat_of)) {
                $retweet = retweet_notice($flink, Notice::getKV('id', $notice->repeat_of));
                if (is_object($retweet)) {
                    Notice_to_status::saveNew($notice->id, twitter_id($retweet));
                    return true;
                } else {
                    // Our error processing will have decided if we need to requeue
                    // this or can discard safely.
                    return $retweet;
                }
            } else {
                return broadcast_oauth($notice, $flink);
            }
        }
    }

    return true;
}

/**
 * Send a retweet to Twitter for a notice that has been previously bridged
 * in or out.
 *
 * Warning: the return value is not guaranteed to be an object; some error
 * conditions will return a 'true' which should be passed on to a calling
 * queue handler.
 *
 * No local information about the resulting retweet is saved: it's up to
 * caller to save new mappings etc if appropriate.
 *
 * @param Foreign_link $flink
 * @param Notice $notice
 * @return mixed object with resulting Twitter status data on success, or true/false/null on error conditions.
 */
function retweet_notice($flink, $notice)
{
    $token = TwitterOAuthClient::unpackToken($flink->credentials);
    $client = new TwitterOAuthClient($token->key, $token->secret);

    $id = twitter_status_id($notice);

    if (empty($id)) {
        common_log(LOG_WARNING, "Trying to retweet notice {$notice->id} with no known status id.");
        return null;
    }

    try {
        $status = $client->statusesRetweet($id);
        return $status;
    } catch (OAuthClientException $e) {
        return process_error($e, $flink, $notice);
    }
}

function twitter_status_id($notice)
{
    $n2s = Notice_to_status::getKV('notice_id', $notice->id);
    if (empty($n2s)) {
        return null;
    } else {
        return $n2s->status_id;
    }
}

/**
 * Pull any extra information from a notice that we should transfer over
 * to Twitter beyond the notice text itself.
 *
 * @param Notice $notice
 * @return array of key-value pairs for Twitter update submission
 * @access private
 */
function twitter_update_params($notice)
{
    $params = array();
    if ($notice->lat || $notice->lon) {
        $params['lat'] = $notice->lat;
        $params['long'] = $notice->lon;
    }
    if (!empty($notice->reply_to) && is_twitter_notice($notice->reply_to)) {
        $reply = Notice::getKV('id', $notice->reply_to);
        $params['in_reply_to_status_id'] = twitter_status_id($reply);
    }
    return $params;
}

function broadcast_oauth($notice, Foreign_link $flink) {
    try {
        $user = $flink->getUser();
    } catch (ServerException $e) {
        common_log(LOG_WARNING, 'Discarding broadcast_oauth for notice '.$notice->id.' because of exception: '.$e->getMessage());
        return true;
    }
    $statustxt = format_status($notice);
    $params = twitter_update_params($notice);

    $token = TwitterOAuthClient::unpackToken($flink->credentials);
    $client = new TwitterOAuthClient($token->key, $token->secret);
    $status = null;

    try {
        $status = $client->statusesUpdate($statustxt, $params);
        if (!empty($status)) {
            Notice_to_status::saveNew($notice->id, twitter_id($status));
        }
    } catch (OAuthClientException $e) {
        return process_error($e, $flink, $notice);
    }

    if (empty($status)) {
        // This could represent a failure posting,
        // or the Twitter API might just be behaving flakey.
        $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' .
                          'trying to post notice %d for User %s (user id %d).',
                          $notice->id,
                          $user->nickname,
                          $user->id);

        common_log(LOG_WARNING, $errmsg);

        return false;
    }

    // Notice crossed the great divide
    $msg = sprintf('Twitter bridge - posted notice %d to Twitter using ' .
                   'OAuth for User %s (user id %d).',
                   $notice->id,
                   $user->nickname,
                   $user->id);

    common_log(LOG_INFO, $msg);

    return true;
}

function process_error($e, $flink, $notice)
{
    $user = $flink->getUser();
    $code = $e->getCode();

    $logmsg = sprintf('Twitter bridge - %d posting notice %d for ' .
                      'User %s (user id: %d): %s.',
                      $code,
                      $notice->id,
                      $user->nickname,
                      $user->id,
                      $e->getMessage());

    common_log(LOG_WARNING, $logmsg);

    // http://dev.twitter.com/pages/responses_errors
    switch($code) {
     case 400:
         // Probably invalid data (bad Unicode chars or coords) that
         // cannot be resolved by just sending again.
         //
         // It could also be rate limiting, but retrying immediately
         // won't help much with that, so we'll discard for now.
         // If a facility for retrying things later comes up in future,
         // we can detect the rate-limiting headers and use that.
         //
         // Discard the message permanently.
         return true;
         break;
     case 401:
        // Probably a revoked or otherwise bad access token - nuke!
        remove_twitter_link($flink);
        return true;
        break;
     case 403:
        // User has exceeder her rate limit -- toss the notice
        return true;
        break;
     case 404:
         // Resource not found. Shouldn't happen much on posting,
         // but just in case!
         //
         // Consider it a matter for tossing the notice.
         return true;
         break;
     default:

        // For every other case, it's probably some flakiness so try
        // sending the notice again later (requeue).

        return false;
        break;
    }
}

function format_status($notice)
{
    // Start with the plaintext source of this notice...
    $statustxt = $notice->content;

    // Convert !groups to #hashes
    // XXX: Make this an optional setting?
    $statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt);

    // detect links, each link uses 23 characters on twitter
    $numberOfLinks = preg_match_all('`((http|https|ftp)://[^\s<]+[^\s<\.)])`i', $statustxt);
    $statusWithoutLinks = preg_replace('`((http|https|ftp)://[^\s<]+[^\s<\.)])`i', '', $statustxt);
    $statusLength = mb_strlen($statusWithoutLinks)  + $numberOfLinks * 23;

    // Twitter raised it but still has a 280-char hardcoded max.
    if ($statusLength > 280) {
        $noticeUrl = common_shorten_url($notice->getUrl());
        // each link uses 23 chars on twitter + 3 for the ' … ' => 26
        $statustxt = mb_substr($statustxt, 0, 280 - 26) . ' … ' . $noticeUrl;
    }

    return $statustxt;
}

function remove_twitter_link($flink)
{
    $user = $flink->getUser();

    common_log(LOG_INFO, 'Removing Twitter bridge Foreign link for ' .
               "user $user->nickname (user id: $user->id).");

    $result = $flink->safeDelete();

    if (empty($result)) {
        common_log(LOG_ERR, 'Could not remove Twitter bridge ' .
                   "Foreign_link for $user->nickname (user id: $user->id)!");
        common_log_db_error($flink, 'DELETE', __FILE__);
    }

    // Notify the user that her Twitter bridge is down

    if (isset($user->email)) {
        $result = mail_twitter_bridge_removed($user);

        if (!$result) {
            $msg = 'Unable to send email to notify ' .
              "$user->nickname (user id: $user->id) " .
              'that their Twitter bridge link was ' .
              'removed!';

            common_log(LOG_WARNING, $msg);
        }
    }
}

/**
 * Send a mail message to notify a user that her Twitter bridge link
 * has stopped working, and therefore has been removed.  This can
 * happen when the user changes her Twitter password, or otherwise
 * revokes access.
 *
 * @param User $user   user whose Twitter bridge link has been removed
 *
 * @return boolean success flag
 */
function mail_twitter_bridge_removed($user)
{
    $profile = $user->getProfile();

    common_switch_locale($user->language);

    // TRANS: Mail subject after forwarding notices to Twitter has stopped working.
    $subject = sprintf(_m('Your Twitter bridge has been disabled'));

    $site_name = common_config('site', 'name');

    // TRANS: Mail body after forwarding notices to Twitter has stopped working.
    // TRANS: %1$ is the name of the user the mail is sent to, %2$s is a URL to the
    // TRANS: Twitter settings, %3$s is the StatusNet sitename.
    $body = sprintf(_m('Hi, %1$s. We\'re sorry to inform you that your ' .
        'link to Twitter has been disabled. We no longer seem to have ' .
    'permission to update your Twitter status. Did you maybe revoke ' .
    '%3$s\'s access?' . "\n\n" .
    'You can re-enable your Twitter bridge by visiting your ' .
    "Twitter settings page:\n\n\t%2\$s\n\n" .
        "Regards,\n%3\$s"),
        $profile->getBestName(),
        common_local_url('twittersettings'),
        common_config('site', 'name'));

    common_switch_locale();
    return mail_to_user($user, $subject, $body);
}


code:
The UserFlag plugin allows flagging of profiles for review and reviewing
flagged profiles.

Installation
============
add "addPlugin('UserFlag');"
to the bottom of your config.php

Settings
========
flagOnBlock: Whether to automatically flag a profile when a user blocks it.

Example
=======
addPlugin('UserFlag', array(
    'flagOnBlock' => true
));

path: plugins/UserFlag/README


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Allows users to flag content and accounts as offensive/spam/whatever
 *
 * PHP version 5
 *
 * LICENCE: This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @category  Plugin
 * @package   StatusNet
 * @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')) {
    exit(1);
}

/**
 * Allows users to flag content and accounts as offensive/spam/whatever
 *
 * @category Plugin
 * @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 UserFlagPlugin extends Plugin
{
    const PLUGIN_VERSION = '2.0.0';

    const REVIEWFLAGS = 'UserFlagPlugin::reviewflags';
    const CLEARFLAGS  = 'UserFlagPlugin::clearflags';

    public $flagOnBlock = true;

    /**
     * Hook for ensuring our tables are created
     *
     * Ensures that the user_flag_profile table exists
     * and has the right columns.
     *
     * @return boolean hook return
     */
    function onCheckSchema()
    {
        $schema = Schema::get();

        // For storing user-submitted flags on profiles
        $schema->ensureTable('user_flag_profile', User_flag_profile::schemaDef());
        return true;
    }

    /**
     * Add our actions to the URL router
     *
     * @param URLMapper $m URL mapper for this hit
     *
     * @return boolean hook return
     */
    public function onRouterInitialized(URLMapper $m)
    {
        $m->connect('main/flag/profile', ['action' => 'flagprofile']);
        $m->connect('main/flag/clear', ['action' => 'clearflag']);
        $m->connect('panel/profile/flag', ['action' => 'adminprofileflag']);
        return true;
    }

    /**
     * Add a 'flag' button to profile page
     *
     * @param Action  $action The action being called
     * @param Profile $profile Profile being shown
     *
     * @return boolean hook result
     */
    function onEndProfilePageActionsElements($action, $profile)
    {
        $this->showFlagButton($action, $profile,
                              array('action' => 'showstream',
                                    'nickname' => $profile->nickname));

        return true;
    }

    /**
     * Add a 'flag' button to profiles in a list
     *
     * @param ProfileListItem $item item being shown
     *
     * @return boolean hook result
     */
    function onEndProfileListItemActionElements($item)
    {
        list($action, $args) = $item->action->returnToArgs();
        $args['action'] = $action;
        $this->showFlagButton($item->action, $item->profile, $args);

        return true;
    }

    /**
     * Actually output a flag button. If the target profile has already been
     * flagged by the current user, a null-action faux button is shown.
     *
     * @param Action $action
     * @param Profile $profile
     * @param array $returnToArgs
     */
    protected function showFlagButton($action, $profile, $returnToArgs)
    {
        $user = common_current_user();

        if (!empty($user) && ($user->id != $profile->id)) {

            $action->elementStart('li', 'entity_flag');

            if (User_flag_profile::exists($profile->id, $user->id)) {
                // @todo FIXME: Add a title explaining what 'flagged' means?
                // TRANS: Message added to a profile if it has been flagged for review.
                $action->element('p', 'flagged', _m('Flagged'));
            } else {
                $form = new FlagProfileForm($action, $profile, $returnToArgs);
                $form->show();
            }

            $action->elementEnd('li');
        }
    }

    /**
     * Check whether a user has one of our defined rights
     *
     * We define extra rights; this function checks to see if a
     * user has one of them.
     *
     * @param User    $user    User being checked
     * @param string  $right   Right we're checking
     * @param boolean &$result out, result of the check
     *
     * @return boolean hook result
     */
    function onUserRightsCheck($user, $right, &$result)
    {
        switch ($right) {
        case self::REVIEWFLAGS:
        case self::CLEARFLAGS:
            $result = $user->hasRole('moderator');
            return false; // done processing!
        }

        return true; // unchanged!
    }

    /**
     * Optionally flag profile when a block happens
     *
     * We optionally add a flag when a profile has been blocked
     *
     * @param User    $user    User doing the block
     * @param Profile $profile Profile being blocked
     *
     * @return boolean hook result
     */
    function onEndBlockProfile($user, $profile)
    {
        if ($this->flagOnBlock && !User_flag_profile::exists($profile->id,
                                                             $user->id)) {

            User_flag_profile::create($user->id, $profile->id);
        }
        return true;
    }

    /**
     * Ensure that flag entries for a profile are deleted
     * along with the profile when deleting users.
     * This prevents breakage of the admin profile flag UI.
     *
     * @param Profile $profile
     * @param array &$related list of related tables; entries
     *              with matching profile_id will be deleted.
     *
     * @return boolean hook result
     */
    function onProfileDeleteRelated($profile, &$related)
    {
        $related[] = 'user_flag_profile';
        return true;
    }

    /**
     * Ensure that flag entries created by a user are deleted
     * when that user gets deleted.
     *
     * @param User $user
     * @param array &$related list of related tables; entries
     *              with matching user_id will be deleted.
     *
     * @return boolean hook result
     */
    function onUserDeleteRelated($user, &$related)
    {
        $related[] = 'user_flag_profile';
        return true;
    }

    /**
     * Provide plugin version information.
     *
     * This data is used when showing the version page.
     *
     * @param array &$versions array of version data arrays; see EVENTS.txt
     *
     * @return boolean hook value
     */
    function onPluginVersion(array &$versions)
    {
        $url = 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/UserFlag';

        $versions[] = array('name' => 'UserFlag',
            'version' => self::PLUGIN_VERSION,
            'author' => 'Evan Prodromou',
            'homepage' => $url,
            'rawdescription' =>
            // TRANS: Plugin description.
            _m('This plugin allows flagging of profiles for review and reviewing flagged profiles.'));

        return true;
    }
}


code:
<?php
/**
 * Show latest and greatest profile flags
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * 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/>.
 */

if (!defined('STATUSNET')) {
    exit(1);
}

/**
 * Show the latest and greatest profile flags
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class AdminprofileflagAction extends Action
{
    var $page     = null;
    var $profiles = null;

    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     */
    function prepare(array $args = array())
    {
        parent::prepare($args);

        $user = common_current_user();

        // User must be logged in.

        if (!common_logged_in()) {
            // TRANS: Error message displayed when trying to perform an action that requires a logged in user.
            $this->clientError(_m('Not logged in.'));
        }

        $user = common_current_user();

        // ...because they're logged in

        assert(!empty($user));

        // It must be a "real" login, not saved cookie login

        if (!common_is_real_login()) {
            // Cookie theft is too easy; we require automatic
            // logins to re-authenticate before admining the site
            common_set_returnto($this->selfUrl());
            if (Event::handle('RedirectToLogin', array($this, $user))) {
                common_redirect(common_local_url('login'), 303);
            }
        }

        // User must have the right to review flags

        if (!$user->hasRight(UserFlagPlugin::REVIEWFLAGS)) {
            // TRANS: Error message displayed when trying to review profile flags while not authorised.
            $this->clientError(_m('You cannot review profile flags.'));
        }

        $this->page = $this->trimmed('page');

        if (empty($this->page)) {
            $this->page = 1;
        }

        $this->profiles = $this->getProfiles();

        return true;
    }

    /**
     * Handle request
     *
     * @param array $args $_REQUEST args; handled in prepare()
     *
     * @return void
     */
    function handle()
    {
        parent::handle();

        $this->showPage();
    }

    /**
     * Title of this page
     *
     * @return string Title of the page
     */
    function title()
    {
        // TRANS: Title for page with a list of profiles that were flagged for review.
        return _m('Flagged profiles');
    }

    /**
     * save the profile flag
     *
     * @return void
     */
    function showContent()
    {
        $pl = new FlaggedProfileList($this->profiles, $this);

        $cnt = $pl->show();

        $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
                          $this->page, 'adminprofileflag');
    }

    /**
     * Retrieve this action's profiles
     *
     * @return Profile $profile Profile query results
     */
    function getProfiles()
    {
        $ufp = new User_flag_profile();

        $ufp->selectAdd();
        $ufp->selectAdd('profile_id');
        $ufp->selectAdd('count(*) as flag_count');

        $ufp->whereAdd('cleared is NULL');

        $ufp->groupBy('profile_id');
        $ufp->orderBy('flag_count DESC, profile_id DESC');

        $offset = ($this->page-1) * PROFILES_PER_PAGE;
        $limit  = PROFILES_PER_PAGE + 1;

        $ufp->limit($offset, $limit);

        $profiles = array();

        if ($ufp->find()) {
            while ($ufp->fetch()) {
                $profile = Profile::getKV('id', $ufp->profile_id);
                if (!empty($profile)) {
                    $profiles[] = $profile;
                }
            }
        }

        $ufp->free();

        return new ArrayWrapper($profiles);
    }
}

/**
 * Specialization of ProfileList to show flagging information
 *
 * Most of the hard part is done in FlaggedProfileListItem.
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class FlaggedProfileList extends ProfileList
{
    /**
     * Factory method for creating new list items
     *
     * @param Profile $profile Profile to create an item for
     *
     * @return ProfileListItem newly-created item
     */
    function newListItem(Profile $profile)
    {
        return new FlaggedProfileListItem($profile, $this->action);
    }
}

/**
 * Specialization of ProfileListItem to show flagging information
 *
 * @category Widget
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class FlaggedProfileListItem extends ProfileListItem
{
    const MAX_FLAGGERS = 5;

    var $user   = null;
    var $r2args = null;

    /**
     * Overload parent's action list with our own moderation-oriented buttons
     *
     * @return void
     */
    function showActions()
    {
        $this->user = common_current_user();

        list($action, $this->r2args) = $this->out->returnToArgs();

        $this->r2args['action'] = $action;

        $this->startActions();
        if (Event::handle('StartProfileListItemActionElements', array($this))) {
            $this->out->elementStart('li', 'entity_moderation');
            // TRANS: Header for moderation menu with action buttons for flagged profiles (like 'sandbox', 'silence', ...).
            $this->out->element('p', null, _m('Moderate'));
            $this->out->elementStart('ul');
            $this->showSandboxButton();
            $this->showSilenceButton();
            $this->showDeleteButton();
            $this->showClearButton();
            $this->out->elementEnd('ul');
            $this->out->elementEnd('li');
            Event::handle('EndProfileListItemActionElements', array($this));
        }
        $this->endActions();
    }

    /**
     * Show a button to sandbox the profile
     *
     * @return void
     */
    function showSandboxButton()
    {
        if ($this->user->hasRight(Right::SANDBOXUSER)) {
            $this->out->elementStart('li', 'entity_sandbox');
            if ($this->profile->isSandboxed()) {
                $usf = new UnSandboxForm($this->out, $this->profile, $this->r2args);
                $usf->show();
            } else {
                $sf = new SandboxForm($this->out, $this->profile, $this->r2args);
                $sf->show();
            }
            $this->out->elementEnd('li');
        }
    }

    /**
     * Show a button to silence the profile
     *
     * @return void
     */
    function showSilenceButton()
    {
        if ($this->user->hasRight(Right::SILENCEUSER)) {
            $this->out->elementStart('li', 'entity_silence');
            if ($this->profile->isSilenced()) {
                $usf = new UnSilenceForm($this->out, $this->profile, $this->r2args);
                $usf->show();
            } else {
                $sf = new SilenceForm($this->out, $this->profile, $this->r2args);
                $sf->show();
            }
            $this->out->elementEnd('li');
        }
    }

    /**
     * Show a button to delete user and profile
     *
     * @return void
     */
    function showDeleteButton()
    {

        if ($this->user->hasRight(Right::DELETEUSER)) {
            $this->out->elementStart('li', 'entity_delete');
            $df = new DeleteUserForm($this->out, $this->profile, $this->r2args);
            $df->show();
            $this->out->elementEnd('li');
        }
    }

    /**
     * Show a button to clear flags
     *
     * @return void
     */
    function showClearButton()
    {
        if ($this->user->hasRight(UserFlagPlugin::CLEARFLAGS)) {
            $this->out->elementStart('li', 'entity_clear');
            $cf = new ClearFlagForm($this->out, $this->profile, $this->r2args);
            $cf->show();
            $this->out->elementEnd('li');
        }
    }

    /**
     * Overload parent function to add flaggers list
     *
     * @return void
     */
    function endProfile()
    {
        $this->showFlaggersList();
        parent::endProfile();
    }

    /**
     * Show a list of people who've flagged this profile
     *
     * @return void
     */
    function showFlaggersList()
    {
        $flaggers = array();

        $ufp = new User_flag_profile();

        $ufp->selectAdd();
        $ufp->selectAdd('user_id');
        $ufp->profile_id = $this->profile->id;
        $ufp->orderBy('created');

        if ($ufp->find()) { // XXX: this should always happen
            while ($ufp->fetch()) {
                $user = User::getKV('id', $ufp->user_id);
                if (!empty($user)) { // XXX: this would also be unusual
                    $flaggers[] = clone($user);
                }
            }
        }

        $cnt    = count($flaggers);
        $others = 0;

        if ($cnt > self::MAX_FLAGGERS) {
            $flaggers = array_slice($flaggers, 0, self::MAX_FLAGGERS);
            $others   = $cnt - self::MAX_FLAGGERS;
        }

        $lnks = array();

        foreach ($flaggers as $flagger) {

            $url = common_local_url('showstream',
                                    array('nickname' => $flagger->nickname));

            $lnks[] = XMLStringer::estring('a', array('href' => $url,
                                                      'class' => 'flagger'),
                                           $flagger->nickname);
        }

        if ($cnt > 0) {
            if ($others > 0) {
                $flagging_users = implode(', ', $lnks);
                // TRANS: Message displayed on a profile if it has been flagged.
                // TRANS: %1$s is a comma separated list of at most 5 user nicknames that flagged.
                // TRANS: %2$d is a positive integer of additional flagging users. Also used for plural.
                $text .= sprintf(_m('Flagged by %1$s and %2$d other', 'Flagged by %1$s and %2$d others', $others), $flagging_users, $others);
            } else {
                // TRANS: Message displayed on a profile if it has been flagged.
                // TRANS: %s is a comma separated list of at most 5 user nicknames that flagged.
                $text .= sprintf(_m('Flagged by %s'), $flagging_users);
            }

            $this->out->elementStart('p', array('class' => 'flaggers'));
            $this->out->raw($text);
            $this->out->elementEnd('p');
        }
    }
}


code:
<?php
/**
 * Clear all flags for a profile
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * 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/>.
 */

if (!defined('STATUSNET')) {
    exit(1);
}

/**
 * Action to clear flags for a profile
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class ClearflagAction extends ProfileFormAction
{
    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     */
    function prepare(array $args = array())
    {
        if (!parent::prepare($args)) {
            return false;
        }

        $user = common_current_user();

        assert(!empty($user)); // checked above
        assert(!empty($this->profile)); // checked above

        return true;
    }

    /**
     * Handle request
     *
     * Overriding the base Action's handle() here to deal check
     * for Ajax and return an HXR response if necessary
     *
     * @param array $args $_REQUEST args; handled in prepare()
     *
     * @return void
     */
    function handle()
    {
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $this->handlePost();
            if (!$this->boolean('ajax')) {
                $this->returnToPrevious();
            }
        }
    }

    /**
     * Handle POST
     *
     * Executes the actions; deletes all flags
     *
     * @return void
     */
    function handlePost()
    {
        $ufp = new User_flag_profile();

        $result = $ufp->query('UPDATE user_flag_profile ' .
                              'SET cleared = now() ' .
                              'WHERE cleared is null ' .
                              'AND profile_id = ' . $this->profile->id);

        if ($result == false) {
            // TRANS: Server exception given when flags could not be cleared.
            // TRANS: %s is a profile nickname.
            $msg = sprintf(_m('Could not clear flags for profile "%s".'),
                           $this->profile->nickname);
            throw new ServerException($msg);
        }

        $ufp->free();

        if ($this->boolean('ajax')) {
            $this->ajaxResults();
        }
    }

    /**
     * Return results in ajax form
     *
     * @return void
     */
    function ajaxResults()
    {
        $this->startHTML('text/xml;charset=utf-8');
        $this->elementStart('head');
        // TRANS: Title for AJAX form to indicated that flags were removed.
        $this->element('title', null, _m('Flags cleared'));
        $this->elementEnd('head');
        $this->elementStart('body');
        // TRANS: Body element for "flags cleared" form.
        $this->element('p', 'cleared', _m('Cleared'));
        $this->elementEnd('body');
        $this->endHTML();
    }
}


code:
<?php
/**
 * Add a flag to a profile
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * 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/>.
 */

if (!defined('STATUSNET')) {
    exit(1);
}

/**
 * Action to flag a profile.
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class FlagprofileAction extends ProfileFormAction
{
    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     */
    function prepare(array $args = array())
    {
        if (!parent::prepare($args)) {
            return false;
        }

        $user = common_current_user();

        assert(!empty($user)); // checked above
        assert(!empty($this->profile)); // checked above

        return true;
    }

    /**
     * Handle request
     *
     * Overriding the base Action's handle() here to deal check
     * for Ajax and return an HXR response if necessary
     *
     * @param array $args $_REQUEST args; handled in prepare()
     *
     * @return void
     */
    function handle()
    {
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            $this->handlePost();
            if (!$this->boolean('ajax')) {
                $this->returnToPrevious();
            }
        }
    }

    /**
     * Handle POST
     *
     * @return void
     */
    function handlePost()
    {
        $user = common_current_user();

        assert(!empty($user));
        assert(!empty($this->profile));

        // throws an exception on error

        if (User_flag_profile::exists($this->profile->id,
                                      $user->id)) {
            // We'll return to the profile page (or return the updated AJAX form)
            // showing the current state, so no harm done.
        } else {
            User_flag_profile::create($user->id, $this->profile->id);
        }

        if ($this->boolean('ajax')) {
            $this->ajaxResults();
        }
    }

    /**
     * Return results as AJAX message
     *
     * @return void
     */
    function ajaxResults()
    {
        $this->startHTML('text/xml;charset=utf-8');
        $this->elementStart('head');
        // TRANS: AJAX form title for a flagged profile.
        $this->element('title', null, _m('Flagged for review'));
        $this->elementEnd('head');
        $this->elementStart('body');
        // TRANS: Body text for AJAX form when a profile has been flagged for review.
        $this->element('p', 'flagged', _m('Flagged'));
        $this->elementEnd('body');
        $this->endHTML();
    }
}


code:
<?php
/**
 * Data class for profile flags
 *
 * PHP version 5
 *
 * @category Data
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 *
 * 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/>.
 */

if (!defined('STATUSNET')) {
    exit(1);
}

require_once INSTALLDIR . '/classes/Memcached_DataObject.php';

/**
 * Data class for profile flags
 *
 * A class representing a user flagging another profile for review.
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class User_flag_profile extends Managed_DataObject
{
    ###START_AUTOCODE
    /* the code below is auto generated do not remove the above tag */

    public $__table = 'user_flag_profile';               // table name
    public $profile_id;                      // int(11)  primary_key not_null
    public $user_id;                         // int(11)  primary_key not_null
    public $cleared;                         // datetime   default_0000-00-00%2000%3A00%3A00
    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(
                'profile_id' => array('type' => 'int', 'not null' => true, 'description' => 'profile id flagged'),
                'user_id' => array('type' => 'int', 'not null' => true, 'description' => 'user id of the actor'),
                'cleared' => array('type' => 'datetime', 'description' => 'when flag was removed'),
                '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'),
            ),
            'primary key' => array('profile_id', 'user_id'),
            'indexes' => array(
                'user_flag_profile_cleared_idx' => array('cleared'),
                'user_flag_profile_created_idx' => array('created'),
            ),
        );
    }

    /**
     * Check if a flag exists for given profile and user
     *
     * @param integer $profile_id Profile to check for
     * @param integer $user_id    User to check for
     *
     * @return boolean true if exists, else false
     */
    static function exists($profile_id, $user_id)
    {
        $ufp = User_flag_profile::pkeyGet(array('profile_id' => $profile_id,
                                                'user_id' => $user_id));

        return !empty($ufp);
    }

    /**
     * Create a new flag
     *
     * @param integer $user_id    ID of user who's flagging
     * @param integer $profile_id ID of profile being flagged
     *
     * @return boolean success flag
     */
    static function create($user_id, $profile_id)
    {
        $ufp = new User_flag_profile();

        $ufp->profile_id = $profile_id;
        $ufp->user_id    = $user_id;
        $ufp->created    = common_sql_now();

        if (!$ufp->insert()) {
            // TRANS: Server exception.
            // TRANS: %d is a profile ID (number).
            $msg = sprintf(_m('Could not flag profile "%d" for review.'),
                           $profile_id);
            throw new ServerException($msg);
        }

        $ufp->free();

        return true;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for clearing profile flags
 *
 * 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  Form
 * @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')) {
    exit(1);
}

require_once INSTALLDIR.'/lib/form.php';

/**
 * Form for clearing profile flags
 *
 * @category Form
 * @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 ClearFlagForm extends ProfileActionForm
{
    /**
     * class of the form
     * Action this form provides
     *
     * @return string class of the form
     */
    function formClass()
    {
        return 'form_user_clearflag';
    }

    /**
     * Action this form provides
     *
     * @return string Name of the action, lowercased.
     */
    function target()
    {
        return 'clearflag';
    }

    /**
     * Title of the form
     *
     * @return string Title of the form, internationalized
     */
    function title()
    {
        // TRANS: Form title for action on a profile.
        return _m('Clear');
    }

    /**
     * Description of the form
     *
     * @return string description of the form, internationalized
     */

    function description()
    {
        // TRANS: Form description for clearing flags from a profile.
        return _m('Clear all flags');
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for flagging a profile
 *
 * 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  Form
 * @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')) {
    exit(1);
}

require_once INSTALLDIR.'/lib/form.php';

/**
 * Form for flagging a profile
 *
 * A form for flagging a profile
 *
 * @category Form
 * @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 FlagProfileForm extends ProfileActionForm
{
    /**
     * class of the form
     * Action this form provides
     *
     * @return string class of the form
     */
    function formClass()
    {
        return 'form_entity_flag ajax';
    }

    /**
     * Action this form provides
     *
     * @return string Name of the action, lowercased.
     */
    function target()
    {
        return 'flagprofile';
    }

    /**
     * Title of the form
     *
     * @return string Title of the form, internationalized
     */
    function title()
    {
        // TRANS: Form title for flagging a profile for review.
        return _m('Flag');
    }

    /**
     * Description of the form
     *
     * @return string description of the form, internationalized
     */
    function description()
    {
        // TRANS: Form description.
        return _m('Flag profile for review.');
    }
}


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-06-08 18:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"

#. TRANS: Message added to a profile if it has been flagged for review.
#. TRANS: Body text for AJAX form when a profile has been flagged for review.
#: UserFlagPlugin.php:136 actions/flagprofile.php:127
msgid "Flagged"
msgstr ""

#. TRANS: Plugin description.
#: UserFlagPlugin.php:242
msgid ""
"This plugin allows flagging of profiles for review and reviewing flagged "
"profiles."
msgstr ""

#. TRANS: Error message displayed when trying to perform an action that requires a logged in user.
#: actions/adminprofileflag.php:65
msgid "Not logged in."
msgstr ""

#. TRANS: Error message displayed when trying to review profile flags while not authorised.
#: actions/adminprofileflag.php:89
msgid "You cannot review profile flags."
msgstr ""

#. TRANS: Title for page with a list of profiles that were flagged for review.
#: actions/adminprofileflag.php:125
msgid "Flagged profiles"
msgstr ""

#. TRANS: Header for moderation menu with action buttons for flagged profiles (like 'sandbox', 'silence', ...).
#: actions/adminprofileflag.php:242
msgid "Moderate"
msgstr ""

#. TRANS: Message displayed on a profile if it has been flagged.
#. TRANS: %1$s is a comma separated list of at most 5 user nicknames that flagged.
#. TRANS: %2$d is a positive integer of additional flagging users. Also used for plural.
#: actions/adminprofileflag.php:388
#, php-format
msgid "Flagged by %1$s and %2$d other"
msgid_plural "Flagged by %1$s and %2$d others"
msgstr[0] ""
msgstr[1] ""

#. TRANS: Message displayed on a profile if it has been flagged.
#. TRANS: %s is a comma separated list of at most 5 user nicknames that flagged.
#: actions/adminprofileflag.php:392
#, php-format
msgid "Flagged by %s"
msgstr ""

#. TRANS: Server exception given when flags could not be cleared.
#. TRANS: %s is a profile nickname.
#: actions/clearflag.php:105
#, php-format
msgid "Could not clear flags for profile \"%s\"."
msgstr ""

#. TRANS: Title for AJAX form to indicated that flags were removed.
#: actions/clearflag.php:127
msgid "Flags cleared"
msgstr ""

#. TRANS: Body element for "flags cleared" form.
#: actions/clearflag.php:131
msgid "Cleared"
msgstr ""

#. TRANS: AJAX form title for a flagged profile.
#: actions/flagprofile.php:123
msgid "Flagged for review"
msgstr ""

#. TRANS: Server exception.
#. TRANS: %d is a profile ID (number).
#: classes/User_flag_profile.php:115
#, php-format
msgid "Could not flag profile \"%d\" for review."
msgstr ""

#. TRANS: Form title for action on a profile.
#: forms/clearflag.php:76
msgid "Clear"
msgstr ""

#. TRANS: Form description for clearing flags from a profile.
#: forms/clearflag.php:88
msgid "Clear all flags"
msgstr ""

#. TRANS: Form title for flagging a profile for review.
#: forms/flagprofile.php:78
msgid "Flag"
msgstr ""

#. TRANS: Form description.
#: forms/flagprofile.php:89
msgid "Flag profile for review."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 12:17+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Afrikaans (http://www.transifex.com/gnu-social/gnu-social/language/af/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: af\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Form title for flagging a profile for review.
#: forms/flagprofile.php:78
msgid "Flag"
msgstr ""

#. TRANS: Form description.
#: forms/flagprofile.php:89
msgid "Flag profile for review."
msgstr ""

#. TRANS: Form title for action on a profile.
#: forms/clearflag.php:76
msgid "Clear"
msgstr ""

#. TRANS: Form description for clearing flags from a profile.
#: forms/clearflag.php:88
msgid "Clear all flags"
msgstr ""

#. TRANS: AJAX form title for a flagged profile.
#: actions/flagprofile.php:123
msgid "Flagged for review"
msgstr ""

#. TRANS: Body text for AJAX form when a profile has been flagged for review.
#. TRANS: Message added to a profile if it has been flagged for review.
#: actions/flagprofile.php:127 UserFlagPlugin.php:134
msgid "Flagged"
msgstr ""

#. TRANS: Error message displayed when trying to perform an action that
#. requires a logged in user.
#: actions/adminprofileflag.php:65
msgid "Not logged in."
msgstr "Nie aangeteken nie."

#. TRANS: Error message displayed when trying to review profile flags while
#. not authorised.
#: actions/adminprofileflag.php:89
msgid "You cannot review profile flags."
msgstr ""

#. TRANS: Title for page with a list of profiles that were flagged for review.
#: actions/adminprofileflag.php:125
msgid "Flagged profiles"
msgstr ""

#. TRANS: Header for moderation menu with action buttons for flagged profiles
#. (like 'sandbox', 'silence', ...).
#: actions/adminprofileflag.php:242
msgid "Moderate"
msgstr "Modereer"

#. TRANS: Message displayed on a profile if it has been flagged.
#. TRANS: %1$s is a comma separated list of at most 5 user nicknames that
#. flagged.
#. TRANS: %2$d is a positive integer of additional flagging users. Also used
#. for plural.
#: actions/adminprofileflag.php:388
#, php-format
msgid "Flagged by %1$s and %2$d other"
msgid_plural "Flagged by %1$s and %2$d others"
msgstr[0] ""
msgstr[1] ""

#. TRANS: Message displayed on a profile if it has been flagged.
#. TRANS: %s is a comma separated list of at most 5 user nicknames that
#. flagged.
#: actions/adminprofileflag.php:392
#, php-format
msgid "Flagged by %s"
msgstr ""

#. TRANS: Server exception given when flags could not be cleared.
#. TRANS: %s is a profile nickname.
#: actions/clearflag.php:105
#, php-format
msgid "Could not clear flags for profile \"%s\"."
msgstr ""

#. TRANS: Title for AJAX form to indicated that flags were removed.
#: actions/clearflag.php:127
msgid "Flags cleared"
msgstr ""

#. TRANS: Body element for "flags cleared" form.
#: actions/clearflag.php:131
msgid "Cleared"
msgstr ""

#. TRANS: Server exception.
#. TRANS: %d is a profile ID (number).
#: classes/User_flag_profile.php:115
#, php-format
msgid "Could not flag profile \"%d\" for review."
msgstr ""

#. TRANS: Plugin description.
#: UserFlagPlugin.php:240
msgid ""
"This plugin allows flagging of profiles for review and reviewing flagged "
"profiles."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2011 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 12:34+0000\n"
"Last-Translator: digitaldreamer <digitaldreamer@email.cz>\n"
"Language-Team: Arabic (http://www.transifex.com/gnu-social/gnu-social/language/ar/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"

#. TRANS: Form title for flagging a profile for review.
#: forms/flagprofile.php:78
msgid "Flag"
msgstr "علّم"

#. TRANS: Form description.
#: forms/flagprofile.php:89
msgid "Flag profile for review."
msgstr ""

#. TRANS: Form title for action on a profile.
#: forms/clearflag.php:76
msgid "Clear"
msgstr "امسح"

#. TRANS: Form description for clearing flags from a profile.
#: forms/clearflag.php:88
msgid "Clear all flags"
msgstr "امسح كل الأعلام"

#. TRANS: AJAX form title for a flagged profile.
#: actions/flagprofile.php:123
msgid "Flagged for review"
msgstr "عُلّم للمراجعة"

#. TRANS: Body text for AJAX form when a profile has been flagged for review.
#. TRANS: Message added to a profile if it has been flagged for review.
#: actions/flagprofile.php:127 UserFlagPlugin.php:134
msgid "Flagged"
msgstr "عُلّم"

#. TRANS: Error message displayed when trying to perform an action that
#. requires a logged in user.
#: actions/adminprofileflag.php:65
msgid "Not logged in."
msgstr "لست والجًا."

#. TRANS: Error message displayed when trying to review profile flags while
#. not authorised.
#: actions/adminprofileflag.php:89
msgid "You cannot review profile flags."
msgstr ""

#. TRANS: Title for page with a list of profiles that were flagged for review.
#: actions/adminprofileflag.php:125
msgid "Flagged profiles"
msgstr "الملفات الشخصية المعلمة"

#. TRANS: Header for moderation menu with action buttons for flagged profiles
#. (like 'sandbox', 'silence', ...).
#: actions/adminprofileflag.php:242
msgid "Moderate"
msgstr "راقب"

#. TRANS: Message displayed on a profile if it has been flagged.
#. TRANS: %1$s is a comma separated list of at most 5 user nicknames that
#. flagged.
#. TRANS: %2$d is a positive integer of additional flagging users. Also used
#. for plural.
#: actions/adminprofileflag.php:388
#, php-format
msgid "Flagged by %1$s and %2$d other"
msgid_plural "Flagged by %1$s and %2$d others"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
msgstr[5] ""

#. TRANS: Message displayed on a profile if it has been flagged.
#. TRANS: %s is a comma separated list of at most 5 user nicknames that
#. flagged.
#: actions/adminprofileflag.php:392
#, php-format
msgid "Flagged by %s"
msgstr ""

#. TRANS: Server exception given when flags could not be cleared.
#. TRANS: %s is a profile nickname.
#: actions/clearflag.php:105
#, php-format
msgid "Could not clear flags for profile \"%s\"."
msgstr ""

#. TRANS: Title for AJAX form to indicated that flags were removed.
#: actions/clearflag.php:127
msgid "Flags cleared"
msgstr ""

#. TRANS: Body element for "flags cleared" form.
#: actions/clearflag.php:131
msgid "Cleared"
msgstr ""

#. TRANS: Server exception.
#. TRANS: %d is a profile ID (number).
#: classes/User_flag_profile.php:115
#, php-format
msgid "Could not flag profile \"%d\" for review."
msgstr ""

#. TRANS: Plugin description.
#: UserFlagPlugin.php:240
msgid ""
"This plugin allows flagging of profiles for review and reviewing flagged "
"profiles."
msgstr ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-07 12:17+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Arabic (Egypt) (http://www.transifex.com/gnu-social/gnu-social/language/ar_EG/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ar_EG\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"

#. TRANS: Form title for flagging a profile for review.
#: forms/flagprofile.php:78
msgid "Flag"
msgstr ""

#. TRANS: Form description.
#: forms/flagprofile.php:89
msgid "Flag profile for review."
msgstr ""

#. TRANS: Form title for action on a profile.
#: forms/clearflag.php:76
msgid "Clear"
msgstr ""

#. TRANS: Form description for clearing flags from a profile.
#: forms/clearflag.php:88
msgid "Clear all flags"
msgstr ""

#. TRANS: AJAX form title for a flagged profile.
#: actions/flagprofile.php:123
msgid "Flagged for review"
msgstr ""

#. TRANS: Body text for AJAX form when a profile has been flagged for review.
#. TRANS: Message added to a profile if it has been flagged for review.
#: actions/flagprofile.php:127 UserFlagPlugin.php:134
msgid "Flagged"
msgstr ""

#. TRANS: Error message displayed when trying to perform an action that
#. requires a logged in user.
#: actions/adminprofileflag.php:65
msgid "Not logged in."
msgstr "لست والجًا."

#. TRANS: Error message displayed when trying to review profile flags while
#. not authorised.
#: actions/adminprofileflag.php:89
msgid "You cannot review profile flags."
msgstr ""

#. TRANS: Title for page with a list of profiles that were flagged for review.
#: actions/adminprofileflag.php:125
msgid "Flagged profiles"
msgstr ""

#. TRANS: Header for moderation menu with action buttons for flagged profiles
#. (like 'sandbox', 'silence', ...).
#: actions/adminprofileflag.php:242
msgid "Moderate"
msgstr ""

#. TRANS: Message displayed on a profile if it has been flagged.
#. TRANS: %1$s is a comma separated list of at most 5 user nicknames that
#. flagged.
#. TRANS: %2$d is a positive integer of additional flagging users. Also used
#. for plural.
#: actions/adminprofileflag.php:388
#, php-format
msgid "Flagged by %1$s and %2$d other"
msgid_plural "Flagged by %1$s and %2$d others"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
msgstr[5] ""

#. TRANS: Message displayed on a profile if it has been flagged.
#. TRANS: %s is a comma separated list of at most 5 user nicknames that
#. flagged.
#: actions/adminprofileflag.php:392
#, php-format
msgid "Flagged by %s"
msgstr ""

#. TRANS: Server exception given when flags could not be cleared.
#. TRANS: %s is a profile nickname.
#: actions/clearflag.php:105
#, php-format
msgid "Could not clear flags for profile \"%s\"."
msgstr ""

#. TRANS: Title for AJAX form to indicated that flags were removed.
#: actions/clearflag.php:127
msgid "Flags cleared"
msgstr ""

#. TRANS: Body element for "flags cleared" form.
#: actions/clearflag.php:131
msgid "Cleared"
msgstr ""

#. TRANS: Server exception.
#. TRANS: %d is a profile ID (number).
#: classes/User_flag_profile.php:115
#, php-format
msgid "Could not flag profile \"%d\" for review."
msgstr ""

#. TRANS: Plugin description.
#: UserFlagPlugin.php:240
msgid ""
"This plugin allows flagging of profiles for review and reviewing flagged "
"profiles."
msgstr ""