]> git.mxchange.org Git - friendica.git/commitdiff
Merge remote-tracking branch 'upstream/develop' into api-again
authorMichael <heluecht@pirati.ca>
Wed, 12 May 2021 09:15:27 +0000 (09:15 +0000)
committerMichael <heluecht@pirati.ca>
Wed, 12 May 2021 09:15:27 +0000 (09:15 +0000)
17 files changed:
src/DI.php
src/Factory/Api/Mastodon/Card.php [new file with mode: 0644]
src/Factory/Api/Mastodon/Error.php
src/Factory/Api/Mastodon/Status.php
src/Model/Item.php
src/Model/Post/Media.php
src/Module/Api/Mastodon/Apps.php
src/Module/Api/Mastodon/Lists/Accounts.php
src/Module/Api/Mastodon/Statuses/Context.php
src/Module/BaseApi.php
src/Module/OAuth/Acknowledge.php [new file with mode: 0644]
src/Module/OAuth/Authorize.php
src/Module/OAuth/Token.php
src/Object/Api/Mastodon/Card.php
src/Object/Api/Mastodon/Status.php
static/routes.config.php
view/theme/frio/css/style.css

index 37091a5ab82d5d439f525279975ef054fa508860..9e77943bda8403f707f4bb2216a321a78053af3b 100644 (file)
@@ -255,6 +255,14 @@ abstract class DI
                return self::$dice->create(Factory\Api\Mastodon\Attachment::class);
        }
 
+       /**
+        * @return Factory\Api\Mastodon\Card
+        */
+       public static function mstdnCard()
+       {
+               return self::$dice->create(Factory\Api\Mastodon\Card::class);
+       }
+
        /**
         * @return Factory\Api\Mastodon\Emoji
         */
diff --git a/src/Factory/Api/Mastodon/Card.php b/src/Factory/Api/Mastodon/Card.php
new file mode 100644 (file)
index 0000000..1505fa8
--- /dev/null
@@ -0,0 +1,80 @@
+<?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\Factory\Api\Mastodon;
+
+use Friendica\BaseFactory;
+use Friendica\Content\Text\BBCode;
+use Friendica\Model\Post;
+use Friendica\Network\HTTPException;
+use Friendica\Util\Strings;
+
+class Card extends BaseFactory
+{
+       /**
+        * @param int $uriId Uri-ID of the item
+        * @return \Friendica\Object\Api\Mastodon\Card
+        * @throws HTTPException\InternalServerErrorException
+        * @throws \ImagickException
+        */
+       public function createFromUriId(int $uriId)
+       {
+               $item = Post::selectFirst(['nody'], ['uri-id' => $uriId]);
+               if (!empty($item['body'])) {
+                       $data = BBCode::getAttachmentData($item['body']);
+               } else {
+                       $data = [];
+               }
+
+               foreach (Post\Media::getByURIId($uriId, [Post\Media::HTML]) as $attached) {
+                       if ((empty($data['url']) || Strings::compareLink($data['url'], $attached['url'])) &&
+                               (!empty($attached['description']) || !empty($attached['image']) || !empty($attached['preview']))) {
+                               $parts = parse_url($attached['url']);
+                               if (!empty($parts['scheme']) && !empty($parts['host'])) {
+                                       if (empty($attached['publisher-name'])) {
+                                               $attached['publisher-name'] = $parts['host'];
+                                       }
+                                       if (empty($attached['publisher-url']) || empty(parse_url($attached['publisher-url'], PHP_URL_SCHEME))) {
+                                               $attached['publisher-url'] = $parts['scheme'] . '://' . $parts['host'];
+
+                                               if (!empty($parts['port'])) {
+                                                       $attached['publisher-url'] .= ':' . $parts['port'];
+                                               }
+                                       }
+                               }
+
+                               $data['url']           = $attached['url'];
+                               $data['title']         = $attached['name'];
+                               $data['description']   = $attached['description'];
+                               $data['type']          = 'link';
+                               $data['author_name']   = $attached['author-name'];
+                               $data['author_url']    = $attached['author-url'];
+                               $data['provider_name'] = $attached['publisher-name'];
+                               $data['provider_url']  = $attached['publisher-url'];
+                               $data['image']         = $attached['preview'];
+                               $data['width']         = $attached['preview-width'];
+                               $data['height']        = $attached['preview-height'];
+                       }
+               }
+
+               return new \Friendica\Object\Api\Mastodon\Card($data);
+       }
+}
index 2c3662288ea6363c7b06ca79a07253b7182b85e4..086cceef98e55ca31e7794deec0c30f6bc038d72 100644 (file)
@@ -35,4 +35,31 @@ class Error extends BaseFactory
 
                System::jsonError(404, $errorobj->toArray());
        }
