Merge remote-tracking branch 'upstream/2021.06-rc' into forum-handling

This commit is contained in:
Michael 2021-06-05 06:14:06 +00:00
commit 8a9f633ce2
9 changed files with 122 additions and 21 deletions

View file

@ -73,6 +73,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en
- [`GET /api/v1/instance`](https://docs.joinmastodon.org/methods/instance#fetch-instance) - [`GET /api/v1/instance`](https://docs.joinmastodon.org/methods/instance#fetch-instance)
- GET /api/v1/instance/rules Undocumented, returns Terms of Service
- [`GET /api/v1/instance/peers`](https://docs.joinmastodon.org/methods/instance#list-of-connected-domains) - [`GET /api/v1/instance/peers`](https://docs.joinmastodon.org/methods/instance#list-of-connected-domains)
- [`GET /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`GET /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/)
- [`POST /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`POST /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/)
@ -140,7 +141,9 @@ They refer to features that don't exist in Friendica yet.
These endpoints won't be implemented at the moment. These endpoints won't be implemented at the moment.
They refer to features or data that don't exist in Friendica yet. They refer to features or data that don't exist in Friendica yet.
- POST /api/meta Misskey API endpoint.
- [`POST /api/v1/accounts`](https://docs.joinmastodon.org/methods/accounts/) - [`POST /api/v1/accounts`](https://docs.joinmastodon.org/methods/accounts/)
- [`GET /api/v1/accounts/:id/featured_tags`](https://docs.joinmastodon.org/methods/accounts/)
- [`POST /api/v1/accounts/:id/pin`](https://docs.joinmastodon.org/methods/accounts/) - [`POST /api/v1/accounts/:id/pin`](https://docs.joinmastodon.org/methods/accounts/)
- [`POST /api/v1/accounts/:id/unpin`](https://docs.joinmastodon.org/methods/accounts/) - [`POST /api/v1/accounts/:id/unpin`](https://docs.joinmastodon.org/methods/accounts/)
- [`GET /api/v1/admin/accounts`](https://docs.joinmastodon.org/methods/admin/) - [`GET /api/v1/admin/accounts`](https://docs.joinmastodon.org/methods/admin/)
@ -167,8 +170,13 @@ They refer to features or data that don't exist in Friendica yet.
- [`POST /api/v1/markers`](https://docs.joinmastodon.org/methods/timelines/markers/) - [`POST /api/v1/markers`](https://docs.joinmastodon.org/methods/timelines/markers/)
- [`GET /api/v1/polls/:id`](https://docs.joinmastodon.org/methods/statuses/polls/) - [`GET /api/v1/polls/:id`](https://docs.joinmastodon.org/methods/statuses/polls/)
- [`POST /api/v1/polls/:id/votes`](https://docs.joinmastodon.org/methods/statuses/polls/) - [`POST /api/v1/polls/:id/votes`](https://docs.joinmastodon.org/methods/statuses/polls/)
- [`DELETE /api/v1/push/subscription`](https://docs.joinmastodon.org/methods/notifications/push/)
- [`GET /api/v1/push/subscription`](https://docs.joinmastodon.org/methods/notifications/push/)
- [`PUSH /api/v1/push/subscription`](https://docs.joinmastodon.org/methods/notifications/push/)
- [`PUT /api/v1/push/subscription`](https://docs.joinmastodon.org/methods/notifications/push/)
- [`POST /api/v1/reports`](https://docs.joinmastodon.org/methods/accounts/reports/) - [`POST /api/v1/reports`](https://docs.joinmastodon.org/methods/accounts/reports/)
- [`GET /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/) - [`GET /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/)
- [`PUT /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/) - [`PUT /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/)
- [`DELETE /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/) - [`DELETE /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/)
- [`GET /api/v1/streaming`](https://docs.joinmastodon.org/methods/timelines/streaming/)
- [`DELETE /api/v1/suggestions/:id`](https://docs.joinmastodon.org/methods/accounts/suggestions/) - [`DELETE /api/v1/suggestions/:id`](https://docs.joinmastodon.org/methods/accounts/suggestions/)

View file

@ -44,6 +44,7 @@ use Friendica\Model\Photo;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Model\Verb; use Friendica\Model\Verb;
use Friendica\Module\BaseApi;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Network\HTTPException\BadRequestException; use Friendica\Network\HTTPException\BadRequestException;
use Friendica\Network\HTTPException\ExpectationFailedException; use Friendica\Network\HTTPException\ExpectationFailedException;
@ -88,6 +89,11 @@ $called_api = [];
*/ */
function api_user() function api_user()
{ {
$user = BaseApi::getCurrentUserID(true);
if (!empty($user)) {
return $user;
}
if (!empty($_SESSION['allow_api'])) { if (!empty($_SESSION['allow_api'])) {
return local_user(); return local_user();
} }

View file

@ -2882,7 +2882,7 @@ class Item
'mime' => $attachment['mimetype'], 'mime' => $attachment['mimetype'],
], ],
]); ]);
if ($item['post-type'] == Item::PT_AUDIO) { if (($item['post-type'] ?? null) == Item::PT_AUDIO) {
$leading .= $media; $leading .= $media;
} else { } else {
$trailing .= $media; $trailing .= $media;

View file

@ -24,6 +24,7 @@ namespace Friendica\Module\Api\Mastodon;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Module\BaseApi; use Friendica\Module\BaseApi;
/** /**
@ -37,14 +38,23 @@ class Accounts extends BaseApi
*/ */
public static function rawContent(array $parameters = []) public static function rawContent(array $parameters = [])
{ {
if (empty($parameters['id'])) { if (empty($parameters['id']) && empty($parameters['name'])) {
DI::mstdnError()->UnprocessableEntity(); DI::mstdnError()->UnprocessableEntity();
} }
if (!empty($parameters['id'])) {
$id = $parameters['id']; $id = $parameters['id'];
if (!DBA::exists('contact', ['id' => $id, 'uid' => 0])) { if (!DBA::exists('contact', ['id' => $id, 'uid' => 0])) {
DI::mstdnError()->RecordNotFound(); DI::mstdnError()->RecordNotFound();
} }
} else {
$contact = Contact::selectFirst(['id'], ['nick' => $parameters['name'], 'uid' => 0]);
if (!empty($contact['id'])) {
$id = $contact['id'];
} elseif (!($id = Contact::getIdForURL($parameters['name'], 0, false))) {
DI::mstdnError()->RecordNotFound();
}
}
$account = DI::mstdnAccount()->createFromContactId($id, self::getCurrentUserID()); $account = DI::mstdnAccount()->createFromContactId($id, self::getCurrentUserID());
System::jsonExit($account); System::jsonExit($account);

View file

@ -29,12 +29,21 @@ use Friendica\Module\BaseApi;
*/ */
class Filters extends BaseApi class Filters extends BaseApi
{ {
public static function post(array $parameters = [])
{
self::login(self::SCOPE_WRITE);
self::unsupported('post');
}
/** /**
* @param array $parameters * @param array $parameters
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function rawContent(array $parameters = []) public static function rawContent(array $parameters = [])
{ {
System::jsonError(404, ['error' => 'Record not found']); self::login(self::SCOPE_READ);
System::jsonExit([]);
} }
} }

View file

@ -0,0 +1,59 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Module\Api\Mastodon\Instance;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Module\BaseApi;
use Friendica\Network\HTTPException;
/**
* Undocumented API endpoint
*/
class Rules extends BaseApi
{
/**
* @param array $parameters
* @throws HTTPException\InternalServerErrorException
*/
public static function rawContent(array $parameters = [])
{
$rules = [];
$id = 0;
if (DI::config()->get('system', 'tosdisplay')) {
$html = BBCode::convert(DI::config()->get('system', 'tostext'), false, BBCode::EXTERNAL);
$msg = HTML::toPlaintext($html, 0, true);
foreach (explode("\n", $msg) as $line) {
$line = trim($line);
if ($line) {
$rules[] = ['id' => (string)++$id, 'text' => $line];
}
}
}
System::jsonExit($rules);
}
}

View file

@ -238,7 +238,7 @@ class BaseApi extends BaseModule
* *
* @return int User ID * @return int User ID
*/ */
protected static function getCurrentUserID() public static function getCurrentUserID(bool $nologin = false)
{ {
if (empty(self::$current_user_id)) { if (empty(self::$current_user_id)) {
self::$current_token = self::getTokenByBearer(); self::$current_token = self::getTokenByBearer();
@ -247,7 +247,10 @@ class BaseApi extends BaseModule
} else { } else {
self::$current_user_id = 0; self::$current_user_id = 0;
} }
}
if ($nologin) {
return (int)self::$current_user_id;
} }
if (empty(self::$current_user_id)) { if (empty(self::$current_user_id)) {

View file

@ -60,6 +60,7 @@ return [
'/accounts' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported '/accounts' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported
'/accounts/{id:\d+}' => [Module\Api\Mastodon\Accounts::class, [R::GET ]], '/accounts/{id:\d+}' => [Module\Api\Mastodon\Accounts::class, [R::GET ]],
'/accounts/{id:\d+}/statuses' => [Module\Api\Mastodon\Accounts\Statuses::class, [R::GET ]], '/accounts/{id:\d+}/statuses' => [Module\Api\Mastodon\Accounts\Statuses::class, [R::GET ]],
'/accounts/{id:\d+}/featured_tags' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo
'/accounts/{id:\d+}/followers' => [Module\Api\Mastodon\Accounts\Followers::class, [R::GET ]], '/accounts/{id:\d+}/followers' => [Module\Api\Mastodon\Accounts\Followers::class, [R::GET ]],
'/accounts/{id:\d+}/following' => [Module\Api\Mastodon\Accounts\Following::class, [R::GET ]], '/accounts/{id:\d+}/following' => [Module\Api\Mastodon\Accounts\Following::class, [R::GET ]],
'/accounts/{id:\d+}/lists' => [Module\Api\Mastodon\Accounts\Lists::class, [R::GET ]], '/accounts/{id:\d+}/lists' => [Module\Api\Mastodon\Accounts\Lists::class, [R::GET ]],
@ -77,6 +78,7 @@ return [
'/accounts/search' => [Module\Api\Mastodon\Accounts\Search::class, [R::GET ]], '/accounts/search' => [Module\Api\Mastodon\Accounts\Search::class, [R::GET ]],
'/accounts/update_credentials' => [Module\Api\Mastodon\Accounts\UpdateCredentials::class, [R::PATCH ]], '/accounts/update_credentials' => [Module\Api\Mastodon\Accounts\UpdateCredentials::class, [R::PATCH ]],
'/accounts/verify_credentials' => [Module\Api\Mastodon\Accounts\VerifyCredentials::class, [R::GET ]], '/accounts/verify_credentials' => [Module\Api\Mastodon\Accounts\VerifyCredentials::class, [R::GET ]],
'/accounts/{name}' => [Module\Api\Mastodon\Accounts::class, [R::GET ]],
'/admin/accounts' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported '/admin/accounts' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported
'/admin/accounts/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported '/admin/accounts/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported
'/admin/accounts/{id:\d+}/{action}' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported '/admin/accounts/{id:\d+}/{action}' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported
@ -108,6 +110,7 @@ return [
'/instance' => [Module\Api\Mastodon\Instance::class, [R::GET ]], '/instance' => [Module\Api\Mastodon\Instance::class, [R::GET ]],
'/instance/activity' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo '/instance/activity' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo
'/instance/peers' => [Module\Api\Mastodon\Instance\Peers::class, [R::GET ]], '/instance/peers' => [Module\Api\Mastodon\Instance\Peers::class, [R::GET ]],
'/instance/rules' => [Module\Api\Mastodon\Instance\Rules::class, [R::GET ]], // not supported
'/lists' => [Module\Api\Mastodon\Lists::class, [R::GET, R::POST]], '/lists' => [Module\Api\Mastodon\Lists::class, [R::GET, R::POST]],
'/lists/{id:\d+}' => [Module\Api\Mastodon\Lists::class, [R::GET, R::PUT, R::DELETE]], '/lists/{id:\d+}' => [Module\Api\Mastodon\Lists::class, [R::GET, R::PUT, R::DELETE]],
'/lists/{id:\d+}/accounts' => [Module\Api\Mastodon\Lists\Accounts::class, [R::GET, R::POST, R::DELETE]], '/lists/{id:\d+}/accounts' => [Module\Api\Mastodon\Lists\Accounts::class, [R::GET, R::POST, R::DELETE]],
@ -122,6 +125,7 @@ return [
'/polls/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported '/polls/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported
'/polls/{id:\d+}/votes' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported '/polls/{id:\d+}/votes' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported
'/preferences' => [Module\Api\Mastodon\Preferences::class, [R::GET ]], '/preferences' => [Module\Api\Mastodon\Preferences::class, [R::GET ]],
'/push/subscription' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST, R::PUT, R::DELETE]], // not supported
'/reports' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported '/reports' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not supported
'/scheduled_statuses' => [Module\Api\Mastodon\ScheduledStatuses::class, [R::GET ]], // Dummy, not supported '/scheduled_statuses' => [Module\Api\Mastodon\ScheduledStatuses::class, [R::GET ]], // Dummy, not supported
'/scheduled_statuses/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::PUT, R::DELETE]], // not supported '/scheduled_statuses/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::PUT, R::DELETE]], // not supported
@ -140,6 +144,7 @@ return [
'/statuses/{id:\d+}/unmute' => [Module\Api\Mastodon\Statuses\Unmute::class, [ R::POST]], '/statuses/{id:\d+}/unmute' => [Module\Api\Mastodon\Statuses\Unmute::class, [ R::POST]],
'/statuses/{id:\d+}/pin' => [Module\Api\Mastodon\Statuses\Pin::class, [ R::POST]], '/statuses/{id:\d+}/pin' => [Module\Api\Mastodon\Statuses\Pin::class, [ R::POST]],
'/statuses/{id:\d+}/unpin' => [Module\Api\Mastodon\Statuses\Unpin::class, [ R::POST]], '/statuses/{id:\d+}/unpin' => [Module\Api\Mastodon\Statuses\Unpin::class, [ R::POST]],
'/streaming' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]],
'/suggestions' => [Module\Api\Mastodon\Suggestions::class, [R::GET ]], '/suggestions' => [Module\Api\Mastodon\Suggestions::class, [R::GET ]],
'/suggestions/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], // not implemented '/suggestions/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], // not implemented
'/timelines/direct' => [Module\Api\Mastodon\Timelines\Direct::class, [R::GET ]], '/timelines/direct' => [Module\Api\Mastodon\Timelines\Direct::class, [R::GET ]],
@ -160,6 +165,7 @@ return [
'/followers/list' => [Module\Api\Twitter\FollowersList::class, [R::GET ]], '/followers/list' => [Module\Api\Twitter\FollowersList::class, [R::GET ]],
'/friends/ids' => [Module\Api\Twitter\FriendsIds::class, [R::GET ]], '/friends/ids' => [Module\Api\Twitter\FriendsIds::class, [R::GET ]],
'/friends/list' => [Module\Api\Twitter\FriendsList::class, [R::GET ]], '/friends/list' => [Module\Api\Twitter\FriendsList::class, [R::GET ]],
'/meta' => [Module\Api\Mastodon\Unimplemented::class, [R::POST ]], // not supported
'/oembed' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], '/oembed' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]],
'/proofs' => [Module\Api\Mastodon\Proofs::class, [R::GET ]], // Dummy, not supported '/proofs' => [Module\Api\Mastodon\Proofs::class, [R::GET ]], // Dummy, not supported
], ],

View file

@ -157,8 +157,8 @@ class ApiTest extends FixtureTest
*/ */
private function assertStatus(array $status = []) private function assertStatus(array $status = [])
{ {
self::assertInternalType('string', $status['text'] ?? ''); self::assertIsString($status['text'] ?? '');
self::assertInternalType('int', $status['id'] ?? ''); self::assertIsInt($status['id'] ?? '');
// We could probably do more checks here. // We could probably do more checks here.
} }
@ -171,9 +171,9 @@ class ApiTest extends FixtureTest
*/ */
private function assertList(array $list = []) private function assertList(array $list = [])
{ {
self::assertInternalType('string', $list['name']); self::assertIsString($list['name']);
self::assertInternalType('int', $list['id']); self::assertIsInt($list['id']);
self::assertInternalType('string', $list['id_str']); self::assertIsString('string', $list['id_str']);
self::assertContains($list['mode'], ['public', 'private']); self::assertContains($list['mode'], ['public', 'private']);
// We could probably do more checks here. // We could probably do more checks here.
} }
@ -762,7 +762,7 @@ class ApiTest extends FixtureTest
public function testApiRssExtraWithoutUserInfo() public function testApiRssExtraWithoutUserInfo()
{ {
$result = api_rss_extra($this->app, [], null); $result = api_rss_extra($this->app, [], null);
self::assertInternalType('array', $result['$user']); self::assertIsArray($result['$user']);
self::assertArrayHasKey('alternate', $result['$rss']); self::assertArrayHasKey('alternate', $result['$rss']);
self::assertArrayHasKey('self', $result['$rss']); self::assertArrayHasKey('self', $result['$rss']);
self::assertArrayHasKey('base', $result['$rss']); self::assertArrayHasKey('base', $result['$rss']);
@ -2355,7 +2355,7 @@ class ApiTest extends FixtureTest
public function testApiGetAttachmentsWithImage() public function testApiGetAttachmentsWithImage()
{ {
$body = '[img]http://via.placeholder.com/1x1.png[/img]'; $body = '[img]http://via.placeholder.com/1x1.png[/img]';
self::assertInternalType('array', api_get_attachments($body)); self::assertIsArray(api_get_attachments($body));
} }
/** /**
@ -2367,7 +2367,7 @@ class ApiTest extends FixtureTest
{ {
$_SERVER['HTTP_USER_AGENT'] = 'AndStatus'; $_SERVER['HTTP_USER_AGENT'] = 'AndStatus';
$body = '[img]http://via.placeholder.com/1x1.png[/img]'; $body = '[img]http://via.placeholder.com/1x1.png[/img]';
self::assertInternalType('array', api_get_attachments($body)); self::assertIsArray(api_get_attachments($body));
} }
/** /**
@ -2378,7 +2378,7 @@ class ApiTest extends FixtureTest
public function testApiGetEntitities() public function testApiGetEntitities()
{ {
$text = 'text'; $text = 'text';
self::assertInternalType('array', api_get_entitities($text, 'bbcode')); self::assertIsArray(api_get_entitities($text, 'bbcode'));
} }
/** /**
@ -2391,10 +2391,10 @@ class ApiTest extends FixtureTest
$_REQUEST['include_entities'] = 'true'; $_REQUEST['include_entities'] = 'true';
$text = 'text'; $text = 'text';
$result = api_get_entitities($text, 'bbcode'); $result = api_get_entitities($text, 'bbcode');
self::assertInternalType('array', $result['hashtags']); self::assertIsArray($result['hashtags']);
self::assertInternalType('array', $result['symbols']); self::assertIsArray($result['symbols']);
self::assertInternalType('array', $result['urls']); self::assertIsArray($result['urls']);
self::assertInternalType('array', $result['user_mentions']); self::assertIsArray($result['user_mentions']);
} }
/** /**
@ -2532,7 +2532,7 @@ class ApiTest extends FixtureTest
$result = api_account_rate_limit_status('json'); $result = api_account_rate_limit_status('json');
self::assertEquals(150, $result['hash']['remaining_hits']); self::assertEquals(150, $result['hash']['remaining_hits']);
self::assertEquals(150, $result['hash']['hourly_limit']); self::assertEquals(150, $result['hash']['hourly_limit']);
self::assertInternalType('int', $result['hash']['reset_time_in_seconds']); self::assertIsInt($result['hash']['reset_time_in_seconds']);
} }
/** /**