<?php
+/**
+ * @copyright Copyright (C) 2010-2022, 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;
use Friendica\App\Arguments;
+use Friendica\App\BaseURL;
use Friendica\Core\L10n;
-use Friendica\Core\Logger;
-use Friendica\Core\System;
-use Friendica\DI;
-use Friendica\Object\Api\Mastodon\Error;
+use Friendica\Module\Response;
use Friendica\Util\Arrays;
-use Friendica\Util\HTTPInputData;
+use Friendica\Util\DateTimeFormat;
use Friendica\Util\XML;
+use Psr\Log\LoggerInterface;
+use Friendica\Factory\Api\Twitter\User as TwitterUser;
/**
- * This class is used to format and return API responses
+ * This class is used to format and create API responses
*/
-class ApiResponse
+class ApiResponse extends Response
{
/** @var L10n */
protected $l10n;
/** @var Arguments */
protected $args;
+ /** @var LoggerInterface */
+ protected $logger;
+ /** @var BaseURL */
+ protected $baseUrl;
+ /** @var TwitterUser */
+ protected $twitterUser;
- /**
- * @param L10n $l10n
- * @param Arguments $args
- */
- public function __construct(L10n $l10n, Arguments $args)
+ public function __construct(L10n $l10n, Arguments $args, LoggerInterface $logger, BaseURL $baseUrl, TwitterUser $twitterUser)
{
- $this->l10n = $l10n;
- $this->args = $args;
+ $this->l10n = $l10n;
+ $this->args = $args;
+ $this->logger = $logger;
+ $this->baseUrl = $baseUrl;
+ $this->twitterUser = $twitterUser;
}
/**
*
* @return string The XML data
*/
- public static function createXML(array $data, string $root_element): string
+ public function createXML(array $data, string $root_element): string
{
$childname = key($data);
$data2 = array_pop($data);
- $namespaces = ['' => 'http://api.twitter.com',
- 'statusnet' => 'http://status.net/schema/api/1/',
- 'friendica' => 'http://friendi.ca/schema/api/1/',
- 'georss' => 'http://www.georss.org/georss'];
+ $namespaces = [
+ '' => 'http://api.twitter.com',
+ 'statusnet' => 'http://status.net/schema/api/1/',
+ 'friendica' => 'http://friendi.ca/schema/api/1/',
+ 'georss' => 'http://www.georss.org/georss'
+ ];
/// @todo Auto detection of needed namespaces
if (in_array($root_element, ['ok', 'hash', 'config', 'version', 'ids', 'notes', 'photos'])) {
return XML::fromArray($data3, $xml, false, $namespaces);
}
+ /**
+ * Set values for RSS template
+ *
+ * @param array $arr Array to be passed to template
+ * @param int $cid Contact ID of template
+ * @return array
+ */
+ private function addRSSValues(array $arr, int $cid)
+ {
+ if (empty($cid)) {
+ return $arr;
+ }
+
+ $user_info = $this->twitterUser->createFromContactId($cid)->toArray();
+
+ $arr['$user'] = $user_info;
+ $arr['$rss'] = [
+ 'alternate' => $user_info['url'],
+ 'self' => $this->baseUrl . '/' . $this->args->getQueryString(),
+ 'base' => $this->baseUrl,
+ 'updated' => DateTimeFormat::utcNow(DateTimeFormat::API),
+ 'atom_updated' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
+ 'language' => $user_info['lang'],
+ 'logo' => $this->baseUrl . '/images/friendica-32.png',
+ ];
+
+ return $arr;
+ }
+
/**
* Formats the data according to the data type
*
* @param string $root_element Name of the root element
* @param string $type Return type (atom, rss, xml, json)
* @param array $data JSON style array
+ * @param int $cid ID of the contact for RSS
*
* @return array|string (string|array) XML data or JSON data
*/
- public static function formatData(string $root_element, string $type, array $data)
+ public function formatData(string $root_element, string $type, array $data, int $cid = 0)
{
switch ($type) {
- case 'atom':
case 'rss':
+ $data = $this->addRSSValues($data, $cid);
+ case 'atom':
case 'xml':
- $ret = static::createXML($data, $root_element);
- break;
+ return $this->createXML($data, $root_element);
case 'json':
default:
- $ret = $data;
- break;
+ return $data;
}
- return $ret;
}
/**
*
* @return void
*/
- public static function error(int $code, string $description, string $message, string $format = null)
+ public function error(int $code, string $description, string $message, string $format = null)
{
$error = [
'error' => $message ?: $description,
'code' => $code . ' ' . $description,
- 'request' => DI::args()->getQueryString()
+ 'request' => $this->args->getQueryString()
];
- header(($_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1') . ' ' . $code . ' ' . $description);
+ $this->setHeader(($_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1') . ' ' . $code . ' ' . $description);
- self::exit('status', ['status' => $error], $format);
+ $this->exit('status', ['status' => $error], $format);
}
/**
*
* @return void
*/
- public static function exit(string $root_element, array $data, string $format = null)
+ public function exit(string $root_element, array $data, string $format = null, int $cid = 0)
{
$format = $format ?? 'json';
- $return = static::formatData($root_element, $format, $data);
+ $return = $this->formatData($root_element, $format, $data, $cid);
switch ($format) {
case 'xml':
- header('Content-Type: text/xml');
+ $this->setType(static::TYPE_XML);
break;
case 'json':
- header('Content-Type: application/json');
+ $this->setType(static::TYPE_JSON);
if (!empty($return)) {
$json = json_encode(end($return));
if (!empty($_GET['callback'])) {
}
break;
case 'rss':
- header('Content-Type: application/rss+xml');
- $return = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $return;
+ $this->setType(static::TYPE_RSS);
break;
case 'atom':
- header('Content-Type: application/atom+xml');
- $return = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $return;
+ $this->setType(static::TYPE_ATOM);
break;
}
- echo $return;
- exit;
+ $this->addContent($return);
+ }
+
+ /**
+ * Wrapper around exit() for JSON only responses
+ *
+ * @param array $data
+ */
+ public function exitWithJson(array $data)
+ {
+ $this->exit('content', ['content' => $data], static::TYPE_JSON);
}
/**
* Quit execution with the message that the endpoint isn't implemented
*
* @param string $method
+ * @param array $request (optional) The request content of the current call for later analysis
*
* @return void
* @throws \Exception
*/
- public static function unsupported(string $method = 'all')
+ public function unsupported(string $method = 'all', array $request = [])
{
- $path = DI::args()->getQueryString();
- Logger::info('Unimplemented API call',
+ $path = $this->args->getQueryString();
+ $this->logger->info('Unimplemented API call',
[
'method' => $method,
'path' => $path,
'agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
- 'request' => HTTPInputData::process()
+ 'request' => $request,
]);
- $error = DI::l10n()->t('API endpoint %s %s is not implemented', strtoupper($method), $path);
- $error_description = DI::l10n()->t('The API endpoint is currently not implemented but might be in the future.');
- $errorobj = new Error($error, $error_description);
- System::jsonError(501, $errorobj->toArray());
+ $error = $this->l10n->t('API endpoint %s %s is not implemented', strtoupper($method), $path);
+ $error_description = $this->l10n->t('The API endpoint is currently not implemented but might be in the future.');
+
+ $this->exit('error', ['error' => ['error' => $error, 'error_description' => $error_description]]);
}
}