+
+       public function UnprocessableEntity(string $error = '')
+       {
+               $error = $error ?: DI::l10n()->t('Unprocessable Entity');
+               $error_description = '';
+               $errorobj = New \Friendica\Object\Api\Mastodon\Error($error, $error_description);
+
+               System::jsonError(422, $errorobj->toArray());
+       }
+
+       public function Unauthorized(string $error = '')
+       {
+               $error = $error ?: DI::l10n()->t('Unauthorized');
+               $error_description = '';
+               $errorobj = New \Friendica\Object\Api\Mastodon\Error($error, $error_description);
+
+               System::jsonError(401, $errorobj->toArray());
+       }
+
+       public function InternalError(string $error = '')
+       {
+               $error = $error ?: DI::l10n()->t('Internal Server Error');
+               $error_description = '';
+               $errorobj = New \Friendica\Object\Api\Mastodon\Error($error, $error_description);
+
+               System::jsonError(500, $errorobj->toArray());
+       }
 }
index 310fa26bbea354f0c900bc892885aa7a030e6a7a..866cfe8027af2684cf09c47082394e75c28a650a 100644 (file)
@@ -86,14 +86,28 @@ class Status extends BaseFactory
 
                $sensitive = DBA::exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw']);
                $application = new \Friendica\Object\Api\Mastodon\Application($item['app'] ?: ContactSelector::networkToName($item['network'], $item['author-link']));
-               $mentions = DI::mstdnMention()->createFromUriId($uriId);
-               $tags = DI::mstdnTag()->createFromUriId($uriId);
-
-               $data = BBCode::getAttachmentData($item['body']);
-               $card = new \Friendica\Object\Api\Mastodon\Card($data);
 
+               $mentions    = DI::mstdnMention()->createFromUriId($uriId);
+               $tags        = DI::mstdnTag()->createFromUriId($uriId);
+               $card        = DI::mstdnCard()->createFromUriId($uriId);
                $attachments = DI::mstdnAttachment()->createFromUriId($uriId);
 
