1
0
Fork 0

Merge pull request #7904 from MrPetovan/task/7887-api-followers-request

Add GET /api/v1/follow_requests Mastodon API endpoint
This commit is contained in:
Philipp 2019-12-10 15:53:06 +01:00 committed by GitHub
commit d0b20fb499
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 3765 additions and 2214 deletions

View file

@ -0,0 +1,86 @@
<?php
namespace Friendica\Api\Mastodon;
use Friendica\Content\Text\BBCode;
use Friendica\Database\DBA;
use Friendica\Util\DateTimeFormat;
/**
* Class Account
*
* @see https://docs.joinmastodon.org/api/entities/#account
*/
class Account
{
/** @var string */
var $id;
/** @var string */
var $username;
/** @var string */
var $acct;
/** @var string */
var $display_name;
/** @var bool */
var $locked;
/** @var string (Datetime) */
var $created_at;
/** @var int */
var $followers_count;
/** @var int */
var $following_count;
/** @var int */
var $statuses_count;
/** @var string */
var $note;
/** @var string (URL)*/
var $url;
/** @var string (URL) */
var $avatar;
/** @var string (URL) */
var $avatar_static;
/** @var string (URL) */
var $header;
/** @var string (URL) */
var $header_static;
/** @var Emoji[] */
var $emojis;
/** @var Account|null */
var $moved = null;
/** @var Field[]|null */
var $fields = null;
/** @var bool|null */
var $bot = null;
/**
* Creates an account record from a contact record. Expects all contact table fields to be set
*
* @param array $contact
* @return Account
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function createFromContact(array $contact) {
$account = new Account();
$account->id = $contact['id'];
$account->username = $contact['nick'];
$account->acct = $contact['nick'];
$account->display_name = $contact['name'];
$account->locked = $contact['blocked'];
$account->created_at = DateTimeFormat::utc($contact['created'], DateTimeFormat::ATOM);
// No data is available from contact
$account->followers_count = 0;
$account->following_count = 0;
$account->statuses_count = 0;
$account->note = BBCode::convert($contact['about']);
$account->url = $contact['url'];
$account->avatar = $contact['avatar'];
$account->avatar_static = $contact['avatar'];
// No header picture in Friendica
$account->header = '';
$account->header_static = '';
// No custom emojis per account in Friendica
$account->emojis = [];
return $account;
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace Friendica\Api\Mastodon;
/**
* Class Emoji
*
* @see https://docs.joinmastodon.org/api/entities/#emoji
*/
class Emoji
{
/** @var string */
var $shortcode;
/** @var string (URL)*/
var $static_url;
/** @var string (URL)*/
var $url;
/** @var bool */
var $visible_in_picker;
}

View file

@ -0,0 +1,18 @@
<?php
namespace Friendica\Api\Mastodon;
/**
* Class Field
*
* @see https://docs.joinmastodon.org/api/entities/#field
*/
class Field
{
/** @var string */
var $name;
/** @var string (HTML) */
var $value;
/** @var string (Datetime)*/
var $verified_at;
}

View file

@ -0,0 +1,79 @@
<?php
namespace Friendica\Module\Api\Mastodon;
use Friendica\Api\Mastodon\Account;
use Friendica\App\BaseURL;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Module\Base\Api;
use Friendica\Network\HTTPException;
/**
* @see https://docs.joinmastodon.org/api/rest/follow-requests/
*/
class FollowRequests extends Api
{
public static function init(array $parameters = [])
{
parent::init($parameters);
self::login();
}
/**
* @param array $parameters
* @throws HTTPException\InternalServerErrorException
* @see https://docs.joinmastodon.org/api/rest/follow-requests/#get-api-v1-follow-requests
*/
public static function rawContent(array $parameters = [])
{
$since_id = $_GET['since_id'] ?? null;
$max_id = $_GET['max_id'] ?? null;
$limit = intval($_GET['limit'] ?? 40);
if (isset($since_id) && isset($max_id)) {
$condition = ['`uid` = ? AND NOT `self` AND `pending` AND `id` > ? AND `id` < ?', self::$current_user_id, $since_id, $max_id];
} elseif (isset($since_id)) {
$condition = ['`uid` = ? AND NOT `self` AND `pending` AND `id` > ?', self::$current_user_id, $since_id];
} elseif (isset($max_id)) {
$condition = ['`uid` = ? AND NOT `self` AND `pending` AND `id` < ?', self::$current_user_id, $max_id];
} else {
$condition = ['`uid` = ? AND NOT `self` AND `pending`', self::$current_user_id];
}
$count = DBA::count('contact', $condition);
$contacts = Contact::selectToArray(
[],
$condition,
['order' => ['id' => 'DESC'], 'limit' => $limit]
);
$return = [];
foreach ($contacts as $contact) {
$account = Account::createFromContact($contact);
$return[] = $account;
}
$base_query = [];
if (isset($_GET['limit'])) {
$base_query['limit'] = $limit;
}
/** @var BaseURL $BaseURL */
$BaseURL = self::getClass(BaseURL::class);
$links = [];
if ($count > $limit) {
$links[] = '<' . $BaseURL->get() . '/api/v1/follow_requests?' . http_build_query($base_query + ['max_id' => $contacts[count($contacts) - 1]['id']]) . '>; rel="next"';
}
$links[] = '<' . $BaseURL->get() . '/api/v1/follow_requests?' . http_build_query($base_query + ['since_id' => $contacts[0]['id']]) . '>; rel="prev"';
header('Link: ' . implode(', ', $links));
System::jsonExit($return);
}
}

105
src/Module/Base/Api.php Normal file
View file

@ -0,0 +1,105 @@
<?php
namespace Friendica\Module\Base;
use Friendica\App\Arguments;
use Friendica\BaseModule;
use Friendica\Core\L10n;
use Friendica\Network\HTTPException;
require_once __DIR__ . '/../../../include/api.php';
class Api extends BaseModule
{
/**
* @var string json|xml|rss|atom
*/
protected static $format = 'json';
/**
* @var bool|int
*/
protected static $current_user_id;
public static function init(array $parameters = [])
{
$Arguments = self::getClass(Arguments::class);
if (substr($Arguments->getQueryString(), -4) === '.xml') {
self::$format = 'xml';
}
if (substr($Arguments->getQueryString(), -4) === '.rss') {
self::$format = 'rss';
}
if (substr($Arguments->getQueryString(), -4) === '.atom') {
self::$format = 'atom';
}
}
public static function post(array $parameters = [])
{
if (!api_user()) {
throw new HTTPException\UnauthorizedException(L10n::t('Permission denied.'));
}
$a = self::getApp();
if (!empty($a->user['uid']) && $a->user['uid'] != api_user()) {
throw new HTTPException\ForbiddenException(L10n::t('Permission denied.'));
}
}
/**
* Log in user via OAuth1 or Simple HTTP Auth.
* Simple Auth allow username in form of <pre>user@server</pre>, ignoring server part
*
* @brief Login API user
*
* @throws HTTPException\ForbiddenException
* @throws HTTPException\UnauthorizedException
* @throws HTTPException\InternalServerErrorException
* @hook 'authenticate'
* array $addon_auth
* 'username' => username from login form
* 'password' => password from login form
* 'authenticated' => return status,
* 'user_record' => return authenticated user record
*/
protected static function login()
{
api_login(self::getApp());
self::$current_user_id = api_user();
}
/**
* @brief Get user info array.
*
* @param int|string $contact_id Contact ID or URL
* @return array|bool
* @throws HTTPException\BadRequestException
* @throws HTTPException\InternalServerErrorException
* @throws HTTPException\UnauthorizedException
* @throws \ImagickException
*/
protected static function getUser($contact_id = null)
{
return api_get_user(self::getApp(), $contact_id);
}
protected static function format($root_element, $data)
{
switch (self::$format) {
case "atom":
case "rss":
case "xml":
$ret = api_create_xml($data, $root_element);
break;
case "json":
default:
$ret = $data;
break;
}
return $ret;
}
}

View file

@ -65,10 +65,11 @@ class Help extends BaseModule
$lastLevel = 1;
$idNum = [0, 0, 0, 0, 0, 0, 0];
foreach ($lines as &$line) {
if (substr($line, 0, 2) == "<h") {
$level = substr($line, 2, 1);
if ($level != "r") {
$level = intval($level);
$matches = [];
foreach ($lines as &$line) {
if (preg_match('#<h([1-6])>([^<]+?)</h\1>#i', $line, $matches)) {
$level = $matches[1];
$anchor = urlencode($matches[2]);
if ($level < $lastLevel) {
for ($k = $level; $k < $lastLevel; $k++) {
$toc .= "</ul></li>";
@ -84,10 +85,13 @@ class Help extends BaseModule
}
$idNum[$level] ++;
$href = $a->getBaseURL() . "/help/{$filename}#{$anchor}";
$toc .= "<li><a href=\"{$href}\">" . strip_tags($line) . "</a></li>";
$id = implode("_", array_slice($idNum, 1, $level));
$href = $a->getBaseURL() . "/help/{$filename}#{$id}";
$toc .= "<li><a href='{$href}'>" . strip_tags($line) . "</a></li>";
$line = "<a name='{$id}'></a>" . $line;
$line = "<a name=\"{$id}\"></a>" . $line;
$line = "<a name=\"{$anchor}\"></a>" . $line;
$lastLevel = $level;
}
}

View file

@ -12,6 +12,7 @@ namespace Friendica\Network;
use Friendica\Core\Config;
use Friendica\Core\Logger;
use Friendica\Database\DBA;
use Friendica\Util\Strings;
use OAuthConsumer;
use OAuthDataStore;
use OAuthToken;
@ -26,15 +27,16 @@ class FKOAuthDataStore extends OAuthDataStore
{
/**
* @return string
* @throws \Exception
*/
private static function genToken()
{
return Friendica\Util\Strings::getRandomHex(32);
return Strings::getRandomHex(32);
}
/**
* @param string $consumer_key key
* @return mixed
* @return OAuthConsumer|null
* @throws \Exception
*/
public function lookup_consumer($consumer_key)
@ -52,17 +54,17 @@ class FKOAuthDataStore extends OAuthDataStore
}
/**
* @param string $consumer consumer
* @param string $token_type type
* @param string $token token
* @return mixed
* @param OAuthConsumer $consumer
* @param string $token_type
* @param string $token_id
* @return OAuthToken|null
* @throws \Exception
*/
public function lookup_token($consumer, $token_type, $token)
public function lookup_token(OAuthConsumer $consumer, $token_type, $token_id)
{
Logger::log(__function__ . ":" . $consumer . ", " . $token_type . ", " . $token);
Logger::log(__function__ . ":" . $consumer . ", " . $token_type . ", " . $token_id);
$s = DBA::select('tokens', ['id', 'secret', 'scope', 'expires', 'uid'], ['client_id' => $consumer->key, 'scope' => $token_type, 'id' => $token]);
$s = DBA::select('tokens', ['id', 'secret', 'scope', 'expires', 'uid'], ['client_id' => $consumer->key, 'scope' => $token_type, 'id' => $token_id]);
$r = DBA::toArray($s);
if (DBA::isResult($r)) {
@ -77,14 +79,14 @@ class FKOAuthDataStore extends OAuthDataStore
}
/**
* @param string $consumer consumer
* @param string $token token
* @param string $nonce nonce
* @param string $timestamp timestamp
* @param OAuthConsumer $consumer
* @param OAuthToken $token
* @param string $nonce
* @param int $timestamp
* @return mixed
* @throws \Exception
*/
public function lookup_nonce($consumer, $token, $nonce, $timestamp)
public function lookup_nonce(OAuthConsumer $consumer, OAuthToken $token, $nonce, int $timestamp)
{
$token = DBA::selectFirst('tokens', ['id', 'secret'], ['client_id' => $consumer->key, 'id' => $nonce, 'expires' => $timestamp]);
if (DBA::isResult($token)) {
@ -95,12 +97,12 @@ class FKOAuthDataStore extends OAuthDataStore
}
/**
* @param string $consumer consumer
* @param string $callback optional, default null
* @return mixed
* @param OAuthConsumer $consumer
* @param string $callback
* @return OAuthToken|null
* @throws \Exception
*/
public function new_request_token($consumer, $callback = null)
public function new_request_token(OAuthConsumer $consumer, $callback = null)
{
Logger::log(__function__ . ":" . $consumer . ", " . $callback);
$key = self::genToken();
@ -131,13 +133,13 @@ class FKOAuthDataStore extends OAuthDataStore
}
/**
* @param string $token token
* @param string $consumer consumer
* @param string $verifier optional, defult null
* @return object
* @throws HTTPException\InternalServerErrorException
* @param OAuthToken $token token
* @param OAuthConsumer $consumer consumer
* @param string $verifier optional, defult null
* @return OAuthToken
* @throws \Exception
*/
public function new_access_token($token, $consumer, $verifier = null)
public function new_access_token(OAuthToken $token, OAuthConsumer $consumer, $verifier = null)
{
Logger::log(__function__ . ":" . $token . ", " . $consumer . ", " . $verifier);