+               $shared = BBCode::fetchShareAttributes($item['body']);
+               if (!empty($shared['guid'])) {
+                       $shared_item = Post::selectFirst(['uri-id', 'plink'], ['guid' => $shared['guid']]);
+
+                       $shared_uri_id = $shared_item['uri-id'] ?? 0;
+
+                       $mentions    = array_merge($mentions, DI::mstdnMention()->createFromUriId($shared_uri_id));
+                       $tags        = array_merge($tags, DI::mstdnTag()->createFromUriId($shared_uri_id));
+                       $attachments = array_merge($attachments, DI::mstdnAttachment()->createFromUriId($shared_uri_id));
+
+                       if (empty($card->toArray())) {
+                               $card = DI::mstdnCard()->createFromUriId($shared_uri_id);
+                       }
+               }
+
+
                if ($item['vid'] == Verb::getID(Activity::ANNOUNCE)) {
                        $reshare = $this->createFromUriId($item['thr-parent-id'], $uid)->toArray();
                        $reshared_item = Post::selectFirst(['title', 'body'], ['uri-id' => $item['thr-parent-id'], 'uid' => [0, $uid]]);
index b13224dfa229c397f811c6f6cb1da3c917867b7c..2ce05589de4b655ca5295cb7d225178c5b0c27c2 100644 (file)
@@ -2927,7 +2927,7 @@ class Item
                DI::profiler()->saveTimestamp($stamp1, 'rendering');
 
                if (isset($data['url']) && !in_array($data['url'], $ignore_links)) {
-                       if (!empty($data['description']) || !empty($data['image'] || !empty($data['preview']))) {
+                       if (!empty($data['description']) || !empty($data['image']) || !empty($data['preview'])) {
                                $parts = parse_url($data['url']);
                                if (!empty($parts['scheme']) && !empty($parts['host'])) {
                                        if (empty($data['provider_name'])) {
index 6f78b09051a7de8f838db6e6ef4d14868f0778ad..c3f745fee64fc9b020f4ba7ce2c8eb5132ead5e9 100644 (file)
@@ -288,9 +288,13 @@ class Media
        public static function insertFromBody(int $uriid, string $body)
        {
                // Simplify image codes
-               $body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
+               $unshared_body = $body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
 
-               $unshared_body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
+               // Only remove the shared data from "real" reshares
+               $shared = BBCode::fetchShareAttributes($body);
+               if (!empty($shared['guid'])) {
+                       $unshared_body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
+               }
 
                $attachments = [];
                if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
@@ -363,8 +367,12 @@ class Media
         */
        public static function insertFromRelevantUrl(int $uriid, string $body)
        {
-               // Don't look at the shared content
-               $body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
+               // Only remove the shared data from "real" reshares
+               $shared = BBCode::fetchShareAttributes($body);
+               if (!empty($shared['guid'])) {
+                       // Don't look at the shared content
+                       $body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
+               }
 
                // Remove all hashtags and mentions
                $body = preg_replace("/([#@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", '', $body);
index 3d06cdbb05cc56012d8e00af90ae60692948ad77..0f0837c784042ed0d523452147ff734c93f17b94 100644 (file)
@@ -43,7 +43,7 @@ class Apps extends BaseApi
                $website  = !isset($_REQUEST['website']) ? '' : $_REQUEST['website'];
 
                if (empty($name) || empty($redirect)) {
-                       DI::mstdnError()->RecordNotFound();
+                       DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Missing parameters'));
                }
 
                $client_id     = bin2hex(random_bytes(32));
@@ -60,7 +60,7 @@ class Apps extends BaseApi
                }
 
                if (!DBA::insert('application', $fields)) {
-                       DI::mstdnError()->RecordNotFound();
+                       DI::mstdnError()->InternalError();
                }
 
                System::jsonExit(DI::mstdnApplication()->createFromApplicationId(DBA::lastInsertId()));
index 0b817cfc82abb8481e934ae2f778f645066bed4a..513ca21d71c16aa9efb9db7e7bc92ae7f88d09ff 100644 (file)
@@ -65,12 +65,18 @@ class Accounts extends BaseApi
                $max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id'];
                // Return results newer than this id
                $since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id'];
-               // Maximum number of results to return. Defaults to 20.
+               // Maximum number of results. Defaults to 40. Max 40.
+               // Set to 0 in order to get all accounts without pagination.
                $limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit'];
 
 
-               $params = ['order' => ['contact-id' => true], 'limit' => $limit];
+               $params = ['order' => ['contact-id' => true]];
 
+               if ($limit != 0) {
+                       $params['limit'] = $limit;
+
+               }
+       
                $condition = ['gid' => $id];
 
                if (!empty($max_id)) {
@@ -87,6 +93,8 @@ class Accounts extends BaseApi
                        $params['order'] = ['contact-id'];
                }
 
+               $accounts = [];
+
                $members = DBA::select('group_member', ['contact-id'], $condition, $params);
                while ($member = DBA::fetch($members)) {
                        $accounts[] = DI::mstdnAccount()->createFromContactId($member['contact-id'], $uid);
index bf49d265ca777804311e6a077236a6e879e0e09e..5615e41cc94d4b7291ac31426afa313d589c36b8 100644 (file)
@@ -54,7 +54,8 @@ class Context extends BaseApi
                $parents  = [];
                $children = [];
 
-               $posts = Post::select(['uri-id', 'thr-parent-id'], ['parent-uri-id' => $parent['parent-uri-id']], [], false);
+               $posts = Post::select(['uri-id', 'thr-parent-id'],
+                       ['parent-uri-id' => $parent['parent-uri-id'], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]], [], false);
                while ($post = Post::fetch($posts)) {
                        if ($post['uri-id'] == $post['thr-parent-id']) {
                                continue;
@@ -67,12 +68,24 @@ class Context extends BaseApi
 
                $statuses = ['ancestors' => [], 'descendants' => []];
 
+               $ancestors = [];
                foreach (self::getParents($id, $parents) as $ancestor) {
-                       $statuses['ancestors'][] = DI::mstdnStatus()->createFromUriId($ancestor, $uid);
+                       $ancestors[$ancestor] = DI::mstdnStatus()->createFromUriId($ancestor, $uid);
                }
 
+               ksort($ancestors);
+               foreach ($ancestors as $ancestor) {
+                       $statuses['ancestors'][] = $ancestor;
+               }
+
+               $descendants = [];
                foreach (self::getChildren($id, $children) as $descendant) {
-                       $statuses['descendants'][] = DI::mstdnStatus()->createFromUriId($descendant, $uid);
+                       $descendants[] = DI::mstdnStatus()->createFromUriId($descendant, $uid);
+               }
+
+               ksort($descendants);
+               foreach ($descendants as $descendant) {
+                       $statuses['descendants'][] = $descendant;
                }
 
                System::jsonExit($statuses);
index f7983202625256a856ce10966ab14328af286348..d2240fcd68b231886b272756d7cca0d46148bc1a 100644 (file)
@@ -138,15 +138,13 @@ class BaseApi extends BaseModule
         */
        protected static function login()
        {
-               $authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
-               $authorization = $_SERVER['AUTHORIZATION'] ?? $authorization;
-
-               if (self::checkBearer($authorization)) {
-                       self::$current_user_id = self::getUserByBearer($authorization);
-                       return (bool)self::$current_user_id;
+               if (empty(self::$current_user_id)) {
+                       self::$current_user_id = self::getUserByBearer();
                }
 
-               api_login(DI::app());
+               if (empty(self::$current_user_id)) {
+                       api_login(DI::app());
+               }
 
                self::$current_user_id = api_user();
 
@@ -160,15 +158,11 @@ class BaseApi extends BaseModule
         */
        protected static function getCurrentUserID()
        {
-               $authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
-               $authorization = $_SERVER['AUTHORIZATION'] ?? $authorization;
-
-               if (self::checkBearer($authorization)) {
-                       self::$current_user_id = self::getUserByBearer($authorization);
-                       return (int)self::$current_user_id;
+               if (empty(self::$current_user_id)) {
+                       self::$current_user_id = self::getUserByBearer();
                }
 
-               if (is_null(self::$current_user_id)) {
+               if (empty(self::$current_user_id)) {
                        api_login(DI::app(), false);
 
                        self::$current_user_id = api_user();
@@ -177,14 +171,16 @@ class BaseApi extends BaseModule
                return (int)self::$current_user_id;
        }
 
-       private static function checkBearer(string $authorization)
+       private static function getUserByBearer()
        {
-               return (substr($authorization, 0, 7) == 'Bearer ');
-       }
+               $authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
+               $authorization = $_SERVER['AUTHORIZATION'] ?? $authorization;
 
-       private static function getUserByBearer(string $authorization)
-       {
-               $bearer = trim(substr($authorization, 6));
+               if (substr($authorization, 0, 7) != 'Bearer ') {
+                       return 0;
+               }
+
+               $bearer = trim(substr($authorization, 7));
                $condition = ['access_token' => $bearer];
                $token = DBA::selectFirst('application-token', ['uid'], $condition);
                if (!DBA::isResult($token)) {
@@ -197,15 +193,23 @@ class BaseApi extends BaseModule
 
        public static function getApplication()
        {
-               $redirect_uri = !isset($_REQUEST['redirect_uri']) ? '' : $_REQUEST['redirect_uri'];
-               $client_id    = !isset($_REQUEST['client_id']) ? '' : $_REQUEST['client_id'];
+               $redirect_uri  = !isset($_REQUEST['redirect_uri']) ? '' : $_REQUEST['redirect_uri'];
+               $client_id     = !isset($_REQUEST['client_id']) ? '' : $_REQUEST['client_id'];
+               $client_secret = !isset($_REQUEST['client_secret']) ? '' : $_REQUEST['client_secret'];
 
-               if (empty($redirect_uri) || empty($client_id)) {
-                       Logger::warning('Incomplete request');
+               if ((empty($redirect_uri) && empty($client_secret)) || empty($client_id)) {
+                       Logger::warning('Incomplete request', ['request' => $_REQUEST]);
                        return [];
                }
 
-               $condition = ['redirect_uri' => $redirect_uri, 'client_id' => $client_id];
+               $condition = ['client_id' => $client_id];
+               if (!empty($client_secret)) {
+                       $condition['client_secret'] = $client_secret;
+               }
+               if (!empty($redirect_uri)) {
+                       $condition['redirect_uri'] = $redirect_uri;
+               }
+
                $application = DBA::selectFirst('application', [], $condition);
                if (!DBA::isResult($application)) {
                        Logger::warning('Application not found', $condition);
@@ -214,7 +218,17 @@ class BaseApi extends BaseModule
                return $application;
        }
 
+       public static function existsTokenForUser(array $application, int $uid)
+       {
+               return DBA::exists('application-token', ['application-id' => $application['id'], 'uid' => $uid]);
+       }
+
        public static function getTokenForUser(array $application, int $uid)
+       {
+               return DBA::selectFirst('application-token', [], ['application-id' => $application['id'], 'uid' => $uid]);
+       }
+
+       public static function createTokenForUser(array $application, int $uid)
        {
                $code         = bin2hex(random_bytes(32));
                $access_token = bin2hex(random_bytes(32));
@@ -226,6 +240,7 @@ class BaseApi extends BaseModule
 
                return DBA::selectFirst('application-token', [], ['application-id' => $application['id'], 'uid' => $uid]);
        }
+
        /**
         * Get user info array.
         *
diff --git a/src/Module/OAuth/Acknowledge.php b/src/Module/OAuth/Acknowledge.php
new file mode 100644 (file)
index 0000000..617ab6c
--- /dev/null
@@ -0,0 +1,55 @@
+<?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\OAuth;
+
+use Friendica\Core\Logger;
+use Friendica\Core\Renderer;
+use Friendica\DI;
+use Friendica\Module\BaseApi;
+
+/**
+ * Dummy class for all currently unimplemented endpoints
+ */
+class Acknowledge extends BaseApi
+{
+       public static function post(array $parameters = [])
+       {
+               DI::session()->set('oauth_acknowledge', true);
+               DI::app()->redirect(DI::session()->get('return_path'));
+       }
+
+       public static function content(array $parameters = [])
+       {
+               DI::session()->set('return_path', $_REQUEST['return_path'] ?? '');
+
+               $tpl = Renderer::getMarkupTemplate('oauth_authorize.tpl');
+               $o = Renderer::replaceMacros($tpl, [
+                       '$title' => DI::l10n()->t('Authorize application connection'),
+                       '$app' => ['name' => $_REQUEST['application'] ?? ''],
+                       '$authorize' => DI::l10n()->t('Do you want to authorize this application to access your posts and contacts, and/or create new posts for you?'),
+                       '$yes' => DI::l10n()->t('Yes'),
+                       '$no' => DI::l10n()->t('No'),
+               ]);
+
+               return $o;
+       }
+}
index c2e8cc30a31b983e8466ed4b45b7745083991a07..ffa7255329e345ac14b2592dda2c8a5cfe1c98ad 100644 (file)
@@ -36,8 +36,6 @@ class Authorize extends BaseApi
         */
        public static function rawContent(array $parameters = [])
        {
-               //return;
-
                $response_type = !isset($_REQUEST['response_type']) ? '' : $_REQUEST['response_type'];
                if ($response_type != 'code') {
                        Logger::warning('Wrong or missing response type', ['response_type' => $response_type]);
@@ -49,15 +47,26 @@ class Authorize extends BaseApi
                        DI::mstdnError()->RecordNotFound();
                }
 
+               $request = $_REQUEST;
+               unset($request['pagename']);
+               $redirect = urlencode('oauth/authorize?' . http_build_query($request));
+
                $uid = local_user();
                if (empty($uid)) {
                        Logger::info('Redirect to login');
-                       DI::app()->redirect('login?return_path=/oauth/authorize');
+                       DI::app()->redirect('login?return_path=' . $redirect);
                } else {
                        Logger::info('Already logged in user', ['uid' => $uid]);
                }
 
-               $token = self::getTokenForUser($application, $uid);
+               if (!self::existsTokenForUser($application, $uid) && !DI::session()->get('oauth_acknowledge')) {
+                       Logger::info('Redirect to acknowledge');
+                       DI::app()->redirect('oauth/acknowledge?return_path=' . $redirect);
+               }
+
+               DI::session()->remove('oauth_acknowledge');
+
+               $token = self::createTokenForUser($application, $uid);
                if (!$token) {
                        DI::mstdnError()->RecordNotFound();
                }
index 6e574a8af199475e6318d48bfac3528f9aa2c3f2..44a86263044eb8017ed02c9024d334eabd670756 100644 (file)
@@ -39,26 +39,26 @@ class Token extends BaseApi
                $grant_type    = !isset($_REQUEST['grant_type']) ? '' : $_REQUEST['grant_type'];
 
                if ($grant_type != 'authorization_code') {
-                       Logger::warning('Wrong or missing grant type', ['grant_type' => $grant_type]);
-                       DI::mstdnError()->RecordNotFound();
+                       Logger::warning('Unsupported or missing grant type', ['request' => $_REQUEST]);
+                       DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Unsupported or missing grant type'));
                }
 
                $application = self::getApplication();
                if (empty($application)) {
-                       DI::mstdnError()->RecordNotFound();
+                       DI::mstdnError()->UnprocessableEntity();
                }
 
                if ($application['client_secret'] != $client_secret) {
                        Logger::warning('Wrong client secret', $client_secret);
-                       DI::mstdnError()->RecordNotFound();
+                       DI::mstdnError()->Unauthorized();
                }
 
                $condition = ['application-id' => $application['id'], 'code' => $code];
 
-               $token = DBA::selectFirst('application-token', ['access_token'], $condition);
+               $token = DBA::selectFirst('application-token', ['access_token', 'created_at'], $condition);
                if (!DBA::isResult($token)) {
                        Logger::warning('Token not found', $condition);
-                       DI::mstdnError()->RecordNotFound();
+                       DI::mstdnError()->Unauthorized();
                }
 
                // @todo Use entity class
index 6abf58fdc817b83422c75aab03936c567703e3d3..b18ba28e44cc0b83c2a10bc9beedc340406467b9 100644 (file)
@@ -39,9 +39,17 @@ class Card extends BaseDataTransferObject
        /** @var string */
        protected $type;
        /** @var string */
+       protected $author_name;
+       /** @var string */
+       protected $author_url;
+       /** @var string */
        protected $provider_name;
        /** @var string */
        protected $provider_url;
+       /** @var int */
+       protected $width;
+       /** @var int */
+       protected $height;
        /** @var string */
        protected $image;
 
@@ -57,9 +65,13 @@ class Card extends BaseDataTransferObject
                $this->title         = $attachment['title'] ?? '';
                $this->description   = $attachment['description'] ?? '';
                $this->type          = $attachment['type'] ?? '';
-               $this->image         = $attachment['image'] ?? '';
+               $this->author_name   = $attachment['author_name'] ?? '';
+               $this->author_url    = $attachment['author_url'] ?? '';
                $this->provider_name = $attachment['provider_name'] ?? '';
                $this->provider_url  = $attachment['provider_url'] ?? '';
+               $this->width         = $attachment['width'] ?? 0;
+               $this->height        = $attachment['height'] ?? 0;
+               $this->image         = $attachment['image'] ?? '';
        }
 
        /**
index 26c0705bd16a6570ea407e208e8a94eb19a378a8..227395d171bc8a6d29e8f1f36274a4f5745dea1c 100644 (file)
@@ -134,7 +134,7 @@ class Status extends BaseDataTransferObject
                $this->mentions = $mentions;
                $this->tags = $tags;
                $this->emojis = [];
-               //$this->card = $card;
+               $this->card = $card->toArray() ?: null;
                $this->poll = null;
        }
 
index f666feeff55bd07f1e38e248207dc1db47fea487..9d8e1fda4277b8783cb34dc6a972f9f9a2e10ed7 100644 (file)
@@ -331,9 +331,14 @@ return [
                '/mark/all' => [Module\Notifications\Notification::class, [R::GET]],
                '/{id:\d+}' => [Module\Notifications\Notification::class, [R::GET, R::POST]],
        ],
-       '/oauth/authorize'             => [Module\OAuth\Authorize::class, [R::GET]],
-       '/oauth/revoke'                => [Module\OAuth\Revoke::class, [R::POST]],
-       '/oauth/token'                 => [Module\OAuth\Token::class, [R::POST]],
+
+       '/oauth' => [
+               '/acknowledge' => [Module\OAuth\Acknowledge::class, [R::GET, R::POST]],
+               '/authorize'   => [Module\OAuth\Authorize::class,   [R::GET]],
+               '/revoke'      => [Module\OAuth\Revoke::class,      [R::POST]],
+               '/token'       => [Module\OAuth\Token::class,       [R::POST]],
+       ],
+
        '/objects/{guid}[/{activity}]' => [Module\Objects::class, [R::GET]],
 
        '/oembed'         => [
index 8ad5c62742215d2cb45d9b0f5d1614b3c2c933c4..4fb504714134de317b44f37a3087572ce05885e4 100644 (file)
@@ -3410,6 +3410,7 @@ section .profile-match-wrapper {
 .mod-home.is-not-singleuser nav.navbar,
 .mod-login nav.navbar {
        background-color: transparent;
+       position: inherit;
 }
 .mod-home.is-not-singleuser #topbar-second,
 .mod-login #topbar-second {