2016-09-25 16:50:08 +00:00
< ? php
/**
2021-03-29 06:40:20 +00:00
* @ copyright Copyright ( C ) 2010 - 2021 , the Friendica project
2020-02-09 15:34:23 +00:00
*
* @ 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 />.
*
2016-09-25 16:50:08 +00:00
* Friendica implementation of statusnet / twitter API
*
2017-12-24 02:20:50 +00:00
* @ file include / api . php
2016-09-25 16:50:08 +00:00
* @ todo Automatically detect if incoming data is HTML or BBCode
*/
2018-01-25 02:08:45 +00:00
2017-04-30 04:07:00 +00:00
use Friendica\App ;
2018-01-10 03:42:04 +00:00
use Friendica\Content\ContactSelector ;
2018-01-27 01:01:32 +00:00
use Friendica\Content\Text\BBCode ;
2018-03-07 21:24:13 +00:00
use Friendica\Content\Text\HTML ;
2018-10-29 21:20:46 +00:00
use Friendica\Core\Logger ;
2018-07-01 18:42:38 +00:00
use Friendica\Core\Protocol ;
2018-01-25 02:08:45 +00:00
use Friendica\Core\System ;
2018-07-20 12:19:26 +00:00
use Friendica\Database\DBA ;
2019-12-15 22:28:01 +00:00
use Friendica\DI ;
2017-12-07 14:04:24 +00:00
use Friendica\Model\Contact ;
2017-12-09 18:45:17 +00:00
use Friendica\Model\Group ;
2018-01-25 02:08:45 +00:00
use Friendica\Model\Item ;
2018-01-15 17:14:09 +00:00
use Friendica\Model\Mail ;
2021-01-23 09:53:44 +00:00
use Friendica\Model\Notification ;
2017-12-07 13:56:11 +00:00
use Friendica\Model\Photo ;
2021-01-16 04:11:28 +00:00
use Friendica\Model\Post ;
2021-06-15 11:12:44 +00:00
use Friendica\Model\Profile ;
2017-11-26 19:46:08 +00:00
use Friendica\Model\User ;
2020-05-26 05:18:50 +00:00
use Friendica\Model\Verb ;
2021-11-08 21:35:41 +00:00
use Friendica\Module\BaseApi ;
2017-11-24 04:48:15 +00:00
use Friendica\Network\HTTPException ;
use Friendica\Network\HTTPException\BadRequestException ;
use Friendica\Network\HTTPException\ForbiddenException ;
use Friendica\Network\HTTPException\InternalServerErrorException ;
use Friendica\Network\HTTPException\MethodNotAllowedException ;
use Friendica\Network\HTTPException\NotFoundException ;
use Friendica\Network\HTTPException\TooManyRequestsException ;
2018-01-25 02:08:45 +00:00
use Friendica\Network\HTTPException\UnauthorizedException ;
2017-12-07 13:56:11 +00:00
use Friendica\Object\Image ;
2019-10-23 22:25:43 +00:00
use Friendica\Protocol\Activity ;
2021-11-08 21:35:41 +00:00
use Friendica\Security\BasicAuth ;
2021-06-08 06:32:24 +00:00
use Friendica\Security\OAuth ;
2018-01-27 02:38:34 +00:00
use Friendica\Util\DateTimeFormat ;
2019-10-18 01:26:15 +00:00
use Friendica\Util\Images ;
2018-01-27 04:09:48 +00:00
use Friendica\Util\Network ;
2018-11-08 13:45:46 +00:00
use Friendica\Util\Strings ;
2017-01-17 19:21:46 +00:00
2019-09-16 12:47:49 +00:00
require_once __DIR__ . '/../mod/item.php' ;
require_once __DIR__ . '/../mod/wall_upload.php' ;
2017-04-05 20:07:55 +00:00
define ( 'API_METHOD_ANY' , '*' );
define ( 'API_METHOD_GET' , 'GET' );
define ( 'API_METHOD_POST' , 'POST,PUT' );
define ( 'API_METHOD_DELETE' , 'POST,DELETE' );
2018-12-30 20:42:56 +00:00
define ( 'API_LOG_PREFIX' , 'API {action} - ' );
2018-01-15 13:05:12 +00:00
$API = [];
2018-04-24 14:16:57 +00:00
$called_api = [];
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Auth API user
*
2017-11-10 05:00:50 +00:00
* It is not sufficient to use local_user () to check whether someone is allowed to use the API ,
* because this will open CSRF holes ( just embed an image with src = friendicasite . com / api / statuses / update ? status = CSRF
* into a page , and visitors will post something without noticing it ) .
*/
function api_user ()
{
2021-11-17 21:50:39 +00:00
$user = OAuth :: getCurrentUserID ();
2021-06-02 22:11:21 +00:00
if ( ! empty ( $user )) {
2021-06-11 03:56:06 +00:00
return $user ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-06-11 06:02:56 +00:00
if ( ! empty ( $_SESSION [ 'allow_api' ])) {
return local_user ();
}
2017-11-10 05:00:50 +00:00
return false ;
}
/**
2020-01-19 06:05:23 +00:00
* Get source name from API client
*
2017-11-10 05:00:50 +00:00
* Clients can send 'source' parameter to be show in post metadata
* as " sent via <source> " .
* Some clients doesn ' t send a source param , we support ones we know
* ( only Twidere , atm )
*
* @ return string
2019-01-07 17:24:01 +00:00
* Client source name , default to " api " if unset / unknown
* @ throws Exception
2017-11-10 05:00:50 +00:00
*/
function api_source ()
{
2021-06-11 06:09:08 +00:00
if ( requestdata ( 'source' )) {
return requestdata ( 'source' );
2016-09-25 16:50:08 +00:00
}
2021-06-11 06:09:08 +00:00
// Support for known clients that doesn't send a source name
if ( ! empty ( $_SERVER [ 'HTTP_USER_AGENT' ])) {
if ( strpos ( $_SERVER [ 'HTTP_USER_AGENT' ], " Twidere " ) !== false ) {
return " Twidere " ;
}
Logger :: info ( API_LOG_PREFIX . 'Unrecognized user-agent' , [ 'module' => 'api' , 'action' => 'source' , 'http_user_agent' => $_SERVER [ 'HTTP_USER_AGENT' ]]);
} else {
Logger :: info ( API_LOG_PREFIX . 'Empty user-agent' , [ 'module' => 'api' , 'action' => 'source' ]);
}
return " api " ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Format date for API
2017-11-10 05:00:50 +00:00
*
* @ param string $str Source date , as UTC
* @ return string Date in UTC formatted as " D M d H:i:s +0000 Y "
2019-01-07 17:24:01 +00:00
* @ throws Exception
2017-11-10 05:00:50 +00:00
*/
function api_date ( $str )
{
// Wed May 23 06:01:13 +0000 2007
2018-01-27 02:38:34 +00:00
return DateTimeFormat :: utc ( $str , " D M d H:i:s +0000 Y " );
2017-11-10 05:00:50 +00:00
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
/**
2017-12-24 02:20:50 +00:00
* Register a function to be the endpoint for defined API path .
2017-11-10 05:00:50 +00:00
*
2019-12-30 22:00:08 +00:00
* @ param string $path API URL path , relative to DI :: baseUrl ()
2017-11-10 05:00:50 +00:00
* @ param string $func Function name to call on path request
* @ param bool $auth API need logged user
* @ param string $method HTTP method reqiured to call this endpoint .
* One of API_METHOD_ANY , API_METHOD_GET , API_METHOD_POST .
* Default to API_METHOD_ANY
*/
function api_register_func ( $path , $func , $auth = false , $method = API_METHOD_ANY )
{
global $API ;
2018-01-15 13:05:12 +00:00
$API [ $path ] = [
2017-11-10 05:00:50 +00:00
'func' => $func ,
'auth' => $auth ,
'method' => $method ,
2018-01-15 13:05:12 +00:00
];
2017-11-10 05:00:50 +00:00
// Workaround for hotot
$path = str_replace ( " api/ " , " api/1.1/ " , $path );
2018-01-15 13:05:12 +00:00
$API [ $path ] = [
2017-11-10 05:00:50 +00:00
'func' => $func ,
'auth' => $auth ,
'method' => $method ,
2018-01-15 13:05:12 +00:00
];
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Check HTTP method of called API
*
2017-11-10 05:00:50 +00:00
* API endpoints can define which HTTP method to accept when called .
* This function check the current HTTP method agains endpoint
* registered method .
*
* @ param string $method Required methods , uppercase , separated by comma
* @ return bool
*/
function api_check_method ( $method )
{
if ( $method == " * " ) {
return true ;
2016-09-25 16:50:08 +00:00
}
2019-10-13 02:01:34 +00:00
return ( stripos ( $method , $_SERVER [ 'REQUEST_METHOD' ] ? ? 'GET' ) !== false );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Main API entry point
2017-11-10 05:00:50 +00:00
*
2020-01-19 06:05:23 +00:00
* Authenticate user , call registered API function , set HTTP headers
2017-12-24 02:20:50 +00:00
*
2019-01-07 17:24:01 +00:00
* @ param App $a App
2019-12-16 00:35:26 +00:00
* @ param App\Arguments $args The app arguments ( optional , will retrieved by the DI - Container in case of missing )
2018-04-09 17:34:02 +00:00
* @ return string | array API call result
2019-01-07 17:24:01 +00:00
* @ throws Exception
2017-11-10 05:00:50 +00:00
*/
2019-12-16 00:35:26 +00:00
function api_call ( App $a , App\Arguments $args = null )
2017-11-10 05:00:50 +00:00
{
global $API , $called_api ;
2016-09-25 16:50:08 +00:00
2019-12-16 00:35:26 +00:00
if ( $args == null ) {
$args = DI :: args ();
}
2017-11-10 05:00:50 +00:00
$type = " json " ;
2020-09-09 04:15:25 +00:00
if ( strpos ( $args -> getCommand (), " .xml " ) > 0 ) {
2017-11-10 05:00:50 +00:00
$type = " xml " ;
}
2020-09-09 04:15:25 +00:00
if ( strpos ( $args -> getCommand (), " .json " ) > 0 ) {
2017-04-05 20:07:55 +00:00
$type = " json " ;
2017-11-10 05:00:50 +00:00
}
2020-09-09 04:15:25 +00:00
if ( strpos ( $args -> getCommand (), " .rss " ) > 0 ) {
2017-11-10 05:00:50 +00:00
$type = " rss " ;
}
2020-09-09 04:15:25 +00:00
if ( strpos ( $args -> getCommand (), " .atom " ) > 0 ) {
2017-11-10 05:00:50 +00:00
$type = " atom " ;
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
try {
foreach ( $API as $p => $info ) {
2020-09-09 04:15:25 +00:00
if ( strpos ( $args -> getCommand (), $p ) === 0 ) {
2017-11-10 05:00:50 +00:00
if ( ! api_check_method ( $info [ 'method' ])) {
throw new MethodNotAllowedException ();
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$called_api = explode ( " / " , $p );
2017-04-05 20:17:15 +00:00
2021-11-17 22:44:52 +00:00
if ( ! empty ( $info [ 'auth' ]) && BaseApi :: getCurrentUserID () === false ) {
2021-11-08 21:35:41 +00:00
BasicAuth :: getCurrentUserID ( true );
2021-08-09 19:48:39 +00:00
Logger :: info ( API_LOG_PREFIX . 'nickname {nickname}' , [ 'module' => 'api' , 'action' => 'call' , 'nickname' => $a -> getLoggedInUserNickname ()]);
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-12-30 20:42:56 +00:00
Logger :: debug ( API_LOG_PREFIX . 'parameters' , [ 'module' => 'api' , 'action' => 'call' , 'parameters' => $_REQUEST ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$stamp = microtime ( true );
2018-01-04 01:54:35 +00:00
$return = call_user_func ( $info [ 'func' ], $type );
2019-10-16 12:43:59 +00:00
$duration = floatval ( microtime ( true ) - $stamp );
2018-12-30 20:42:56 +00:00
2020-07-09 19:03:48 +00:00
Logger :: info ( API_LOG_PREFIX . 'duration {duration}' , [ 'module' => 'api' , 'action' => 'call' , 'duration' => round ( $duration , 2 )]);
2016-09-25 16:50:08 +00:00
2019-12-15 22:50:35 +00:00
DI :: profiler () -> saveLog ( DI :: logger (), API_LOG_PREFIX . 'performance' );
2016-11-04 22:45:20 +00:00
2018-01-04 01:54:35 +00:00
if ( false === $return ) {
2017-11-10 05:00:50 +00:00
/*
* api function returned false withour throw an
* exception . This should not happend , throw a 500
*/
throw new InternalServerErrorException ();
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " xml " :
header ( " Content-Type: text/xml " );
break ;
case " json " :
header ( " Content-Type: application/json " );
2019-08-02 16:43:36 +00:00
if ( ! empty ( $return )) {
$json = json_encode ( end ( $return ));
if ( ! empty ( $_GET [ 'callback' ])) {
$json = $_GET [ 'callback' ] . " ( " . $json . " ) " ;
}
$return = $json ;
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
break ;
case " rss " :
header ( " Content-Type: application/rss+xml " );
2018-01-04 01:54:35 +00:00
$return = '<?xml version="1.0" encoding="UTF-8"?>' . " \n " . $return ;
2017-11-10 05:00:50 +00:00
break ;
case " atom " :
header ( " Content-Type: application/atom+xml " );
2018-01-04 01:54:35 +00:00
$return = '<?xml version="1.0" encoding="UTF-8"?>' . " \n " . $return ;
2017-11-10 05:00:50 +00:00
break ;
2016-09-25 16:50:08 +00:00
}
2018-01-04 01:54:35 +00:00
return $return ;
2016-09-25 16:50:08 +00:00
}
}
2017-11-10 05:00:50 +00:00
2019-12-16 00:30:34 +00:00
Logger :: warning ( API_LOG_PREFIX . 'not implemented' , [ 'module' => 'api' , 'action' => 'call' , 'query' => DI :: args () -> getQueryString ()]);
2020-11-11 20:28:36 +00:00
throw new NotFoundException ();
2017-11-10 05:00:50 +00:00
} catch ( HTTPException $e ) {
2021-11-12 19:04:47 +00:00
DI :: apiResponse () -> error ( $e -> getCode (), $e -> getDescription (), $e -> getMessage (), $type );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Set values for RSS template
2017-11-10 05:00:50 +00:00
*
* @ param array $arr Array to be passed to template
* @ param array $user_info User info
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
* @ todo find proper type - hints
2017-11-10 05:00:50 +00:00
*/
2021-11-17 21:28:51 +00:00
function api_rss_extra ( $arr , $user_info )
2017-11-10 05:00:50 +00:00
{
if ( is_null ( $user_info )) {
2021-11-17 22:44:52 +00:00
$uid = BaseApi :: getCurrentUserID ();
2021-11-17 21:28:51 +00:00
if ( empty ( $uid )) {
throw new ForbiddenException ();
}
2021-11-17 23:03:18 +00:00
2021-11-17 21:28:51 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( $uid ) -> toArray ();
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$arr [ '$user' ] = $user_info ;
2018-01-15 13:05:12 +00:00
$arr [ '$rss' ] = [
2017-11-10 05:00:50 +00:00
'alternate' => $user_info [ 'url' ],
2019-12-30 22:00:08 +00:00
'self' => DI :: baseUrl () . " / " . DI :: args () -> getQueryString (),
'base' => DI :: baseUrl (),
2017-11-10 05:00:50 +00:00
'updated' => api_date ( null ),
2018-01-27 02:38:34 +00:00
'atom_updated' => DateTimeFormat :: utcNow ( DateTimeFormat :: ATOM ),
2018-07-04 21:33:09 +00:00
'language' => $user_info [ 'lang' ],
2019-12-30 22:00:08 +00:00
'logo' => DI :: baseUrl () . " /images/friendica-32.png " ,
2018-01-15 13:05:12 +00:00
];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
return $arr ;
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Unique contact to contact url .
2017-11-10 05:00:50 +00:00
*
* @ param int $id Contact id
* @ return bool | string
2019-01-07 17:24:01 +00:00
* Contact url or False if contact id is unknown
* @ throws Exception
2017-11-10 05:00:50 +00:00
*/
2017-12-17 11:11:28 +00:00
function api_unique_id_to_nurl ( $id )
2017-11-10 05:00:50 +00:00
{
2018-07-20 12:19:26 +00:00
$r = DBA :: selectFirst ( 'contact' , [ 'nurl' ], [ 'id' => $id ]);
2017-11-10 05:00:50 +00:00
2018-07-21 12:46:04 +00:00
if ( DBA :: isResult ( $r )) {
2017-12-17 11:11:28 +00:00
return $r [ " nurl " ];
2017-11-10 05:00:50 +00:00
} else {
return false ;
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Get user info array .
2017-11-10 05:00:50 +00:00
*
2019-01-07 17:24:01 +00:00
* @ param App $a App
2017-11-10 05:00:50 +00:00
* @ param int | string $contact_id Contact ID or URL
2019-01-21 16:36:01 +00:00
* @ return array | bool
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-11-10 05:00:50 +00:00
*/
2021-11-08 21:35:41 +00:00
function api_get_user ( $contact_id = null )
2017-11-10 05:00:50 +00:00
{
global $called_api ;
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
$user = null ;
$extra_query = " " ;
$url = " " ;
2018-12-30 20:42:56 +00:00
Logger :: info ( API_LOG_PREFIX . 'Fetching data for user {user}' , [ 'module' => 'api' , 'action' => 'get_user' , 'user' => $contact_id ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Searching for contact URL
if ( ! is_null ( $contact_id ) && ( intval ( $contact_id ) == 0 )) {
2021-10-09 21:16:15 +00:00
$user = Strings :: normaliseLink ( $contact_id );
2017-11-10 05:00:50 +00:00
$url = $user ;
2021-10-09 21:16:15 +00:00
$extra_query = " AND `contact`.`nurl` = ? " ;
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () !== false ) {
$extra_query .= " AND `contact`.`uid`= " . intval ( BaseApi :: getCurrentUserID ());
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
// Searching for contact id with uid = 0
if ( ! is_null ( $contact_id ) && ( intval ( $contact_id ) != 0 )) {
2021-10-09 21:16:15 +00:00
$user = api_unique_id_to_nurl ( intval ( $contact_id ));
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $user == " " ) {
2018-06-03 09:55:41 +00:00
throw new BadRequestException ( " User ID " . $contact_id . " not found. " );
2017-11-10 05:00:50 +00:00
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
$url = $user ;
2021-10-09 21:16:15 +00:00
$extra_query = " AND `contact`.`nurl` = ? " ;
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () !== false ) {
$extra_query .= " AND `contact`.`uid`= " . intval ( BaseApi :: getCurrentUserID ());
2017-05-15 20:11:33 +00:00
}
2016-09-25 16:50:08 +00:00
}
2018-11-30 14:06:22 +00:00
if ( is_null ( $user ) && ! empty ( $_GET [ 'user_id' ])) {
2021-10-09 21:16:15 +00:00
$user = api_unique_id_to_nurl ( $_GET [ 'user_id' ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $user == " " ) {
2018-06-03 09:55:41 +00:00
throw new BadRequestException ( " User ID " . $_GET [ 'user_id' ] . " not found. " );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
$url = $user ;
2021-10-09 21:16:15 +00:00
$extra_query = " AND `contact`.`nurl` = ? " ;
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () !== false ) {
$extra_query .= " AND `contact`.`uid`= " . intval ( BaseApi :: getCurrentUserID ());
2017-11-10 05:00:50 +00:00
}
}
2018-11-30 14:06:22 +00:00
if ( is_null ( $user ) && ! empty ( $_GET [ 'screen_name' ])) {
2021-10-09 21:16:15 +00:00
$user = $_GET [ 'screen_name' ];
$extra_query = " AND `contact`.`nick` = ? " ;
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () !== false ) {
$extra_query .= " AND `contact`.`uid`= " . intval ( BaseApi :: getCurrentUserID ());
2017-11-10 05:00:50 +00:00
}
}
2016-09-25 16:50:08 +00:00
2018-11-30 14:06:22 +00:00
if ( is_null ( $user ) && ! empty ( $_GET [ 'profileurl' ])) {
2021-10-09 21:16:15 +00:00
$user = Strings :: normaliseLink ( $_GET [ 'profileurl' ]);
$extra_query = " AND `contact`.`nurl` = ? " ;
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () !== false ) {
$extra_query .= " AND `contact`.`uid`= " . intval ( BaseApi :: getCurrentUserID ());
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-07-01 04:16:32 +00:00
// $called_api is the API path exploded on / and is expected to have at least 2 elements
2021-07-25 14:27:13 +00:00
if ( is_null ( $user ) && ( DI :: args () -> getArgc () > ( count ( $called_api ) - 1 )) && ( count ( $called_api ) > 0 )) {
2017-11-10 05:00:50 +00:00
$argid = count ( $called_api );
2021-07-25 14:27:13 +00:00
if ( ! empty ( DI :: args () -> getArgv ()[ $argid ])) {
$data = explode ( " . " , DI :: args () -> getArgv ()[ $argid ]);
2018-09-02 08:01:13 +00:00
if ( count ( $data ) > 1 ) {
2021-11-12 18:52:01 +00:00
[ $user , $null ] = $data ;
2018-09-02 08:01:13 +00:00
}
2018-07-08 09:37:05 +00:00
}
2017-11-10 05:00:50 +00:00
if ( is_numeric ( $user )) {
2021-10-09 21:16:15 +00:00
$user = api_unique_id_to_nurl ( intval ( $user ));
2016-09-25 16:50:08 +00:00
2018-04-10 08:46:10 +00:00
if ( $user != " " ) {
$url = $user ;
2021-10-09 21:16:15 +00:00
$extra_query = " AND `contact`.`nurl` = ? " ;
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () !== false ) {
$extra_query .= " AND `contact`.`uid`= " . intval ( BaseApi :: getCurrentUserID ());
2018-04-10 08:46:10 +00:00
}
2017-04-05 20:07:55 +00:00
}
2017-11-10 05:00:50 +00:00
} else {
2021-10-09 21:16:15 +00:00
$extra_query = " AND `contact`.`nick` = ? " ;
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () !== false ) {
$extra_query .= " AND `contact`.`uid`= " . intval ( BaseApi :: getCurrentUserID ());
2016-09-25 16:50:08 +00:00
}
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-12-30 20:42:56 +00:00
Logger :: info ( API_LOG_PREFIX . 'getting user {user}' , [ 'module' => 'api' , 'action' => 'get_user' , 'user' => $user ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( ! $user ) {
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () === false ) {
2021-11-08 21:35:41 +00:00
BasicAuth :: getCurrentUserID ( true );
2017-11-10 05:00:50 +00:00
return false ;
} else {
2021-11-17 22:44:52 +00:00
$user = BaseApi :: getCurrentUserID ();
2021-10-09 21:16:15 +00:00
$extra_query = " AND `contact`.`uid` = ? AND `contact`.`self` " ;
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-12-30 20:42:56 +00:00
Logger :: info ( API_LOG_PREFIX . 'found user {user}' , [ 'module' => 'api' , 'action' => 'get_user' , 'user' => $user , 'extra_query' => $extra_query ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// user info
2021-10-09 21:16:15 +00:00
$uinfo = DBA :: toArray ( DBA :: p (
2017-11-10 05:00:50 +00:00
" SELECT *, `contact`.`id` AS `cid` FROM `contact`
WHERE 1
$extra_query " ,
$user
2021-10-09 21:16:15 +00:00
));
2016-09-25 16:50:08 +00:00
2021-11-16 23:21:40 +00:00
if ( DBA :: isResult ( $uinfo )) {
// Selecting the id by priority, friendica first
2018-04-09 19:34:53 +00:00
api_best_nickname ( $uinfo );
2021-11-16 23:21:40 +00:00
return DI :: twitterUser () -> createFromContactId ( $uinfo [ 0 ][ 'cid' ], $uinfo [ 0 ][ 'uid' ]) -> toArray ();
2018-04-09 19:34:53 +00:00
}
2016-11-04 22:45:20 +00:00
2021-11-16 23:21:40 +00:00
if ( $url == " " ) {
throw new BadRequestException ( " User not found. " );
2017-11-10 05:00:50 +00:00
}
2021-11-16 23:21:40 +00:00
$cid = Contact :: getIdForURL ( $url , 0 , false );
2017-12-24 11:51:38 +00:00
2021-11-16 23:21:40 +00:00
if ( ! empty ( $cid )) {
return DI :: twitterUser () -> createFromContactId ( $cid , 0 ) -> toArray ();
2017-12-24 11:51:38 +00:00
} else {
2021-11-16 23:21:40 +00:00
throw new BadRequestException ( " User " . $url . " not found. " );
2017-12-18 19:39:35 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* return api - formatted array for item ' s author and owner
2017-11-10 05:00:50 +00:00
*
2019-01-07 17:24:01 +00:00
* @ param App $a App
* @ param array $item item from db
2017-11-10 05:00:50 +00:00
* @ return array ( array : author , array : owner )
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-11-10 05:00:50 +00:00
*/
function api_item_get_user ( App $a , $item )
{
2021-11-17 22:44:52 +00:00
$status_user = DI :: twitterUser () -> createFromContactId ( $item [ 'author-id' ] ? ? 0 , BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
2019-03-17 18:39:34 +00:00
$author_user = $status_user ;
2020-03-02 17:20:18 +00:00
$status_user [ " protected " ] = isset ( $item [ 'private' ]) && ( $item [ 'private' ] == Item :: PRIVATE );
2017-11-10 05:00:50 +00:00
2019-10-13 02:01:34 +00:00
if (( $item [ 'thr-parent' ] ? ? '' ) == ( $item [ 'uri' ] ? ? '' )) {
2021-11-17 22:44:52 +00:00
$owner_user = DI :: twitterUser () -> createFromContactId ( $item [ 'owner-id' ] ? ? 0 , BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
} else {
2019-03-17 18:39:34 +00:00
$owner_user = $author_user ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2019-03-17 18:39:34 +00:00
return ([ $status_user , $author_user , $owner_user ]);
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
* TWITTER API
*/
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
* Returns an HTTP 200 OK response code and a representation of the requesting user if authentication was successful ;
* returns a 401 status code and an error message if not .
2019-01-07 17:24:01 +00:00
*
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / manage - account - settings / api - reference / get - account - verify_credentials
*
* @ param string $type Return type ( atom , rss , xml , json )
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-11-10 05:00:50 +00:00
*/
function api_account_verify_credentials ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
unset ( $_REQUEST [ " user_id " ]);
unset ( $_GET [ " user_id " ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
unset ( $_REQUEST [ " screen_name " ]);
unset ( $_GET [ " screen_name " ]);
2016-09-25 16:50:08 +00:00
2019-10-13 02:01:34 +00:00
$skip_status = $_REQUEST [ 'skip_status' ] ? ? false ;
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// "verified" isn't used here in the standard
unset ( $user_info [ " verified " ]);
// - Adding last status
if ( ! $skip_status ) {
2019-03-23 14:01:25 +00:00
$item = api_get_last_status ( $user_info [ 'pid' ], $user_info [ 'uid' ]);
2019-11-21 22:40:54 +00:00
if ( ! empty ( $item )) {
2019-03-23 14:01:25 +00:00
$user_info [ 'status' ] = api_format_item ( $item , $type );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// "uid" and "self" are only needed for some internal stuff, so remove it from here
unset ( $user_info [ " uid " ]);
unset ( $user_info [ " self " ]);
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " user " , $type , [ 'user' => $user_info ]);
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/account/verify_credentials' , 'api_account_verify_credentials' , true );
/**
* Get data from $_POST or $_GET
2017-12-24 02:20:50 +00:00
*
* @ param string $k
2019-01-07 17:24:01 +00:00
* @ return null
2017-11-10 05:00:50 +00:00
*/
function requestdata ( $k )
{
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_POST [ $k ])) {
2017-11-10 05:00:50 +00:00
return $_POST [ $k ];
}
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_GET [ $k ])) {
2017-11-10 05:00:50 +00:00
return $_GET [ $k ];
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
return null ;
}
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
2018-06-05 05:42:26 +00:00
* Deprecated function to upload media .
2017-12-24 02:20:50 +00:00
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_statuses_mediap ( $type )
{
2020-01-04 22:42:01 +00:00
$a = DI :: app ();
2016-09-25 16:50:08 +00:00
2021-11-17 22:59:18 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2017-11-10 05:00:50 +00:00
2021-11-17 23:03:18 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2021-11-17 23:03:18 +00:00
2021-11-17 22:44:52 +00:00
$_REQUEST [ 'profile_uid' ] = BaseApi :: getCurrentUserID ();
2017-11-10 05:00:50 +00:00
$_REQUEST [ 'api_source' ] = true ;
2021-05-01 15:48:19 +00:00
$txt = requestdata ( 'status' ) ? ? '' ;
2017-11-10 05:00:50 +00:00
/// @TODO old-lost code?
//$txt = urldecode(requestdata('status'));
if (( strpos ( $txt , '<' ) !== false ) || ( strpos ( $txt , '>' ) !== false )) {
2018-11-06 11:34:32 +00:00
$txt = HTML :: toBBCodeVideo ( $txt );
2017-11-10 05:00:50 +00:00
$config = HTMLPurifier_Config :: createDefault ();
$config -> set ( 'Cache.DefinitionImpl' , null );
$purifier = new HTMLPurifier ( $config );
$txt = $purifier -> purify ( $txt );
2016-09-25 16:50:08 +00:00
}
2018-03-07 21:24:13 +00:00
$txt = HTML :: toBBCode ( $txt );
2016-09-25 16:50:08 +00:00
2021-07-25 14:27:13 +00:00
DI :: args () -> getArgv ()[ 1 ] = $user_info [ 'screen_name' ]; //should be set to username?
2016-09-25 16:50:08 +00:00
2018-06-05 05:42:26 +00:00
$picture = wall_upload_post ( $a , false );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// now that we have the img url in bbcode we can add it to the status and insert the wall item.
2018-06-05 05:42:26 +00:00
$_REQUEST [ 'body' ] = $txt . " \n \n " . '[url=' . $picture [ " albumpage " ] . '][img]' . $picture [ " preview " ] . " [/img][/url] " ;
2018-09-02 07:20:04 +00:00
$item_id = item_post ( $a );
2016-09-25 16:50:08 +00:00
2018-09-02 07:20:04 +00:00
// output the post that we just posted.
return api_status_show ( $type , $item_id );
2017-11-10 05:00:50 +00:00
}
/// @TODO move this to top of file or somewhere better!
api_register_func ( 'api/statuses/mediap' , 'api_statuses_mediap' , true , API_METHOD_POST );
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
* Updates the user’ s current status .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws TooManyRequestsException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / tweets / post - and - engage / api - reference / post - statuses - update
*/
2017-11-10 05:00:50 +00:00
function api_statuses_update ( $type )
{
2020-01-04 22:42:01 +00:00
$a = DI :: app ();
2017-11-10 05:00:50 +00:00
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2018-01-04 18:26:09 +00:00
2017-11-10 05:00:50 +00:00
// convert $_POST array items to the form we use for web posts.
if ( requestdata ( 'htmlstatus' )) {
2021-05-01 15:48:19 +00:00
$txt = requestdata ( 'htmlstatus' ) ? ? '' ;
2017-11-10 05:00:50 +00:00
if (( strpos ( $txt , '<' ) !== false ) || ( strpos ( $txt , '>' ) !== false )) {
2018-11-06 11:34:32 +00:00
$txt = HTML :: toBBCodeVideo ( $txt );
2017-11-10 05:00:50 +00:00
2016-09-25 16:50:08 +00:00
$config = HTMLPurifier_Config :: createDefault ();
$config -> set ( 'Cache.DefinitionImpl' , null );
2017-11-10 05:00:50 +00:00
2016-09-25 16:50:08 +00:00
$purifier = new HTMLPurifier ( $config );
$txt = $purifier -> purify ( $txt );
2018-03-07 21:24:13 +00:00
$_REQUEST [ 'body' ] = HTML :: toBBCode ( $txt );
2017-11-10 05:00:50 +00:00
}
} else {
$_REQUEST [ 'body' ] = requestdata ( 'status' );
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$_REQUEST [ 'title' ] = requestdata ( 'title' );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$parent = requestdata ( 'in_reply_to_status_id' );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Twidere sends "-1" if it is no reply ...
if ( $parent == - 1 ) {
$parent = " " ;
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
if ( ctype_digit ( $parent )) {
$_REQUEST [ 'parent' ] = $parent ;
} else {
$_REQUEST [ 'parent_uri' ] = $parent ;
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( requestdata ( 'lat' ) && requestdata ( 'long' )) {
$_REQUEST [ 'coord' ] = sprintf ( " %s %s " , requestdata ( 'lat' ), requestdata ( 'long' ));
}
2021-11-17 22:44:52 +00:00
$_REQUEST [ 'profile_uid' ] = BaseApi :: getCurrentUserID ();
2017-11-10 05:00:50 +00:00
2018-07-19 13:52:05 +00:00
if ( ! $parent ) {
2017-11-10 05:00:50 +00:00
// Check for throttling (maximum posts per day, week and month)
2020-01-19 20:21:13 +00:00
$throttle_day = DI :: config () -> get ( 'system' , 'throttle_limit_day' );
2017-11-10 05:00:50 +00:00
if ( $throttle_day > 0 ) {
2018-01-27 02:38:34 +00:00
$datefrom = date ( DateTimeFormat :: MYSQL , time () - 24 * 60 * 60 );
2017-11-10 05:00:50 +00:00
2021-11-17 22:44:52 +00:00
$condition = [ " `gravity` = ? AND `uid` = ? AND `wall` AND `received` > ? " , GRAVITY_PARENT , BaseApi :: getCurrentUserID (), $datefrom ];
2021-02-04 05:51:25 +00:00
$posts_day = Post :: count ( $condition );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $posts_day > $throttle_day ) {
2021-11-17 22:44:52 +00:00
logger :: info ( 'Daily posting limit reached for user ' . BaseApi :: getCurrentUserID ());
2020-01-18 19:52:34 +00:00
// die(api_error($type, DI::l10n()->t("Daily posting limit of %d posts reached. The post was rejected.", $throttle_day));
2020-01-18 19:53:01 +00:00
throw new TooManyRequestsException ( DI :: l10n () -> tt ( " Daily posting limit of %d post reached. The post was rejected. " , " Daily posting limit of %d posts reached. The post was rejected. " , $throttle_day ));
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
}
2020-01-19 20:21:13 +00:00
$throttle_week = DI :: config () -> get ( 'system' , 'throttle_limit_week' );
2017-11-10 05:00:50 +00:00
if ( $throttle_week > 0 ) {
2018-01-27 02:38:34 +00:00
$datefrom = date ( DateTimeFormat :: MYSQL , time () - 24 * 60 * 60 * 7 );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$condition = [ " `gravity` = ? AND `uid` = ? AND `wall` AND `received` > ? " , GRAVITY_PARENT , BaseApi :: getCurrentUserID (), $datefrom ];
2021-02-04 05:51:25 +00:00
$posts_week = Post :: count ( $condition );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $posts_week > $throttle_week ) {
2021-11-17 22:44:52 +00:00
logger :: info ( 'Weekly posting limit reached for user ' . BaseApi :: getCurrentUserID ());
2020-01-18 19:52:34 +00:00
// die(api_error($type, DI::l10n()->t("Weekly posting limit of %d posts reached. The post was rejected.", $throttle_week)));
2020-01-18 19:53:01 +00:00
throw new TooManyRequestsException ( DI :: l10n () -> tt ( " Weekly posting limit of %d post reached. The post was rejected. " , " Weekly posting limit of %d posts reached. The post was rejected. " , $throttle_week ));
2017-11-10 05:00:50 +00:00
}
}
2016-09-25 16:50:08 +00:00
2020-01-19 20:21:13 +00:00
$throttle_month = DI :: config () -> get ( 'system' , 'throttle_limit_month' );
2017-11-10 05:00:50 +00:00
if ( $throttle_month > 0 ) {
2018-01-27 02:38:34 +00:00
$datefrom = date ( DateTimeFormat :: MYSQL , time () - 24 * 60 * 60 * 30 );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$condition = [ " `gravity` = ? AND `uid` = ? AND `wall` AND `received` > ? " , GRAVITY_PARENT , BaseApi :: getCurrentUserID (), $datefrom ];
2021-02-04 05:51:25 +00:00
$posts_month = Post :: count ( $condition );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $posts_month > $throttle_month ) {
2021-11-17 22:44:52 +00:00
logger :: info ( 'Monthly posting limit reached for user ' . BaseApi :: getCurrentUserID ());
2020-01-18 19:52:34 +00:00
// die(api_error($type, DI::l10n()->t("Monthly posting limit of %d posts reached. The post was rejected.", $throttle_month));
throw new TooManyRequestsException ( DI :: l10n () -> t ( " Monthly posting limit of %d post reached. The post was rejected. " , " Monthly posting limit of %d posts reached. The post was rejected. " , $throttle_month ));
2017-11-10 05:00:50 +00:00
}
2017-04-05 20:07:55 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-05-01 15:48:19 +00:00
if ( requestdata ( 'media_ids' )) {
$ids = explode ( ',' , requestdata ( 'media_ids' ) ? ? '' );
} elseif ( ! empty ( $_FILES [ 'media' ])) {
2017-11-10 05:00:50 +00:00
// upload the image if we have one
2018-06-05 05:42:26 +00:00
$picture = wall_upload_post ( $a , false );
2018-06-05 05:44:04 +00:00
if ( is_array ( $picture )) {
2021-05-01 15:48:19 +00:00
$ids [] = $picture [ 'id' ];
2017-04-05 20:07:55 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-05-01 15:48:19 +00:00
$attachments = [];
$ressources = [];
if ( ! empty ( $ids )) {
2019-08-06 05:34:04 +00:00
foreach ( $ids as $id ) {
2021-05-01 15:48:19 +00:00
$media = DBA :: toArray ( DBA :: p ( " SELECT `resource-id`, `scale`, `nickname`, `type`, `desc`, `filename`, `datasize`, `width`, `height` FROM `photo`
INNER JOIN `user` ON `user` . `uid` = `photo` . `uid` WHERE `resource-id` IN
( SELECT `resource-id` FROM `photo` WHERE `id` = ? ) AND `photo` . `uid` = ?
2021-11-17 22:44:52 +00:00
ORDER BY `photo` . `width` DESC LIMIT 2 " , $id , BaseApi::getCurrentUserID()));
2021-05-22 21:45:15 +00:00
2021-05-01 15:48:19 +00:00
if ( ! empty ( $media )) {
$ressources [] = $media [ 0 ][ 'resource-id' ];
2019-10-18 01:26:15 +00:00
$phototypes = Images :: supportedTypes ();
2021-05-01 15:48:19 +00:00
$ext = $phototypes [ $media [ 0 ][ 'type' ]];
2021-05-22 21:45:15 +00:00
2021-05-01 15:48:19 +00:00
$attachment = [ 'type' => Post\Media :: IMAGE , 'mimetype' => $media [ 0 ][ 'type' ],
'url' => DI :: baseUrl () . '/photo/' . $media [ 0 ][ 'resource-id' ] . '-' . $media [ 0 ][ 'scale' ] . '.' . $ext ,
'size' => $media [ 0 ][ 'datasize' ],
'name' => $media [ 0 ][ 'filename' ] ? : $media [ 0 ][ 'resource-id' ],
'description' => $media [ 0 ][ 'desc' ] ? ? '' ,
'width' => $media [ 0 ][ 'width' ],
'height' => $media [ 0 ][ 'height' ]];
2021-05-22 21:45:15 +00:00
2021-05-01 15:48:19 +00:00
if ( count ( $media ) > 1 ) {
$attachment [ 'preview' ] = DI :: baseUrl () . '/photo/' . $media [ 1 ][ 'resource-id' ] . '-' . $media [ 1 ][ 'scale' ] . '.' . $ext ;
$attachment [ 'preview-width' ] = $media [ 1 ][ 'width' ];
$attachment [ 'preview-height' ] = $media [ 1 ][ 'height' ];
}
$attachments [] = $attachment ;
2019-08-06 05:34:04 +00:00
}
2017-04-05 20:07:55 +00:00
}
2021-05-01 15:48:19 +00:00
// We have to avoid that the post is rejected because of an empty body
if ( empty ( $_REQUEST [ 'body' ])) {
2021-10-02 08:22:15 +00:00
$_REQUEST [ 'body' ] = '[hr]' ;
2021-05-01 15:48:19 +00:00
}
}
if ( ! empty ( $attachments )) {
$_REQUEST [ 'attachments' ] = $attachments ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// set this so that the item_post() function is quiet and doesn't redirect or emit json
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$_REQUEST [ 'api_source' ] = true ;
2016-09-25 16:50:08 +00:00
2018-11-30 14:06:22 +00:00
if ( empty ( $_REQUEST [ 'source' ])) {
2017-11-10 05:00:50 +00:00
$_REQUEST [ " source " ] = api_source ();
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// call out normal post function
2018-09-02 07:20:04 +00:00
$item_id = item_post ( $a );
2016-09-25 16:50:08 +00:00
2021-05-01 15:48:19 +00:00
if ( ! empty ( $ressources ) && ! empty ( $item_id )) {
$item = Post :: selectFirst ([ 'uri-id' , 'allow_cid' , 'allow_gid' , 'deny_cid' , 'deny_gid' ], [ 'id' => $item_id ]);
foreach ( $ressources as $ressource ) {
2021-11-17 22:44:52 +00:00
Photo :: setPermissionForRessource ( $ressource , BaseApi :: getCurrentUserID (), $item [ 'allow_cid' ], $item [ 'allow_gid' ], $item [ 'deny_cid' ], $item [ 'deny_gid' ]);
2021-05-01 15:48:19 +00:00
}
}
2018-09-02 07:20:04 +00:00
// output the post that we just posted.
return api_status_show ( $type , $item_id );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/statuses/update' , 'api_statuses_update' , true , API_METHOD_POST );
api_register_func ( 'api/statuses/update_with_media' , 'api_statuses_update' , true , API_METHOD_POST );
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
* Uploads an image to Friendica .
*
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / media / upload - media / api - reference / post - media - upload
*/
2018-01-04 01:54:35 +00:00
function api_media_upload ()
2017-11-10 05:00:50 +00:00
{
2020-01-04 22:42:01 +00:00
$a = DI :: app ();
2016-09-25 16:50:08 +00:00
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2018-01-04 18:26:09 +00:00
2018-11-30 14:06:22 +00:00
if ( empty ( $_FILES [ 'media' ])) {
2017-11-10 05:00:50 +00:00
// Output error
throw new BadRequestException ( " No media. " );
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$media = wall_upload_post ( $a , false );
if ( ! $media ) {
// Output error
throw new InternalServerErrorException ();
}
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$returndata = [];
2017-11-10 05:00:50 +00:00
$returndata [ " media_id " ] = $media [ " id " ];
$returndata [ " media_id_string " ] = ( string ) $media [ " id " ];
$returndata [ " size " ] = $media [ " size " ];
2018-01-15 13:05:12 +00:00
$returndata [ " image " ] = [ " w " => $media [ " width " ],
2018-12-15 20:15:55 +00:00
" h " => $media [ " height " ],
" image_type " => $media [ " type " ],
2018-12-16 06:16:45 +00:00
" friendica_preview_url " => $media [ " preview " ]];
2016-09-25 16:50:08 +00:00
2020-06-29 20:22:00 +00:00
Logger :: info ( 'Media uploaded' , [ 'return' => $returndata ]);
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
return [ " media " => $returndata ];
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/media/upload' , 'api_media_upload' , true , API_METHOD_POST );
2016-09-25 16:50:08 +00:00
2019-07-30 05:02:26 +00:00
/**
* Updates media meta data ( picture descriptions )
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws TooManyRequestsException
* @ throws UnauthorizedException
* @ see https :// developer . twitter . com / en / docs / tweets / post - and - engage / api - reference / post - statuses - update
*
* @ todo Compare the corresponding Twitter function for correct return values
*/
function api_media_metadata_create ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2019-07-30 05:02:26 +00:00
2019-07-30 22:26:01 +00:00
$postdata = Network :: postdata ();
2019-07-30 05:02:26 +00:00
if ( empty ( $postdata )) {
throw new BadRequestException ( " No post data " );
}
$data = json_decode ( $postdata , true );
if ( empty ( $data )) {
throw new BadRequestException ( " Invalid post data " );
}
if ( empty ( $data [ 'media_id' ]) || empty ( $data [ 'alt_text' ])) {
throw new BadRequestException ( " Missing post data values " );
}
if ( empty ( $data [ 'alt_text' ][ 'text' ])) {
throw new BadRequestException ( " No alt text. " );
}
Logger :: info ( 'Updating metadata' , [ 'media_id' => $data [ 'media_id' ]]);
2021-11-17 22:44:52 +00:00
$condition = [ 'id' => $data [ 'media_id' ], 'uid' => BaseApi :: getCurrentUserID ()];
2019-07-30 05:02:26 +00:00
$photo = DBA :: selectFirst ( 'photo' , [ 'resource-id' ], $condition );
if ( ! DBA :: isResult ( $photo )) {
throw new BadRequestException ( " Metadata not found. " );
}
DBA :: update ( 'photo' , [ 'desc' => $data [ 'alt_text' ][ 'text' ]], [ 'resource-id' => $photo [ 'resource-id' ]]);
}
api_register_func ( 'api/media/metadata/create' , 'api_media_metadata_create' , true , API_METHOD_POST );
2017-12-24 02:20:50 +00:00
/**
2019-03-02 22:03:13 +00:00
* @ param string $type Return format ( atom , rss , xml , json )
2019-01-07 17:24:01 +00:00
* @ param int $item_id
2020-05-15 18:08:47 +00:00
* @ return array | string
2019-03-02 22:03:13 +00:00
* @ throws Exception
2017-12-24 02:20:50 +00:00
*/
2019-03-02 21:10:57 +00:00
function api_status_show ( $type , $item_id )
2017-11-10 05:00:50 +00:00
{
2019-03-02 21:10:57 +00:00
Logger :: info ( API_LOG_PREFIX . 'Start' , [ 'action' => 'status_show' , 'type' => $type , 'item_id' => $item_id ]);
2019-03-02 22:03:13 +00:00
$status_info = [];
2019-01-21 16:40:14 +00:00
2019-03-02 22:03:13 +00:00
$item = api_get_item ([ 'id' => $item_id ]);
2019-11-21 22:40:54 +00:00
if ( ! empty ( $item )) {
2019-03-02 22:03:13 +00:00
$status_info = api_format_item ( $item , $type );
2019-01-21 16:40:14 +00:00
}
2019-03-02 21:10:57 +00:00
Logger :: info ( API_LOG_PREFIX . 'End' , [ 'action' => 'get_status' , 'status_info' => $status_info ]);
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'statuses' , $type , [ 'status' => $status_info ]);
2019-03-02 21:10:57 +00:00
}
/**
* Retrieves the last public status of the provided user info
*
2019-03-02 22:03:13 +00:00
* @ param int $ownerId Public contact Id
* @ param int $uid User Id
2019-03-02 21:10:57 +00:00
* @ return array
2019-03-02 22:03:13 +00:00
* @ throws Exception
2019-03-02 21:10:57 +00:00
*/
2019-03-23 14:01:25 +00:00
function api_get_last_status ( $ownerId , $uid )
2019-03-02 21:10:57 +00:00
{
$condition = [
2019-05-27 15:01:13 +00:00
'author-id' => $ownerId ,
2019-03-02 21:10:57 +00:00
'uid' => $uid ,
'gravity' => [ GRAVITY_PARENT , GRAVITY_COMMENT ],
2020-03-02 07:57:23 +00:00
'private' => [ Item :: PUBLIC , Item :: UNLISTED ]
2019-03-02 21:10:57 +00:00
];
2017-11-10 05:00:50 +00:00
2019-03-02 22:03:13 +00:00
$item = api_get_item ( $condition );
2019-03-23 14:01:25 +00:00
return $item ;
2019-03-02 21:10:57 +00:00
}
2016-09-25 16:50:08 +00:00
2019-03-02 21:10:57 +00:00
/**
* Retrieves a single item record based on the provided condition and converts it for API use .
*
* @ param array $condition Item table condition array
* @ return array
2019-03-02 22:03:13 +00:00
* @ throws Exception
2019-03-02 21:10:57 +00:00
*/
2019-03-02 22:03:13 +00:00
function api_get_item ( array $condition )
2019-03-02 21:10:57 +00:00
{
2021-01-16 04:11:28 +00:00
$item = Post :: selectFirst ( Item :: DISPLAY_FIELDLIST , $condition , [ 'order' => [ 'id' => true ]]);
2019-03-02 21:10:57 +00:00
2019-03-02 22:03:13 +00:00
return $item ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
* Returns extended information of a given user , specified by ID or screen name as per the required id parameter .
* The author ' s most recent status will be returned inline .
2017-12-24 02:20:50 +00:00
*
* @ param string $type Return type ( atom , rss , xml , json )
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / follow - search - get - users / api - reference / get - users - show
2017-11-10 05:00:50 +00:00
*/
function api_users_show ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
2019-03-23 14:01:25 +00:00
$item = api_get_last_status ( $user_info [ 'pid' ], $user_info [ 'uid' ]);
2019-11-21 22:40:54 +00:00
if ( ! empty ( $item )) {
2019-03-23 14:01:25 +00:00
$user_info [ 'status' ] = api_format_item ( $item , $type );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// "uid" and "self" are only needed for some internal stuff, so remove it from here
2019-03-02 21:10:57 +00:00
unset ( $user_info [ 'uid' ]);
unset ( $user_info [ 'self' ]);
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'user' , $type , [ 'user' => $user_info ]);
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/users/show' , 'api_users_show' );
api_register_func ( 'api/externalprofile/show' , 'api_users_show' );
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
* Search a public user account .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / follow - search - get - users / api - reference / get - users - search
*/
2017-11-10 05:00:50 +00:00
function api_users_search ( $type )
{
2018-01-15 13:05:12 +00:00
$userlist = [];
2016-09-25 16:50:08 +00:00
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_GET [ 'q' ])) {
2019-12-12 22:04:08 +00:00
$contacts = Contact :: selectToArray (
[ 'id' ],
[
'`uid` = 0 AND (`name` = ? OR `nick` = ? OR `url` = ? OR `addr` = ?)' ,
$_GET [ 'q' ],
$_GET [ 'q' ],
$_GET [ 'q' ],
$_GET [ 'q' ],
]
);
2016-09-25 16:50:08 +00:00
2019-12-12 22:04:08 +00:00
if ( DBA :: isResult ( $contacts )) {
2017-11-10 05:00:50 +00:00
$k = 0 ;
2019-12-12 22:04:08 +00:00
foreach ( $contacts as $contact ) {
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromContactId ( $contact [ 'id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2019-12-12 22:04:08 +00:00
if ( $type == 'xml' ) {
$userlist [ $k ++ . ':user' ] = $user_info ;
2017-11-10 05:00:50 +00:00
} else {
$userlist [] = $user_info ;
}
}
2019-12-12 22:04:08 +00:00
$userlist = [ 'users' => $userlist ];
2017-11-10 05:00:50 +00:00
} else {
2019-12-12 22:04:08 +00:00
throw new NotFoundException ( 'User ' . $_GET [ 'q' ] . ' not found.' );
2017-04-05 20:07:55 +00:00
}
2017-11-10 05:00:50 +00:00
} else {
2019-12-12 22:04:08 +00:00
throw new BadRequestException ( 'No search term specified.' );
2016-09-25 16:50:08 +00:00
}
2018-01-04 01:54:35 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'users' , $type , $userlist );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/users/search' , 'api_users_search' );
2016-09-25 16:50:08 +00:00
2017-12-18 22:51:03 +00:00
/**
* Return user objects
*
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / follow - search - get - users / api - reference / get - users - lookup
*
* @ param string $type Return format : json or xml
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
2017-12-19 13:52:46 +00:00
* @ throws NotFoundException if the results are empty .
2019-01-07 17:24:01 +00:00
* @ throws UnauthorizedException
2017-12-18 22:51:03 +00:00
*/
function api_users_lookup ( $type )
{
2018-01-15 13:05:12 +00:00
$users = [];
2017-12-19 10:37:55 +00:00
2018-07-01 08:03:57 +00:00
if ( ! empty ( $_REQUEST [ 'user_id' ])) {
2017-12-19 10:37:55 +00:00
foreach ( explode ( ',' , $_REQUEST [ 'user_id' ]) as $id ) {
if ( ! empty ( $id )) {
2021-11-08 21:35:41 +00:00
$users [] = api_get_user ( $id );
2017-12-19 10:37:55 +00:00
}
2017-12-18 22:51:03 +00:00
}
}
2017-12-19 10:37:55 +00:00
if ( empty ( $users )) {
throw new NotFoundException ;
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " users " , $type , [ 'users' => $users ]);
2017-12-18 22:51:03 +00:00
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/users/lookup' , 'api_users_lookup' , true );
2017-12-18 01:59:11 +00:00
/**
* Returns statuses that match a specified query .
*
* @ see https :// developer . twitter . com / en / docs / tweets / search / api - reference / get - search - tweets
*
* @ param string $type Return format : json , xml , atom , rss
*
* @ return array | string
2017-12-19 13:52:46 +00:00
* @ throws BadRequestException if the " q " parameter is missing .
2019-01-07 17:24:01 +00:00
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-18 01:59:11 +00:00
*/
function api_search ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2018-04-09 15:36:33 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2018-04-09 15:36:33 +00:00
2019-02-19 00:56:41 +00:00
if ( empty ( $_REQUEST [ 'q' ])) {
throw new BadRequestException ( 'q parameter is required.' );
}
2018-12-17 04:41:55 +00:00
$searchTerm = trim ( rawurldecode ( $_REQUEST [ 'q' ]));
2017-12-18 01:59:11 +00:00
2018-12-16 16:11:55 +00:00
$data = [];
2018-12-21 17:42:39 +00:00
$data [ 'status' ] = [];
2018-12-16 16:11:55 +00:00
$count = 15 ;
2018-12-21 16:00:56 +00:00
$exclude_replies = ! empty ( $_REQUEST [ 'exclude_replies' ]);
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_REQUEST [ 'rpp' ])) {
2017-12-18 12:35:36 +00:00
$count = $_REQUEST [ 'rpp' ];
2018-11-30 14:06:22 +00:00
} elseif ( ! empty ( $_REQUEST [ 'count' ])) {
2017-12-18 12:35:36 +00:00
$count = $_REQUEST [ 'count' ];
}
2020-01-05 19:32:39 +00:00
2019-10-13 02:01:34 +00:00
$since_id = $_REQUEST [ 'since_id' ] ? ? 0 ;
$max_id = $_REQUEST [ 'max_id' ] ? ? 0 ;
$page = $_REQUEST [ 'page' ] ? ? 1 ;
$start = max ( 0 , ( $page - 1 ) * $count );
2018-12-16 16:11:55 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2018-12-17 05:33:03 +00:00
if ( preg_match ( '/^#(\w+)$/' , $searchTerm , $matches ) === 1 && isset ( $matches [ 1 ])) {
2018-12-17 04:41:55 +00:00
$searchTerm = $matches [ 1 ];
2020-05-01 09:41:17 +00:00
$condition = [ " `iid` > ? AND `name` = ? AND (NOT `private` OR (`private` AND `uid` = ?)) " , $since_id , $searchTerm , local_user ()];
$tags = DBA :: select ( 'tag-search-view' , [ 'uri-id' ], $condition );
$uriids = [];
while ( $tag = DBA :: fetch ( $tags )) {
$uriids [] = $tag [ 'uri-id' ];
2018-12-21 17:42:39 +00:00
}
2020-05-01 09:41:17 +00:00
DBA :: close ( $tags );
2018-12-21 16:00:56 +00:00
2020-05-01 09:41:17 +00:00
if ( empty ( $uriids )) {
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'statuses' , $type , $data );
2018-12-21 17:42:39 +00:00
}
2020-05-01 09:41:17 +00:00
$condition = [ 'uri-id' => $uriids ];
2018-12-21 17:42:39 +00:00
if ( $exclude_replies ) {
2020-05-01 09:41:17 +00:00
$condition [ 'gravity' ] = GRAVITY_PARENT ;
2018-12-21 17:42:39 +00:00
}
2020-05-01 09:41:17 +00:00
$params [ 'group_by' ] = [ 'uri-id' ];
2018-12-17 04:41:55 +00:00
} else {
2020-01-05 19:32:39 +00:00
$condition = [ " `id` > ?
2020-05-27 12:19:06 +00:00
" . ( $exclude_replies ? " AND `gravity` = " . GRAVITY_PARENT : ' ') . "
2018-12-17 04:41:55 +00:00
AND ( `uid` = 0 OR ( `uid` = ? AND NOT `global` ))
AND `body` LIKE CONCAT ( '%' , ? , '%' ) " ,
2021-11-17 22:44:52 +00:00
$since_id , BaseApi :: getCurrentUserID (), $_REQUEST [ 'q' ]];
2018-12-17 04:41:55 +00:00
if ( $max_id > 0 ) {
2018-12-22 03:39:16 +00:00
$condition [ 0 ] .= ' AND `id` <= ?' ;
2018-12-17 04:41:55 +00:00
$condition [] = $max_id ;
}
2018-06-09 19:12:13 +00:00
}
2018-06-09 16:56:37 +00:00
2019-12-12 22:04:59 +00:00
$statuses = [];
if ( parse_url ( $searchTerm , PHP_URL_SCHEME ) != '' ) {
2021-11-17 22:44:52 +00:00
$id = Item :: fetchByLink ( $searchTerm , BaseApi :: getCurrentUserID ());
2019-12-12 22:04:59 +00:00
if ( ! $id ) {
// Public post
$id = Item :: fetchByLink ( $searchTerm );
}
if ( ! empty ( $id )) {
2021-01-16 22:37:27 +00:00
$statuses = Post :: select ([], [ 'id' => $id ]);
2019-12-12 22:04:59 +00:00
}
}
2021-11-17 22:44:52 +00:00
$statuses = $statuses ? : Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2017-12-18 12:35:36 +00:00
2021-01-16 22:37:27 +00:00
$data [ 'status' ] = api_format_items ( Post :: toArray ( $statuses ), $user_info );
2017-12-18 12:35:36 +00:00
2018-12-21 16:00:56 +00:00
bindComments ( $data [ 'status' ]);
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'statuses' , $type , $data );
2017-12-18 01:59:11 +00:00
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/search/tweets' , 'api_search' , true );
api_register_func ( 'api/search' , 'api_search' , true );
2017-11-10 05:00:50 +00:00
/**
2017-12-24 02:20:50 +00:00
* Returns the most recent statuses posted by the user and the users they follow .
*
2019-01-07 17:24:01 +00:00
* @ see https :// developer . twitter . com / en / docs / tweets / timelines / api - reference / get - statuses - home_timeline
2017-11-10 05:00:50 +00:00
*
2017-12-24 02:20:50 +00:00
* @ param string $type Return type ( atom , rss , xml , json )
2017-11-10 05:00:50 +00:00
*
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ todo Optional parameters
* @ todo Add reply info
2017-11-10 05:00:50 +00:00
*/
function api_statuses_home_timeline ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2021-11-17 23:03:18 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
unset ( $_REQUEST [ " user_id " ]);
unset ( $_GET [ " user_id " ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
unset ( $_REQUEST [ " screen_name " ]);
unset ( $_GET [ " screen_name " ]);
2016-09-25 16:50:08 +00:00
2018-04-07 20:37:57 +00:00
// get last network messages
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// params
2019-10-13 02:01:34 +00:00
$count = $_REQUEST [ 'count' ] ? ? 20 ;
$page = $_REQUEST [ 'page' ] ? ? 0 ;
$since_id = $_REQUEST [ 'since_id' ] ? ? 0 ;
$max_id = $_REQUEST [ 'max_id' ] ? ? 0 ;
2018-11-30 14:06:22 +00:00
$exclude_replies = ! empty ( $_REQUEST [ 'exclude_replies' ]);
2019-10-13 02:01:34 +00:00
$conversation_id = $_REQUEST [ 'conversation_id' ] ? ? 0 ;
2016-09-25 16:50:08 +00:00
2019-10-13 02:01:34 +00:00
$start = max ( 0 , ( $page - 1 ) * $count );
2016-09-25 16:50:08 +00:00
2021-01-16 22:57:36 +00:00
$condition = [ " `uid` = ? AND `gravity` IN (?, ?) AND `id` > ? " ,
2021-11-17 22:44:52 +00:00
BaseApi :: getCurrentUserID (), GRAVITY_PARENT , GRAVITY_COMMENT , $since_id ];
2018-06-09 16:56:37 +00:00
2017-11-10 05:00:50 +00:00
if ( $max_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `id` <= ? " ;
2018-06-09 16:56:37 +00:00
$condition [] = $max_id ;
2017-11-10 05:00:50 +00:00
}
2018-11-30 14:06:22 +00:00
if ( $exclude_replies ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= ' AND `gravity` = ?' ;
2020-05-28 16:02:36 +00:00
$condition [] = GRAVITY_PARENT ;
2017-11-10 05:00:50 +00:00
}
if ( $conversation_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `parent` = ? " ;
2018-06-09 16:56:37 +00:00
$condition [] = $conversation_id ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-06-09 16:56:37 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2017-11-10 05:00:50 +00:00
2021-01-16 22:37:27 +00:00
$items = Post :: toArray ( $statuses );
2018-06-09 20:08:15 +00:00
$ret = api_format_items ( $items , $user_info , false , $type );
2017-11-10 05:00:50 +00:00
// Set all posts from the query above to seen
2018-01-15 13:05:12 +00:00
$idarray = [];
2018-06-09 20:08:15 +00:00
foreach ( $items as $item ) {
2017-11-10 05:00:50 +00:00
$idarray [] = intval ( $item [ " id " ]);
}
2016-09-25 16:50:08 +00:00
2018-05-02 19:26:15 +00:00
if ( ! empty ( $idarray )) {
2021-01-16 04:11:28 +00:00
$unseen = Post :: exists ([ 'unseen' => true , 'id' => $idarray ]);
2017-11-10 05:00:50 +00:00
if ( $unseen ) {
2018-05-02 19:26:15 +00:00
Item :: update ([ 'unseen' => false ], [ 'unseen' => true , 'id' => $idarray ]);
2016-09-25 16:50:08 +00:00
}
}
2019-04-24 04:26:23 +00:00
2018-12-21 16:00:56 +00:00
bindComments ( $ret );
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2017-11-10 05:00:50 +00:00
break ;
}
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-12-21 16:00:56 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/statuses/home_timeline' , 'api_statuses_home_timeline' , true );
api_register_func ( 'api/statuses/friends_timeline' , 'api_statuses_home_timeline' , true );
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
* Returns the most recent statuses from public users .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_statuses_public_timeline ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2017-04-05 20:07:55 +00:00
2018-04-07 20:37:57 +00:00
// get last network messages
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// params
2019-10-13 02:01:34 +00:00
$count = $_REQUEST [ 'count' ] ? ? 20 ;
$page = $_REQUEST [ 'page' ] ? ? 1 ;
$since_id = $_REQUEST [ 'since_id' ] ? ? 0 ;
$max_id = $_REQUEST [ 'max_id' ] ? ? 0 ;
2018-11-30 14:06:22 +00:00
$exclude_replies = ( ! empty ( $_REQUEST [ 'exclude_replies' ]) ? 1 : 0 );
2019-10-13 02:01:34 +00:00
$conversation_id = $_REQUEST [ 'conversation_id' ] ? ? 0 ;
2016-09-25 16:50:08 +00:00
2019-10-13 02:01:34 +00:00
$start = max ( 0 , ( $page - 1 ) * $count );
2016-09-25 16:50:08 +00:00
2017-12-17 17:47:15 +00:00
if ( $exclude_replies && ! $conversation_id ) {
2021-02-09 17:56:17 +00:00
$condition = [ " `gravity` = ? AND `id` > ? AND `private` = ? AND `wall` AND NOT `author-hidden` " ,
GRAVITY_PARENT , $since_id , Item :: PUBLIC ];
2016-09-25 16:50:08 +00:00
2018-06-09 19:12:13 +00:00
if ( $max_id > 0 ) {
2021-02-09 17:56:17 +00:00
$condition [ 0 ] .= " AND `id` <= ? " ;
2018-06-09 19:12:13 +00:00
$condition [] = $max_id ;
}
2018-06-09 16:56:37 +00:00
2021-02-09 17:56:17 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2017-12-17 17:47:15 +00:00
2021-01-19 07:23:01 +00:00
$r = Post :: toArray ( $statuses );
2017-12-17 17:47:15 +00:00
} else {
2021-01-16 22:57:36 +00:00
$condition = [ " `gravity` IN (?, ?) AND `id` > ? AND `private` = ? AND `wall` AND `origin` AND NOT `author-hidden` " ,
2020-03-02 07:57:23 +00:00
GRAVITY_PARENT , GRAVITY_COMMENT , $since_id , Item :: PUBLIC ];
2017-12-17 17:47:15 +00:00
2018-06-09 19:12:13 +00:00
if ( $max_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `id` <= ? " ;
2018-06-09 19:12:13 +00:00
$condition [] = $max_id ;
}
if ( $conversation_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `parent` = ? " ;
2018-06-09 19:12:13 +00:00
$condition [] = $conversation_id ;
}
2018-06-09 16:56:37 +00:00
2018-06-09 19:12:13 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2017-12-17 17:47:15 +00:00
2021-01-16 22:37:27 +00:00
$r = Post :: toArray ( $statuses );
2017-12-17 17:47:15 +00:00
}
2017-11-10 05:00:50 +00:00
$ret = api_format_items ( $r , $user_info , false , $type );
2018-12-21 16:00:56 +00:00
bindComments ( $ret );
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2017-11-10 05:00:50 +00:00
break ;
}
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/statuses/public_timeline' , 'api_statuses_public_timeline' , true );
2016-09-25 16:50:08 +00:00
2017-12-16 20:41:50 +00:00
/**
2017-12-24 02:20:50 +00:00
* Returns the most recent statuses posted by users this node knows about .
*
2017-12-16 20:41:50 +00:00
* @ param string $type Return format : json , xml , atom , rss
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
2017-12-16 20:41:50 +00:00
* @ throws ForbiddenException
2019-01-07 17:24:01 +00:00
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-16 20:41:50 +00:00
*/
function api_statuses_networkpublic_timeline ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2017-12-16 20:41:50 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2017-12-16 20:41:50 +00:00
2019-10-13 02:01:34 +00:00
$since_id = $_REQUEST [ 'since_id' ] ? ? 0 ;
$max_id = $_REQUEST [ 'max_id' ] ? ? 0 ;
2017-12-16 20:41:50 +00:00
// pagination
2019-10-13 02:01:34 +00:00
$count = $_REQUEST [ 'count' ] ? ? 20 ;
$page = $_REQUEST [ 'page' ] ? ? 1 ;
$start = max ( 0 , ( $page - 1 ) * $count );
2017-12-16 20:41:50 +00:00
2021-02-04 22:51:33 +00:00
$condition = [ " `uid` = 0 AND `gravity` IN (?, ?) AND `id` > ? AND `private` = ? " ,
2020-03-02 07:57:23 +00:00
GRAVITY_PARENT , GRAVITY_COMMENT , $since_id , Item :: PUBLIC ];
2017-12-16 20:41:50 +00:00
2018-06-09 19:12:13 +00:00
if ( $max_id > 0 ) {
2021-02-04 22:51:33 +00:00
$condition [ 0 ] .= " AND `id` <= ? " ;
2018-06-09 19:12:13 +00:00
$condition [] = $max_id ;
}
2017-12-16 20:41:50 +00:00
2021-02-04 22:51:33 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2021-11-17 22:44:52 +00:00
$statuses = Post :: toArray ( Post :: selectForUser ( BaseApi :: getCurrentUserID (), Item :: DISPLAY_FIELDLIST , $condition , $params ));
2021-02-05 07:14:09 +00:00
2021-02-05 06:33:34 +00:00
$ret = api_format_items ( $statuses , $user_info , false , $type );
2021-02-05 07:14:09 +00:00
2018-12-21 16:00:56 +00:00
bindComments ( $ret );
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2017-12-16 20:41:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-12-16 20:41:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2017-12-16 20:41:50 +00:00
break ;
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2017-12-16 20:41:50 +00:00
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/statuses/networkpublic_timeline' , 'api_statuses_networkpublic_timeline' , true );
2017-11-10 05:00:50 +00:00
/**
2017-12-24 02:20:50 +00:00
* Returns a single status .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / tweets / post - and - engage / api - reference / get - statuses - show - id
2017-11-10 05:00:50 +00:00
*/
function api_statuses_show ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// params
2021-07-25 14:27:13 +00:00
$id = intval ( DI :: args () -> getArgv ()[ 3 ] ? ? 0 );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $id == 0 ) {
2019-10-13 02:01:34 +00:00
$id = intval ( $_REQUEST [ 'id' ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Hotot workaround
if ( $id == 0 ) {
2021-07-25 14:27:13 +00:00
$id = intval ( DI :: args () -> getArgv ()[ 4 ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-10-03 19:43:49 +00:00
logger :: notice ( 'API: api_statuses_show: ' . $id );
2016-09-25 16:50:08 +00:00
2018-07-01 09:17:59 +00:00
$conversation = ! empty ( $_REQUEST [ 'conversation' ]);
2016-09-25 16:50:08 +00:00
2018-05-10 10:13:48 +00:00
// try to fetch the item for the local user - or the public item, if there is no local one
2021-01-17 12:49:53 +00:00
$uri_item = Post :: selectFirst ([ 'uri-id' ], [ 'id' => $id ]);
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $uri_item )) {
2021-01-17 12:49:53 +00:00
throw new BadRequestException ( sprintf ( " There is no status with the id %d " , $id ));
2018-05-10 10:13:48 +00:00
}
2021-11-17 22:44:52 +00:00
$item = Post :: selectFirst ([ 'id' ], [ 'uri-id' => $uri_item [ 'uri-id' ], 'uid' => [ 0 , BaseApi :: getCurrentUserID ()]], [ 'order' => [ 'uid' => true ]]);
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $item )) {
2021-01-17 12:49:53 +00:00
throw new BadRequestException ( sprintf ( " There is no status with the uri-id %d for the given user. " , $uri_item [ 'uri-id' ]));
2018-05-10 10:13:48 +00:00
}
$id = $item [ 'id' ];
2018-06-09 16:56:37 +00:00
if ( $conversation ) {
2018-06-27 18:09:33 +00:00
$condition = [ 'parent' => $id , 'gravity' => [ GRAVITY_PARENT , GRAVITY_COMMENT ]];
2018-06-09 16:56:37 +00:00
$params = [ 'order' => [ 'id' => true ]];
} else {
2018-06-27 18:09:33 +00:00
$condition = [ 'id' => $id , 'gravity' => [ GRAVITY_PARENT , GRAVITY_COMMENT ]];
2018-06-09 16:56:37 +00:00
$params = [];
}
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2017-11-10 05:00:50 +00:00
/// @TODO How about copying this to above methods which don't check $r ?
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $statuses )) {
2021-01-17 12:49:53 +00:00
throw new BadRequestException ( sprintf ( " There is no status or conversation with the id %d. " , $id ));
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-01-16 22:37:27 +00:00
$ret = api_format_items ( Post :: toArray ( $statuses ), $user_info , false , $type );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $conversation ) {
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2017-11-10 05:00:50 +00:00
} else {
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret [ 0 ]];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " status " , $type , $data );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/statuses/show' , 'api_statuses_show' , true );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2017-12-24 02:20:50 +00:00
*
* @ param string $type Return type ( atom , rss , xml , json )
*
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ todo nothing to say ?
2017-11-10 05:00:50 +00:00
*/
function api_conversation_show ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// params
2021-07-25 14:27:13 +00:00
$id = intval ( DI :: args () -> getArgv ()[ 3 ] ? ? 0 );
2019-10-13 02:01:34 +00:00
$since_id = intval ( $_REQUEST [ 'since_id' ] ? ? 0 );
$max_id = intval ( $_REQUEST [ 'max_id' ] ? ? 0 );
$count = intval ( $_REQUEST [ 'count' ] ? ? 20 );
$page = intval ( $_REQUEST [ 'page' ] ? ? 1 );
2016-09-25 16:50:08 +00:00
2019-10-13 02:01:34 +00:00
$start = max ( 0 , ( $page - 1 ) * $count );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $id == 0 ) {
2019-10-13 02:01:34 +00:00
$id = intval ( $_REQUEST [ 'id' ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Hotot workaround
if ( $id == 0 ) {
2021-07-25 14:27:13 +00:00
$id = intval ( DI :: args () -> getArgv ()[ 4 ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-12-30 20:42:56 +00:00
Logger :: info ( API_LOG_PREFIX . '{subaction}' , [ 'module' => 'api' , 'action' => 'conversation' , 'subaction' => 'show' , 'id' => $id ]);
2016-09-25 16:50:08 +00:00
2018-05-10 10:13:48 +00:00
// try to fetch the item for the local user - or the public item, if there is no local one
2021-01-17 14:57:55 +00:00
$item = Post :: selectFirst ([ 'parent-uri-id' ], [ 'id' => $id ]);
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $item )) {
2021-11-08 07:23:18 +00:00
throw new BadRequestException ( " There is no status with the id $id . " );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$parent = Post :: selectFirst ([ 'id' ], [ 'uri-id' => $item [ 'parent-uri-id' ], 'uid' => [ 0 , BaseApi :: getCurrentUserID ()]], [ 'order' => [ 'uid' => true ]]);
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $parent )) {
2018-05-10 10:13:48 +00:00
throw new BadRequestException ( " There is no status with this id. " );
}
$id = $parent [ 'id' ];
2021-01-16 22:57:36 +00:00
$condition = [ " `parent` = ? AND `uid` IN (0, ?) AND `gravity` IN (?, ?) AND `id` > ? " ,
2021-11-17 22:44:52 +00:00
$id , BaseApi :: getCurrentUserID (), GRAVITY_PARENT , GRAVITY_COMMENT , $since_id ];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $max_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `id` <= ? " ;
2018-06-09 16:56:37 +00:00
$condition [] = $max_id ;
}
2017-11-10 05:00:50 +00:00
2018-06-09 16:56:37 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2018-06-09 16:56:37 +00:00
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $statuses )) {
2018-06-09 16:56:37 +00:00
throw new BadRequestException ( " There is no status with id $id . " );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-01-16 22:37:27 +00:00
$ret = api_format_items ( Post :: toArray ( $statuses ), $user_info , false , $type );
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/conversation/show' , 'api_conversation_show' , true );
api_register_func ( 'api/statusnet/conversation' , 'api_conversation_show' , true );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2017-12-24 02:20:50 +00:00
* Repeats a status .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / tweets / post - and - engage / api - reference / post - statuses - retweet - id
2017-11-10 05:00:50 +00:00
*/
function api_statuses_repeat ( $type )
{
global $called_api ;
2016-09-25 16:50:08 +00:00
2020-01-04 22:42:01 +00:00
$a = DI :: app ();
2016-09-25 16:50:08 +00:00
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2018-01-04 18:26:09 +00:00
2017-11-10 05:00:50 +00:00
// params
2021-07-25 14:27:13 +00:00
$id = intval ( DI :: args () -> getArgv ()[ 3 ] ? ? 0 );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $id == 0 ) {
2019-10-13 02:01:34 +00:00
$id = intval ( $_REQUEST [ 'id' ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Hotot workaround
if ( $id == 0 ) {
2021-07-25 14:27:13 +00:00
$id = intval ( DI :: args () -> getArgv ()[ 4 ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-10-03 19:49:55 +00:00
logger :: notice ( 'API: api_statuses_repeat: ' . $id );
2017-11-10 05:00:50 +00:00
2020-11-07 08:22:59 +00:00
$fields = [ 'uri-id' , 'network' , 'body' , 'title' , 'author-name' , 'author-link' , 'author-avatar' , 'guid' , 'created' , 'plink' ];
2021-01-17 20:56:15 +00:00
$item = Post :: selectFirst ( $fields , [ 'id' => $id , 'private' => [ Item :: PUBLIC , Item :: UNLISTED ]]);
2021-04-26 06:50:12 +00:00
2021-01-17 21:24:58 +00:00
if ( DBA :: isResult ( $item ) && ! empty ( $item [ 'body' ])) {
2020-11-07 08:22:59 +00:00
if ( in_array ( $item [ 'network' ], [ Protocol :: ACTIVITYPUB , Protocol :: DFRN , Protocol :: TWITTER ])) {
if ( ! Item :: performActivity ( $id , 'announce' , local_user ())) {
throw new InternalServerErrorException ();
}
2021-04-26 06:50:12 +00:00
2020-11-07 08:22:59 +00:00
$item_id = $id ;
2017-11-10 05:00:50 +00:00
} else {
2020-11-07 08:22:59 +00:00
if ( strpos ( $item [ 'body' ], " [/share] " ) !== false ) {
$pos = strpos ( $item [ 'body' ], " [share " );
$post = substr ( $item [ 'body' ], $pos );
} else {
$post = BBCode :: getShareOpeningTag ( $item [ 'author-name' ], $item [ 'author-link' ], $item [ 'author-avatar' ], $item [ 'plink' ], $item [ 'created' ], $item [ 'guid' ]);
if ( ! empty ( $item [ 'title' ])) {
$post .= '[h3]' . $item [ 'title' ] . " [/h3] \n " ;
}
2016-09-25 16:50:08 +00:00
2020-11-07 08:22:59 +00:00
$post .= $item [ 'body' ];
$post .= " [/share] " ;
2019-12-05 05:24:29 +00:00
}
2020-11-07 08:22:59 +00:00
$_REQUEST [ 'body' ] = $post ;
2021-11-17 22:44:52 +00:00
$_REQUEST [ 'profile_uid' ] = BaseApi :: getCurrentUserID ();
2020-11-07 08:22:59 +00:00
$_REQUEST [ 'api_source' ] = true ;
2019-12-05 05:24:29 +00:00
2020-11-07 08:22:59 +00:00
if ( empty ( $_REQUEST [ 'source' ])) {
$_REQUEST [ " source " ] = api_source ();
}
2016-09-25 16:50:08 +00:00
2020-11-07 08:22:59 +00:00
$item_id = item_post ( $a );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
} else {
2021-01-17 21:57:50 +00:00
throw new ForbiddenException ();
2016-09-25 16:50:08 +00:00
}
2018-09-02 07:35:12 +00:00
// output the post that we just posted.
2018-04-24 14:16:57 +00:00
$called_api = [];
2018-09-02 07:20:04 +00:00
return api_status_show ( $type , $item_id );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/statuses/retweet' , 'api_statuses_repeat' , true , API_METHOD_POST );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2017-12-24 02:20:50 +00:00
* Destroys a specific status .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / tweets / post - and - engage / api - reference / post - statuses - destroy - id
2017-11-10 05:00:50 +00:00
*/
function api_statuses_destroy ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2018-01-04 18:26:09 +00:00
2017-11-10 05:00:50 +00:00
// params
2021-07-25 14:27:13 +00:00
$id = intval ( DI :: args () -> getArgv ()[ 3 ] ? ? 0 );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $id == 0 ) {
2019-10-13 02:01:34 +00:00
$id = intval ( $_REQUEST [ 'id' ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Hotot workaround
if ( $id == 0 ) {
2021-07-25 14:27:13 +00:00
$id = intval ( DI :: args () -> getArgv ()[ 4 ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-10-03 19:49:55 +00:00
logger :: notice ( 'API: api_statuses_destroy: ' . $id );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$ret = api_statuses_show ( $type );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
Item :: deleteForUser ([ 'id' => $id ], BaseApi :: getCurrentUserID ());
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
return $ret ;
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/statuses/destroy' , 'api_statuses_destroy' , true , API_METHOD_DELETE );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2017-12-24 02:20:50 +00:00
* Returns the most recent mentions .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see http :// developer . twitter . com / doc / get / statuses / mentions
2017-11-10 05:00:50 +00:00
*/
function api_statuses_mentions ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
unset ( $_REQUEST [ " user_id " ]);
unset ( $_GET [ " user_id " ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
unset ( $_REQUEST [ " screen_name " ]);
unset ( $_GET [ " screen_name " ]);
2016-09-25 16:50:08 +00:00
2018-04-07 20:37:57 +00:00
// get last network messages
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
// params
2020-11-27 11:24:31 +00:00
$since_id = intval ( $_REQUEST [ 'since_id' ] ? ? 0 );
$max_id = intval ( $_REQUEST [ 'max_id' ] ? ? 0 );
$count = intval ( $_REQUEST [ 'count' ] ? ? 20 );
$page = intval ( $_REQUEST [ 'page' ] ? ? 1 );
2016-09-25 16:50:08 +00:00
2019-10-13 02:01:34 +00:00
$start = max ( 0 , ( $page - 1 ) * $count );
2016-09-25 16:50:08 +00:00
2021-02-01 20:11:25 +00:00
$query = " `gravity` IN (?, ?) AND `uri-id` IN
( SELECT `uri-id` FROM `post-user-notification` WHERE `uid` = ? AND `notification-type` & ? != 0 ORDER BY `uri-id` )
AND ( `uid` = 0 OR ( `uid` = ? AND NOT `global` )) AND `id` > ? " ;
2021-01-17 13:10:32 +00:00
2021-09-18 02:14:32 +00:00
$condition = [
GRAVITY_PARENT , GRAVITY_COMMENT ,
2021-11-17 22:44:52 +00:00
BaseApi :: getCurrentUserID (),
2021-09-18 02:14:32 +00:00
Post\UserNotification :: TYPE_EXPLICIT_TAGGED | Post\UserNotification :: TYPE_IMPLICIT_TAGGED |
Post\UserNotification :: TYPE_THREAD_COMMENT | Post\UserNotification :: TYPE_DIRECT_COMMENT |
Post\UserNotification :: TYPE_DIRECT_THREAD_COMMENT ,
2021-11-17 22:44:52 +00:00
BaseApi :: getCurrentUserID (), $since_id ,
2021-09-18 02:14:32 +00:00
];
2018-02-11 21:13:29 +00:00
2017-11-10 05:00:50 +00:00
if ( $max_id > 0 ) {
2021-01-31 18:32:22 +00:00
$query .= " AND `id` <= ? " ;
2018-06-09 16:56:37 +00:00
$condition [] = $max_id ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-01-17 13:10:32 +00:00
array_unshift ( $condition , $query );
2020-01-05 19:32:39 +00:00
2018-06-09 16:56:37 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2017-11-10 05:00:50 +00:00
2021-01-16 22:37:27 +00:00
$ret = api_format_items ( Post :: toArray ( $statuses ), $user_info , false , $type );
2017-11-10 05:00:50 +00:00
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2017-11-10 05:00:50 +00:00
break ;
}
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/statuses/mentions' , 'api_statuses_mentions' , true );
api_register_func ( 'api/statuses/replies' , 'api_statuses_mentions' , true );
2016-09-25 16:50:08 +00:00
2017-12-16 16:34:44 +00:00
/**
2017-12-24 02:20:50 +00:00
* Returns the most recent statuses posted by the user .
*
2017-12-16 16:34:44 +00:00
* @ param string $type Either " json " or " xml "
* @ return string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
2017-12-16 16:34:44 +00:00
* @ throws ForbiddenException
2019-01-07 17:24:01 +00:00
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
* @ see https :// developer . twitter . com / en / docs / tweets / timelines / api - reference / get - statuses - user_timeline
2017-12-16 16:34:44 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_statuses_user_timeline ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
Logger :: info ( 'api_statuses_user_timeline' , [ 'api_user' => BaseApi :: getCurrentUserID (), 'user_info' => $user_info , '_REQUEST' => $_REQUEST ]);
2017-11-10 05:00:50 +00:00
2019-10-13 02:01:34 +00:00
$since_id = $_REQUEST [ 'since_id' ] ? ? 0 ;
$max_id = $_REQUEST [ 'max_id' ] ? ? 0 ;
2018-11-30 14:06:22 +00:00
$exclude_replies = ! empty ( $_REQUEST [ 'exclude_replies' ]);
2019-10-13 02:01:34 +00:00
$conversation_id = $_REQUEST [ 'conversation_id' ] ? ? 0 ;
2016-09-25 16:50:08 +00:00
2017-12-16 16:34:44 +00:00
// pagination
2019-10-13 02:01:34 +00:00
$count = $_REQUEST [ 'count' ] ? ? 20 ;
$page = $_REQUEST [ 'page' ] ? ? 1 ;
$start = max ( 0 , ( $page - 1 ) * $count );
2016-09-25 16:50:08 +00:00
2021-01-16 22:57:36 +00:00
$condition = [ " `uid` = ? AND `gravity` IN (?, ?) AND `id` > ? AND `contact-id` = ? " ,
2021-11-17 22:44:52 +00:00
BaseApi :: getCurrentUserID (), GRAVITY_PARENT , GRAVITY_COMMENT , $since_id , $user_info [ 'cid' ]];
2018-06-09 16:56:37 +00:00
2017-11-10 05:00:50 +00:00
if ( $user_info [ 'self' ] == 1 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= ' AND `wall` ' ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-11-30 14:06:22 +00:00
if ( $exclude_replies ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= ' AND `gravity` = ?' ;
2020-05-28 16:02:36 +00:00
$condition [] = GRAVITY_PARENT ;
2017-11-10 05:00:50 +00:00
}
2017-12-16 16:34:44 +00:00
2017-11-10 05:00:50 +00:00
if ( $conversation_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `parent` = ? " ;
2018-06-09 16:56:37 +00:00
$condition [] = $conversation_id ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-16 16:34:44 +00:00
if ( $max_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `id` <= ? " ;
2018-06-09 16:56:37 +00:00
$condition [] = $max_id ;
2017-12-16 16:34:44 +00:00
}
2018-06-09 16:56:37 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2017-11-10 05:00:50 +00:00
2021-01-16 22:37:27 +00:00
$ret = api_format_items ( Post :: toArray ( $statuses ), $user_info , true , $type );
2017-11-10 05:00:50 +00:00
2018-12-21 16:00:56 +00:00
bindComments ( $ret );
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2017-11-10 05:00:50 +00:00
break ;
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2017-12-23 23:27:45 +00:00
api_register_func ( 'api/statuses/user_timeline' , 'api_statuses_user_timeline' , true );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2017-12-24 02:20:50 +00:00
* Star / unstar an item .
2017-11-10 05:00:50 +00:00
* param : id : id of the item
*
2017-12-24 02:20:50 +00:00
* @ param string $type Return type ( atom , rss , xml , json )
*
2019-01-07 17:24:01 +00:00
* @ return array | string
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// web . archive . org / web / 20131019055350 / https :// dev . twitter . com / docs / api / 1 / post / favorites / create /% 3 Aid
2017-11-10 05:00:50 +00:00
*/
function api_favorites_create_destroy ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// for versioned api.
/// @TODO We need a better global soluton
$action_argv_id = 2 ;
2021-07-25 14:27:13 +00:00
if ( count ( DI :: args () -> getArgv ()) > 1 && DI :: args () -> getArgv ()[ 1 ] == " 1.1 " ) {
2017-11-10 05:00:50 +00:00
$action_argv_id = 3 ;
}
2016-09-25 16:50:08 +00:00
2021-07-25 14:27:13 +00:00
if ( DI :: args () -> getArgc () <= $action_argv_id ) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( " Invalid request. " );
}
2021-07-25 14:27:13 +00:00
$action = str_replace ( " . " . $type , " " , DI :: args () -> getArgv ()[ $action_argv_id ]);
if ( DI :: args () -> getArgc () == $action_argv_id + 2 ) {
$itemid = intval ( DI :: args () -> getArgv ()[ $action_argv_id + 1 ] ? ? 0 );
2017-11-10 05:00:50 +00:00
} else {
2019-10-13 02:01:34 +00:00
$itemid = intval ( $_REQUEST [ 'id' ] ? ? 0 );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$item = Post :: selectFirstForUser ( BaseApi :: getCurrentUserID (), [], [ 'id' => $itemid , 'uid' => BaseApi :: getCurrentUserID ()]);
2016-09-25 16:50:08 +00:00
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $item )) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( " Invalid item. " );
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
switch ( $action ) {
case " create " :
2018-06-09 16:56:37 +00:00
$item [ 'starred' ] = 1 ;
2017-11-10 05:00:50 +00:00
break ;
case " destroy " :
2018-06-09 16:56:37 +00:00
$item [ 'starred' ] = 0 ;
2017-11-10 05:00:50 +00:00
break ;
default :
throw new BadRequestException ( " Invalid action " . $action );
}
2016-09-25 16:50:08 +00:00
2018-06-09 16:56:37 +00:00
$r = Item :: update ([ 'starred' => $item [ 'starred' ]], [ 'id' => $itemid ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $r === false ) {
throw new InternalServerErrorException ( " DB error " );
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2018-06-09 16:56:37 +00:00
$rets = api_format_items ([ $item ], $user_info , false , $type );
2017-11-10 05:00:50 +00:00
$ret = $rets [ 0 ];
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " status " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/favorites/create' , 'api_favorites_create_destroy' , true , API_METHOD_POST );
api_register_func ( 'api/favorites/destroy' , 'api_favorites_create_destroy' , true , API_METHOD_DELETE );
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
* Returns the most recent favorite statuses .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_favorites ( $type )
{
global $called_api ;
2016-09-25 16:50:08 +00:00
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2017-11-10 05:00:50 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
2018-01-15 13:05:12 +00:00
$called_api = [];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// in friendica starred item are private
// return favorites only for self
2018-12-30 20:42:56 +00:00
Logger :: info ( API_LOG_PREFIX . 'for {self}' , [ 'module' => 'api' , 'action' => 'favorites' , 'self' => $user_info [ 'self' ]]);
2017-11-10 05:00:50 +00:00
if ( $user_info [ 'self' ] == 0 ) {
2018-01-15 13:05:12 +00:00
$ret = [];
2017-11-10 05:00:50 +00:00
} else {
2016-09-25 16:50:08 +00:00
// params
2019-10-13 02:01:34 +00:00
$since_id = $_REQUEST [ 'since_id' ] ? ? 0 ;
$max_id = $_REQUEST [ 'max_id' ] ? ? 0 ;
$count = $_GET [ 'count' ] ? ? 20 ;
$page = $_REQUEST [ 'page' ] ? ? 1 ;
2016-09-25 16:50:08 +00:00
2019-10-13 02:01:34 +00:00
$start = max ( 0 , ( $page - 1 ) * $count );
2016-09-25 16:50:08 +00:00
2018-06-27 18:09:33 +00:00
$condition = [ " `uid` = ? AND `gravity` IN (?, ?) AND `id` > ? AND `starred` " ,
2021-11-17 22:44:52 +00:00
BaseApi :: getCurrentUserID (), GRAVITY_PARENT , GRAVITY_COMMENT , $since_id ];
2018-06-09 16:56:37 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2017-04-05 20:07:55 +00:00
if ( $max_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `id` <= ? " ;
2018-06-09 16:56:37 +00:00
$condition [] = $max_id ;
2017-04-05 20:07:55 +00:00
}
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2016-09-25 16:50:08 +00:00
2021-01-16 22:37:27 +00:00
$ret = api_format_items ( Post :: toArray ( $statuses ), $user_info , false , $type );
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2018-12-21 16:00:56 +00:00
bindComments ( $ret );
2018-01-15 13:05:12 +00:00
$data = [ 'status' => $ret ];
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/favorites' , 'api_favorites' , true );
2017-12-24 02:20:50 +00:00
/**
*
* @ param array $item
* @ param array $recipient
* @ param array $sender
*
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws InternalServerErrorException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_format_messages ( $item , $recipient , $sender )
{
// standard meta information
2018-01-15 13:05:12 +00:00
$ret = [
2018-07-01 18:42:38 +00:00
'id' => $item [ 'id' ],
2018-10-01 17:36:23 +00:00
'sender_id' => $sender [ 'id' ],
2018-07-01 18:42:38 +00:00
'text' => " " ,
'recipient_id' => $recipient [ 'id' ],
2019-10-13 02:01:34 +00:00
'created_at' => api_date ( $item [ 'created' ] ? ? DateTimeFormat :: utcNow ()),
2018-07-01 18:42:38 +00:00
'sender_screen_name' => $sender [ 'screen_name' ],
'recipient_screen_name' => $recipient [ 'screen_name' ],
'sender' => $sender ,
'recipient' => $recipient ,
'title' => " " ,
2019-10-13 02:01:34 +00:00
'friendica_seen' => $item [ 'seen' ] ? ? 0 ,
'friendica_parent_uri' => $item [ 'parent-uri' ] ? ? '' ,
2018-01-15 13:05:12 +00:00
];
2017-11-10 05:00:50 +00:00
// "uid" and "self" are only needed for some internal stuff, so remove it from here
2018-07-01 18:42:38 +00:00
if ( isset ( $ret [ 'sender' ][ 'uid' ])) {
unset ( $ret [ 'sender' ][ 'uid' ]);
}
if ( isset ( $ret [ 'sender' ][ 'self' ])) {
unset ( $ret [ 'sender' ][ 'self' ]);
}
if ( isset ( $ret [ 'recipient' ][ 'uid' ])) {
unset ( $ret [ 'recipient' ][ 'uid' ]);
}
if ( isset ( $ret [ 'recipient' ][ 'self' ])) {
unset ( $ret [ 'recipient' ][ 'self' ]);
}
2017-11-10 05:00:50 +00:00
//don't send title to regular StatusNET requests to avoid confusing these apps
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_GET [ 'getText' ])) {
2017-11-10 05:00:50 +00:00
$ret [ 'title' ] = $item [ 'title' ];
if ( $_GET [ 'getText' ] == 'html' ) {
2021-07-10 12:58:48 +00:00
$ret [ 'text' ] = BBCode :: convertForUriId ( $item [ 'uri-id' ], $item [ 'body' ], BBCode :: API );
2017-11-10 05:00:50 +00:00
} elseif ( $_GET [ 'getText' ] == 'plain' ) {
2021-07-10 12:58:48 +00:00
$ret [ 'text' ] = trim ( HTML :: toPlaintext ( BBCode :: convertForUriId ( $item [ 'uri-id' ], api_clean_plain_items ( $item [ 'body' ]), BBCode :: API ), 0 ));
2017-11-10 05:00:50 +00:00
}
} else {
2021-07-10 12:58:48 +00:00
$ret [ 'text' ] = $item [ 'title' ] . " \n " . HTML :: toPlaintext ( BBCode :: convertForUriId ( $item [ 'uri-id' ], api_clean_plain_items ( $item [ 'body' ]), BBCode :: API ), 0 );
2017-11-10 05:00:50 +00:00
}
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_GET [ 'getUserObjects' ]) && $_GET [ 'getUserObjects' ] == 'false' ) {
2017-11-10 05:00:50 +00:00
unset ( $ret [ 'sender' ]);
unset ( $ret [ 'recipient' ]);
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
return $ret ;
}
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
*
* @ param array $item
*
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws InternalServerErrorException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_convert_item ( $item )
{
2021-04-26 06:50:12 +00:00
$body = api_add_attachments_to_body ( $item );
2021-07-06 06:38:15 +00:00
$entities = api_get_entitities ( $statustext , $body , $item [ 'uri-id' ]);
2019-12-13 10:12:40 +00:00
// Add pictures to the attachment array and remove them from the body
2021-07-06 06:38:15 +00:00
$attachments = api_get_attachments ( $body , $item [ 'uri-id' ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Workaround for ostatus messages where the title is identically to the body
2021-07-10 12:58:48 +00:00
$html = BBCode :: convertForUriId ( $item [ 'uri-id' ], api_clean_plain_items ( $body ), BBCode :: API );
2018-03-07 21:29:44 +00:00
$statusbody = trim ( HTML :: toPlaintext ( $html , 0 ));
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// handle data: images
$statusbody = api_format_items_embeded_images ( $item , $statusbody );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$statustitle = trim ( $item [ 'title' ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if (( $statustitle != '' ) && ( strpos ( $statusbody , $statustitle ) !== false )) {
$statustext = trim ( $statusbody );
} else {
$statustext = trim ( $statustitle . " \n \n " . $statusbody );
}
2016-09-25 16:50:08 +00:00
2019-10-13 02:01:34 +00:00
if ((( $item [ 'network' ] ? ? Protocol :: PHANTOM ) == Protocol :: FEED ) && ( mb_strlen ( $statustext ) > 1000 )) {
$statustext = mb_substr ( $statustext , 0 , 1000 ) . " ... \n " . ( $item [ 'plink' ] ? ? '' );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-07-10 12:58:48 +00:00
$statushtml = BBCode :: convertForUriId ( $item [ 'uri-id' ], BBCode :: removeAttachment ( $body ), BBCode :: API );
2017-11-10 05:00:50 +00:00
// Workaround for clients with limited HTML parser functionality
2018-01-15 13:05:12 +00:00
$search = [ " <br> " , " <blockquote> " , " </blockquote> " ,
2017-11-10 05:00:50 +00:00
" <h1> " , " </h1> " , " <h2> " , " </h2> " ,
" <h3> " , " </h3> " , " <h4> " , " </h4> " ,
2018-01-15 13:05:12 +00:00
" <h5> " , " </h5> " , " <h6> " , " </h6> " ];
$replace = [ " <br> " , " <br><blockquote> " , " </blockquote><br> " ,
2017-11-10 05:00:50 +00:00
" <br><h1> " , " </h1><br> " , " <br><h2> " , " </h2><br> " ,
" <br><h3> " , " </h3><br> " , " <br><h4> " , " </h4><br> " ,
2018-01-15 13:05:12 +00:00
" <br><h5> " , " </h5><br> " , " <br><h6> " , " </h6><br> " ];
2017-11-10 05:00:50 +00:00
$statushtml = str_replace ( $search , $replace , $statushtml );
if ( $item [ 'title' ] != " " ) {
2021-07-10 12:58:48 +00:00
$statushtml = " <br><h4> " . BBCode :: convertForUriId ( $item [ 'uri-id' ], $item [ 'title' ]) . " </h4><br> " . $statushtml ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
do {
$oldtext = $statushtml ;
$statushtml = str_replace ( " <br><br> " , " <br> " , $statushtml );
} while ( $oldtext != $statushtml );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( substr ( $statushtml , 0 , 4 ) == '<br>' ) {
$statushtml = substr ( $statushtml , 4 );
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( substr ( $statushtml , 0 , - 4 ) == '<br>' ) {
$statushtml = substr ( $statushtml , - 4 );
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// feeds without body should contain the link
2019-10-13 02:01:34 +00:00
if ((( $item [ 'network' ] ? ? Protocol :: PHANTOM ) == Protocol :: FEED ) && ( strlen ( $item [ 'body' ]) == 0 )) {
2021-07-10 12:58:48 +00:00
$statushtml .= BBCode :: convertForUriId ( $item [ 'uri-id' ], $item [ 'plink' ]);
2016-09-25 16:50:08 +00:00
}
2018-01-15 13:05:12 +00:00
return [
2017-11-10 05:00:50 +00:00
" text " => $statustext ,
" html " => $statushtml ,
" attachments " => $attachments ,
" entities " => $entities
2018-01-15 13:05:12 +00:00
];
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-04-26 06:50:12 +00:00
/**
* Add media attachments to the body
*
* @ param array $item
* @ return string body with added media
*/
function api_add_attachments_to_body ( array $item )
{
2021-05-01 15:48:19 +00:00
$body = Post\Media :: addAttachmentsToBody ( $item [ 'uri-id' ], $item [ 'body' ]);
2021-04-26 06:50:12 +00:00
if ( strpos ( $body , '[/img]' ) !== false ) {
return $body ;
}
foreach ( Post\Media :: getByURIId ( $item [ 'uri-id' ], [ Post\Media :: HTML ]) as $media ) {
if ( ! empty ( $media [ 'preview' ])) {
$description = $media [ 'description' ] ? : $media [ 'name' ];
if ( ! empty ( $description )) {
$body .= " \n [img= " . $media [ 'preview' ] . ']' . $description . '[/img]' ;
} else {
$body .= " \n [img] " . $media [ 'preview' ] . '[/img]' ;
}
}
}
return $body ;
}
2017-12-24 02:20:50 +00:00
/**
*
* @ param string $body
2021-07-06 10:00:53 +00:00
* @ param int $uriid
2017-12-24 02:20:50 +00:00
*
2018-04-24 14:22:29 +00:00
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws InternalServerErrorException
2017-12-24 02:20:50 +00:00
*/
2021-07-06 06:38:15 +00:00
function api_get_attachments ( & $body , $uriid )
2017-11-10 05:00:50 +00:00
{
2019-12-13 10:12:40 +00:00
$body = preg_replace ( " / \ [img \ =([0-9]*)x([0-9]*) \ ](.*?) \ [ \ /img \ ]/ism " , '[img]$3[/img]' , $body );
$body = preg_replace ( " / \ [img \ =(.*?) \ ](.*?) \ [ \ /img \ ]/ism " , '[img]$1[/img]' , $body );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$URLSearchString = " ^ \ [ \ ] " ;
2019-12-13 10:12:40 +00:00
if ( ! preg_match_all ( " / \ [img \ ]([ $URLSearchString ]*) \ [ \ /img \ ]/ism " , $body , $images )) {
2018-04-24 14:22:29 +00:00
return [];
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2019-12-13 10:12:40 +00:00
// Remove all embedded pictures, since they are added as attachments
foreach ( $images [ 0 ] as $orig ) {
$body = str_replace ( $orig , '' , $body );
}
2018-01-15 13:05:12 +00:00
$attachments = [];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
foreach ( $images [ 1 ] as $image ) {
2019-10-18 01:26:15 +00:00
$imagedata = Images :: getInfoFromURLCached ( $image );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $imagedata ) {
2021-07-06 06:38:15 +00:00
$attachments [] = [ " url " => Post\Link :: getByLink ( $uriid , $image ), " mimetype " => $imagedata [ " mime " ], " size " => $imagedata [ " size " ]];
2017-04-05 20:07:55 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
return $attachments ;
}
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
*
* @ param string $text
* @ param string $bbcode
*
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws InternalServerErrorException
2017-12-24 02:20:50 +00:00
* @ todo Links at the first character of the post
*/
2021-07-06 06:38:15 +00:00
function api_get_entitities ( & $text , $bbcode , $uriid )
2017-11-10 05:00:50 +00:00
{
2019-10-13 02:01:34 +00:00
$include_entities = strtolower ( $_REQUEST [ 'include_entities' ] ? ? 'false' );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $include_entities != " true " ) {
preg_match_all ( " / \ [img](.*?) \ [ \ /img \ ]/ism " , $bbcode , $images );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
foreach ( $images [ 1 ] as $image ) {
2021-07-06 06:38:15 +00:00
$replace = Post\Link :: getByLink ( $uriid , $image );
2017-11-10 05:00:50 +00:00
$text = str_replace ( $image , $replace , $text );
2016-09-25 16:50:08 +00:00
}
2018-01-15 13:05:12 +00:00
return [];
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2018-02-05 00:23:49 +00:00
$bbcode = BBCode :: cleanPictureLinks ( $bbcode );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Change pure links in text to bbcode uris
$bbcode = preg_replace ( " /([^ \ ] \ =' " . '"' . " ]|^)(https? \ : \ / \ /[a-zA-Z0-9 \ : \ / \ - \ ? \ & \ ; \ . \ = \ _ \ ~ \ # \ % \$ \ ! \ + \ ,]+)/ism " , '$1[url=$2]$2[/url]' , $bbcode );
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$entities = [];
$entities [ " hashtags " ] = [];
$entities [ " symbols " ] = [];
$entities [ " urls " ] = [];
$entities [ " user_mentions " ] = [];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$URLSearchString = " ^ \ [ \ ] " ;
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$bbcode = preg_replace ( " /# \ [url \ =([ $URLSearchString ]*) \ ](.*?) \ [ \ /url \ ]/ism " , '#$2' , $bbcode );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$bbcode = preg_replace ( " / \ [bookmark \ =([ $URLSearchString ]*) \ ](.*?) \ [ \ /bookmark \ ]/ism " , '[url=$1]$2[/url]' , $bbcode );
$bbcode = preg_replace ( " / \ [video \ ](.*?) \ [ \ /video \ ]/ism " , '[url=$1]$1[/url]' , $bbcode );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$bbcode = preg_replace (
" / \ [youtube \ ]([A-Za-z0-9 \ -_=]+)(.*?) \ [ \ /youtube \ ]/ism " ,
'[url=https://www.youtube.com/watch?v=$1]https://www.youtube.com/watch?v=$1[/url]' ,
$bbcode
);
$bbcode = preg_replace ( " / \ [youtube \ ](.*?) \ [ \ /youtube \ ]/ism " , '[url=$1]$1[/url]' , $bbcode );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$bbcode = preg_replace (
" / \ [vimeo \ ]([0-9]+)(.*?) \ [ \ /vimeo \ ]/ism " ,
'[url=https://vimeo.com/$1]https://vimeo.com/$1[/url]' ,
$bbcode
);
$bbcode = preg_replace ( " / \ [vimeo \ ](.*?) \ [ \ /vimeo \ ]/ism " , '[url=$1]$1[/url]' , $bbcode );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
$bbcode = preg_replace ( " / \ [img \ =([0-9]*)x([0-9]*) \ ](.*?) \ [ \ /img \ ]/ism " , '[img]$3[/img]' , $bbcode );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
preg_match_all ( " / \ [url \ =([ $URLSearchString ]*) \ ](.*?) \ [ \ /url \ ]/ism " , $bbcode , $urls );
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$ordered_urls = [];
2017-11-10 05:00:50 +00:00
foreach ( $urls [ 1 ] as $id => $url ) {
$start = iconv_strpos ( $text , $url , 0 , " UTF-8 " );
if ( ! ( $start === false )) {
2018-01-15 13:05:12 +00:00
$ordered_urls [ $start ] = [ " url " => $url , " title " => $urls [ 2 ][ $id ]];
2017-11-10 05:00:50 +00:00
}
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
ksort ( $ordered_urls );
$offset = 0 ;
2019-12-13 10:12:40 +00:00
2017-11-10 05:00:50 +00:00
foreach ( $ordered_urls as $url ) {
if (( substr ( $url [ " title " ], 0 , 7 ) != " http:// " ) && ( substr ( $url [ " title " ], 0 , 8 ) != " https:// " )
&& ! strpos ( $url [ " title " ], " http:// " ) && ! strpos ( $url [ " title " ], " https:// " )
2017-12-22 23:10:32 +00:00
) {
2017-11-10 05:00:50 +00:00
$display_url = $url [ " title " ];
2017-12-22 23:10:32 +00:00
} else {
2018-01-15 13:05:12 +00:00
$display_url = str_replace ([ " http://www. " , " https://www. " ], [ " " , " " ], $url [ " url " ]);
$display_url = str_replace ([ " http:// " , " https:// " ], [ " " , " " ], $display_url );
2016-09-25 16:50:08 +00:00
2017-12-22 23:10:32 +00:00
if ( strlen ( $display_url ) > 26 ) {
2017-11-10 05:00:50 +00:00
$display_url = substr ( $display_url , 0 , 25 ) . " … " ;
2017-12-22 23:10:32 +00:00
}
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
$start = iconv_strpos ( $text , $url [ " url " ], $offset , " UTF-8 " );
if ( ! ( $start === false )) {
2018-01-15 13:05:12 +00:00
$entities [ " urls " ][] = [ " url " => $url [ " url " ],
2017-11-10 05:00:50 +00:00
" expanded_url " => $url [ " url " ],
" display_url " => $display_url ,
2018-01-15 13:05:12 +00:00
" indices " => [ $start , $start + strlen ( $url [ " url " ])]];
2017-11-10 05:00:50 +00:00
$offset = $start + 1 ;
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2019-08-04 03:45:23 +00:00
preg_match_all ( " / \ [img \ =(.*?) \ ](.*?) \ [ \ /img \ ]/ism " , $bbcode , $images , PREG_SET_ORDER );
2018-01-15 13:05:12 +00:00
$ordered_images = [];
2019-08-04 03:45:23 +00:00
foreach ( $images as $image ) {
2019-08-04 04:29:52 +00:00
$start = iconv_strpos ( $text , $image [ 1 ], 0 , " UTF-8 " );
2019-08-04 03:45:23 +00:00
if ( ! ( $start === false )) {
$ordered_images [ $start ] = [ 'url' => $image [ 1 ], 'alt' => $image [ 2 ]];
}
}
preg_match_all ( " / \ [img](.*?) \ [ \ /img \ ]/ism " , $bbcode , $images );
2017-11-10 05:00:50 +00:00
foreach ( $images [ 1 ] as $image ) {
2019-08-04 04:29:52 +00:00
$start = iconv_strpos ( $text , $image , 0 , " UTF-8 " );
2017-12-23 23:27:45 +00:00
if ( ! ( $start === false )) {
2019-08-04 03:45:23 +00:00
$ordered_images [ $start ] = [ 'url' => $image , 'alt' => '' ];
2017-12-23 23:27:45 +00:00
}
2016-09-25 16:50:08 +00:00
}
2019-12-13 10:12:40 +00:00
2017-11-10 05:00:50 +00:00
$offset = 0 ;
2019-08-04 03:45:23 +00:00
foreach ( $ordered_images as $image ) {
$url = $image [ 'url' ];
$ext_alt_text = $image [ 'alt' ];
2018-01-15 13:05:12 +00:00
$display_url = str_replace ([ " http://www. " , " https://www. " ], [ " " , " " ], $url );
$display_url = str_replace ([ " http:// " , " https:// " ], [ " " , " " ], $display_url );
2017-11-10 05:00:50 +00:00
2017-12-23 23:27:45 +00:00
if ( strlen ( $display_url ) > 26 ) {
2017-11-10 05:00:50 +00:00
$display_url = substr ( $display_url , 0 , 25 ) . " … " ;
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
2019-08-04 04:29:52 +00:00
$start = iconv_strpos ( $text , $url , $offset , " UTF-8 " );
2017-11-10 05:00:50 +00:00
if ( ! ( $start === false )) {
2019-10-18 01:26:15 +00:00
$image = Images :: getInfoFromURLCached ( $url );
2017-11-10 05:00:50 +00:00
if ( $image ) {
2021-07-06 06:38:15 +00:00
$media_url = Post\Link :: getByLink ( $uriid , $url );
$sizes [ " medium " ] = [ " w " => $image [ 0 ], " h " => $image [ 1 ], " resize " => " fit " ];
2017-11-10 05:00:50 +00:00
2018-01-15 13:05:12 +00:00
$entities [ " media " ][] = [
2017-11-10 05:00:50 +00:00
" id " => $start + 1 ,
2019-01-21 16:40:14 +00:00
" id_str " => ( string ) ( $start + 1 ),
2018-01-15 13:05:12 +00:00
" indices " => [ $start , $start + strlen ( $url )],
2018-11-08 16:28:29 +00:00
" media_url " => Strings :: normaliseLink ( $media_url ),
2017-11-10 05:00:50 +00:00
" media_url_https " => $media_url ,
" url " => $url ,
" display_url " => $display_url ,
" expanded_url " => $url ,
2019-08-04 03:45:23 +00:00
" ext_alt_text " => $ext_alt_text ,
2017-11-10 05:00:50 +00:00
" type " => " photo " ,
2018-01-15 13:05:12 +00:00
" sizes " => $sizes ];
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
$offset = $start + 1 ;
2016-09-25 16:50:08 +00:00
}
}
2017-11-10 05:00:50 +00:00
return $entities ;
}
2017-12-24 02:20:50 +00:00
/**
*
* @ param array $item
* @ param string $text
*
* @ return string
*/
2018-01-04 01:54:35 +00:00
function api_format_items_embeded_images ( $item , $text )
2017-11-10 05:00:50 +00:00
{
$text = preg_replace_callback (
2018-01-04 01:54:35 +00:00
'|data:image/([^;]+)[^=]+=*|m' ,
function () use ( $item ) {
2019-12-30 22:00:08 +00:00
return DI :: baseUrl () . '/display/' . $item [ 'guid' ];
2017-11-10 05:00:50 +00:00
},
$text
);
return $text ;
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* return < a href = 'url' > name </ a > as array
2017-11-10 05:00:50 +00:00
*
* @ param string $txt text
* @ return array
2017-12-24 02:20:50 +00:00
* 'name' => 'name' ,
2017-11-10 05:00:50 +00:00
* 'url => ' url '
*/
function api_contactlink_to_array ( $txt )
{
2018-01-15 13:05:12 +00:00
$match = [];
2017-11-10 05:00:50 +00:00
$r = preg_match_all ( '|<a href="([^"]*)">([^<]*)</a>|' , $txt , $match );
if ( $r && count ( $match ) == 3 ) {
2018-01-15 13:05:12 +00:00
$res = [
2017-11-10 05:00:50 +00:00
'name' => $match [ 2 ],
'url' => $match [ 1 ]
2018-01-15 13:05:12 +00:00
];
2017-11-10 05:00:50 +00:00
} else {
2018-01-15 13:05:12 +00:00
$res = [
2018-02-11 21:13:29 +00:00
'name' => $txt ,
2017-11-10 05:00:50 +00:00
'url' => " "
2018-01-15 13:05:12 +00:00
];
2017-11-10 05:00:50 +00:00
}
return $res ;
}
2017-09-14 05:16:23 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* return likes , dislikes and attend status for item
2017-11-10 05:00:50 +00:00
*
2019-01-07 17:24:01 +00:00
* @ param array $item array
2017-12-24 02:20:50 +00:00
* @ param string $type Return type ( atom , rss , xml , json )
*
2017-11-10 05:00:50 +00:00
* @ return array
2019-01-07 17:24:01 +00:00
* likes => int count ,
* dislikes => int count
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-11-10 05:00:50 +00:00
*/
2018-10-01 18:16:19 +00:00
function api_format_items_activities ( $item , $type = " json " )
2017-11-10 05:00:50 +00:00
{
2018-01-15 13:05:12 +00:00
$activities = [
'like' => [],
'dislike' => [],
'attendyes' => [],
'attendno' => [],
'attendmaybe' => [],
2019-12-19 07:19:03 +00:00
'announce' => [],
2018-01-15 13:05:12 +00:00
];
2017-11-10 05:00:50 +00:00
2019-12-19 07:19:03 +00:00
$condition = [ 'uid' => $item [ 'uid' ], 'thr-parent' => $item [ 'uri' ], 'gravity' => GRAVITY_ACTIVITY ];
2021-01-16 22:37:27 +00:00
$ret = Post :: selectForUser ( $item [ 'uid' ], [ 'author-id' , 'verb' ], $condition );
2017-11-10 05:00:50 +00:00
2021-01-17 12:49:53 +00:00
while ( $parent_item = Post :: fetch ( $ret )) {
2017-11-10 05:00:50 +00:00
// not used as result should be structured like other user data
//builtin_activity_puller($i, $activities);
// get user data and add it to the array of the activity
2021-11-17 22:44:52 +00:00
$user = DI :: twitterUser () -> createFromContactId ( $parent_item [ 'author-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2018-10-01 18:16:19 +00:00
switch ( $parent_item [ 'verb' ]) {
2019-10-23 22:25:43 +00:00
case Activity :: LIKE :
2017-11-10 05:00:50 +00:00
$activities [ 'like' ][] = $user ;
break ;
2019-10-23 22:25:43 +00:00
case Activity :: DISLIKE :
2017-11-10 05:00:50 +00:00
$activities [ 'dislike' ][] = $user ;
break ;
2019-10-23 22:25:43 +00:00
case Activity :: ATTEND :
2017-11-10 05:00:50 +00:00
$activities [ 'attendyes' ][] = $user ;
break ;
2019-10-23 22:25:43 +00:00
case Activity :: ATTENDNO :
2017-11-10 05:00:50 +00:00
$activities [ 'attendno' ][] = $user ;
break ;
2019-10-23 22:25:43 +00:00
case Activity :: ATTENDMAYBE :
2017-11-10 05:00:50 +00:00
$activities [ 'attendmaybe' ][] = $user ;
break ;
2019-12-19 07:19:03 +00:00
case Activity :: ANNOUNCE :
$activities [ 'announce' ][] = $user ;
break ;
2017-11-10 05:00:50 +00:00
default :
break ;
2017-09-14 05:16:23 +00:00
}
2017-11-10 05:00:50 +00:00
}
2017-09-14 05:16:23 +00:00
2018-07-20 12:19:26 +00:00
DBA :: close ( $ret );
2018-06-18 20:36:34 +00:00
2017-11-10 05:00:50 +00:00
if ( $type == " xml " ) {
2018-01-15 13:05:12 +00:00
$xml_activities = [];
2017-11-10 05:00:50 +00:00
foreach ( $activities as $k => $v ) {
// change xml element from "like" to "friendica:like"
$xml_activities [ " friendica: " . $k ] = $v ;
// add user data into xml output
$k_user = 0 ;
2017-12-23 23:27:45 +00:00
foreach ( $v as $user ) {
2017-11-10 05:00:50 +00:00
$xml_activities [ " friendica: " . $k ][ $k_user ++. " :user " ] = $user ;
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
}
$activities = $xml_activities ;
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
return $activities ;
}
2017-06-08 02:00:59 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* format items to be returned by api
2017-11-10 05:00:50 +00:00
*
2019-03-02 22:03:13 +00:00
* @ param array $items array of items
2017-12-24 02:20:50 +00:00
* @ param array $user_info
* @ param bool $filter_user filter items by $user_info
2019-01-07 17:24:01 +00:00
* @ param string $type Return type ( atom , rss , xml , json )
* @ return array
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-11-10 05:00:50 +00:00
*/
2019-03-02 22:03:13 +00:00
function api_format_items ( $items , $user_info , $filter_user = false , $type = " json " )
2017-11-10 05:00:50 +00:00
{
2021-11-17 21:28:51 +00:00
$a = DI :: app ();
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$ret = [];
2016-09-25 16:50:08 +00:00
2021-02-04 18:14:15 +00:00
if ( empty ( $items )) {
return $ret ;
}
2019-03-02 22:03:13 +00:00
foreach (( array ) $items as $item ) {
2021-11-12 18:52:01 +00:00
[ $status_user , $author_user , $owner_user ] = api_item_get_user ( $a , $item );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// Look if the posts are matching if they should be filtered by user id
if ( $filter_user && ( $status_user [ " id " ] != $user_info [ " id " ])) {
continue ;
2017-04-05 20:07:55 +00:00
}
2016-09-25 16:50:08 +00:00
2019-03-23 13:59:52 +00:00
$status = api_format_item ( $item , $type , $status_user , $author_user , $owner_user );
2016-09-25 16:50:08 +00:00
2019-03-02 22:03:13 +00:00
$ret [] = $status ;
}
2016-09-25 16:50:08 +00:00
2019-03-02 22:03:13 +00:00
return $ret ;
}
2017-11-10 05:00:50 +00:00
2019-03-02 22:03:13 +00:00
/**
* @ param array $item Item record
* @ param string $type Return format ( atom , rss , xml , json )
* @ param array $status_user User record of the item author , can be provided by api_item_get_user ()
2019-03-23 13:59:52 +00:00
* @ param array $author_user User record of the item author , can be provided by api_item_get_user ()
2019-03-02 22:03:13 +00:00
* @ param array $owner_user User record of the item owner , can be provided by api_item_get_user ()
* @ return array API - formatted status
* @ throws BadRequestException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
*/
2019-03-23 13:59:52 +00:00
function api_format_item ( $item , $type = " json " , $status_user = null , $author_user = null , $owner_user = null )
2019-03-02 22:03:13 +00:00
{
2021-11-17 21:28:51 +00:00
$a = DI :: app ();
2017-11-10 05:00:50 +00:00
2019-03-23 13:59:52 +00:00
if ( empty ( $status_user ) || empty ( $author_user ) || empty ( $owner_user )) {
2021-11-12 18:52:01 +00:00
[ $status_user , $author_user , $owner_user ] = api_item_get_user ( $a , $item );
2019-03-02 22:03:13 +00:00
}
2016-09-25 16:50:08 +00:00
2021-09-23 21:18:36 +00:00
DI :: contentItem () -> localize ( $item );
2016-09-25 16:50:08 +00:00
2019-03-02 22:03:13 +00:00
$in_reply_to = api_in_reply_to ( $item );
2016-09-25 16:50:08 +00:00
2019-03-02 22:03:13 +00:00
$converted = api_convert_item ( $item );
2016-09-25 16:50:08 +00:00
2019-03-02 22:03:13 +00:00
if ( $type == " xml " ) {
$geo = " georss:point " ;
} else {
$geo = " geo " ;
}
$status = [
'text' => $converted [ " text " ],
'truncated' => false ,
'created_at' => api_date ( $item [ 'created' ]),
'in_reply_to_status_id' => $in_reply_to [ 'status_id' ],
'in_reply_to_status_id_str' => $in_reply_to [ 'status_id_str' ],
'source' => (( $item [ 'app' ]) ? $item [ 'app' ] : 'web' ),
'id' => intval ( $item [ 'id' ]),
'id_str' => ( string ) intval ( $item [ 'id' ]),
'in_reply_to_user_id' => $in_reply_to [ 'user_id' ],
'in_reply_to_user_id_str' => $in_reply_to [ 'user_id_str' ],
'in_reply_to_screen_name' => $in_reply_to [ 'screen_name' ],
$geo => null ,
'favorited' => $item [ 'starred' ] ? true : false ,
'user' => $status_user ,
'friendica_author' => $author_user ,
2019-03-23 13:59:52 +00:00
'friendica_owner' => $owner_user ,
2020-03-02 07:57:23 +00:00
'friendica_private' => $item [ 'private' ] == Item :: PRIVATE ,
2019-03-02 22:03:13 +00:00
//'entities' => NULL,
'statusnet_html' => $converted [ " html " ],
'statusnet_conversation_id' => $item [ 'parent' ],
2019-12-30 22:00:08 +00:00
'external_url' => DI :: baseUrl () . " /display/ " . $item [ 'guid' ],
2019-03-02 22:03:13 +00:00
'friendica_activities' => api_format_items_activities ( $item , $type ),
2019-09-03 21:11:33 +00:00
'friendica_title' => $item [ 'title' ],
2021-07-10 12:58:48 +00:00
'friendica_html' => BBCode :: convertForUriId ( $item [ 'uri-id' ], $item [ 'body' ], BBCode :: EXTERNAL )
2019-03-02 22:03:13 +00:00
];
2016-09-25 16:50:08 +00:00
2019-03-02 22:03:13 +00:00
if ( count ( $converted [ " attachments " ]) > 0 ) {
$status [ " attachments " ] = $converted [ " attachments " ];
}
if ( count ( $converted [ " entities " ]) > 0 ) {
$status [ " entities " ] = $converted [ " entities " ];
}
if ( $status [ " source " ] == 'web' ) {
2019-12-27 17:24:29 +00:00
$status [ " source " ] = ContactSelector :: networkToName ( $item [ 'author-network' ], $item [ 'author-link' ], $item [ 'network' ]);
} elseif ( ContactSelector :: networkToName ( $item [ 'author-network' ], $item [ 'author-link' ], $item [ 'network' ]) != $status [ " source " ]) {
$status [ " source " ] = trim ( $status [ " source " ] . ' (' . ContactSelector :: networkToName ( $item [ 'author-network' ], $item [ 'author-link' ], $item [ 'network' ]) . ')' );
2019-03-02 22:03:13 +00:00
}
2019-04-24 04:26:23 +00:00
$retweeted_item = [];
2019-04-24 19:06:39 +00:00
$quoted_item = [];
2019-04-24 04:26:23 +00:00
2019-04-24 19:06:39 +00:00
if ( empty ( $retweeted_item ) && ( $item [ 'owner-id' ] == $item [ 'author-id' ])) {
$announce = api_get_announce ( $item );
if ( ! empty ( $announce )) {
$retweeted_item = $item ;
$item = $announce ;
2021-11-17 22:44:52 +00:00
$status [ 'friendica_owner' ] = DI :: twitterUser () -> createFromContactId ( $announce [ 'author-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2019-04-24 19:06:39 +00:00
}
}
if ( ! empty ( $quoted_item )) {
2019-12-13 10:12:40 +00:00
if ( $quoted_item [ 'id' ] != $item [ 'id' ]) {
$quoted_status = api_format_item ( $quoted_item );
/// @todo Only remove the attachments that are also contained in the quotes status
unset ( $status [ 'attachments' ]);
unset ( $status [ 'entities' ]);
} else {
$conv_quoted = api_convert_item ( $quoted_item );
$quoted_status = $status ;
unset ( $quoted_status [ 'attachments' ]);
unset ( $quoted_status [ 'entities' ]);
unset ( $quoted_status [ 'statusnet_conversation_id' ]);
$quoted_status [ 'text' ] = $conv_quoted [ 'text' ];
$quoted_status [ 'statusnet_html' ] = $conv_quoted [ 'html' ];
try {
2021-11-17 22:44:52 +00:00
$quoted_status [ " user " ] = DI :: twitterUser () -> createFromContactId ( $quoted_item [ 'author-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2019-12-13 10:12:40 +00:00
} catch ( BadRequestException $e ) {
// user not found. should be found?
/// @todo check if the user should be always found
$quoted_status [ " user " ] = [];
}
}
2019-04-24 21:28:56 +00:00
unset ( $quoted_status [ 'friendica_author' ]);
unset ( $quoted_status [ 'friendica_owner' ]);
unset ( $quoted_status [ 'friendica_activities' ]);
unset ( $quoted_status [ 'friendica_private' ]);
2019-04-24 04:26:23 +00:00
}
if ( ! empty ( $retweeted_item )) {
$retweeted_status = $status ;
2019-04-24 21:28:56 +00:00
unset ( $retweeted_status [ 'friendica_author' ]);
unset ( $retweeted_status [ 'friendica_owner' ]);
unset ( $retweeted_status [ 'friendica_activities' ]);
unset ( $retweeted_status [ 'friendica_private' ]);
unset ( $retweeted_status [ 'statusnet_conversation_id' ]);
2019-04-24 04:26:23 +00:00
$status [ 'user' ] = $status [ 'friendica_owner' ];
try {
2021-11-17 22:44:52 +00:00
$retweeted_status [ " user " ] = DI :: twitterUser () -> createFromContactId ( $retweeted_item [ 'author-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2019-04-24 04:26:23 +00:00
} catch ( BadRequestException $e ) {
// user not found. should be found?
/// @todo check if the user should be always found
$retweeted_status [ " user " ] = [];
}
2016-09-25 16:50:08 +00:00
2019-04-24 04:26:23 +00:00
$rt_converted = api_convert_item ( $retweeted_item );
2019-03-02 22:03:13 +00:00
2019-04-24 04:26:23 +00:00
$retweeted_status [ 'text' ] = $rt_converted [ " text " ];
$retweeted_status [ 'statusnet_html' ] = $rt_converted [ " html " ];
2021-10-02 08:22:15 +00:00
$retweeted_status [ 'friendica_html' ] = $rt_converted [ " html " ];
2019-04-24 04:26:23 +00:00
$retweeted_status [ 'created_at' ] = api_date ( $retweeted_item [ 'created' ]);
2019-04-24 19:06:39 +00:00
if ( ! empty ( $quoted_status )) {
$retweeted_status [ 'quoted_status' ] = $quoted_status ;
}
2019-04-24 21:28:56 +00:00
$status [ 'friendica_author' ] = $retweeted_status [ 'user' ];
2019-04-24 19:06:39 +00:00
$status [ 'retweeted_status' ] = $retweeted_status ;
} elseif ( ! empty ( $quoted_status )) {
$root_status = api_convert_item ( $item );
$status [ 'text' ] = $root_status [ " text " ];
$status [ 'statusnet_html' ] = $root_status [ " html " ];
$status [ 'quoted_status' ] = $quoted_status ;
2019-03-02 22:03:13 +00:00
}
// "uid" and "self" are only needed for some internal stuff, so remove it from here
unset ( $status [ " user " ][ " uid " ]);
unset ( $status [ " user " ][ " self " ]);
if ( $item [ " coord " ] != " " ) {
$coords = explode ( ' ' , $item [ " coord " ]);
if ( count ( $coords ) == 2 ) {
if ( $type == " json " ) {
$status [ " geo " ] = [ 'type' => 'Point' ,
'coordinates' => [( float ) $coords [ 0 ],
( float ) $coords [ 1 ]]];
} else { // Not sure if this is the official format - if someone founds a documentation we can check
$status [ " georss:point " ] = $item [ " coord " ];
2017-11-10 05:00:50 +00:00
}
}
2019-03-02 22:03:13 +00:00
}
return $status ;
2017-11-10 05:00:50 +00:00
}
2017-12-24 02:20:50 +00:00
/**
2018-04-07 13:54:26 +00:00
* Returns all lists the user subscribes to .
2017-12-24 02:20:50 +00:00
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2018-04-07 13:54:26 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / create - manage - lists / api - reference / get - lists - list
2017-12-24 02:20:50 +00:00
*/
2018-04-07 13:54:26 +00:00
function api_lists_list ( $type )
2017-11-10 05:00:50 +00:00
{
2018-01-15 13:05:12 +00:00
$ret = [];
2017-11-10 05:00:50 +00:00
/// @TODO $ret is not filled here?
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'lists' , $type , [ " lists_list " => $ret ]);
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2018-04-07 13:54:26 +00:00
api_register_func ( 'api/lists/list' , 'api_lists_list' , true );
api_register_func ( 'api/lists/subscriptions' , 'api_lists_list' , true );
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
2018-04-07 13:54:26 +00:00
* Returns all groups the user owns .
2017-12-24 02:20:50 +00:00
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2018-04-07 13:54:26 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / create - manage - lists / api - reference / get - lists - ownerships
2017-12-24 02:20:50 +00:00
*/
2018-04-07 13:54:26 +00:00
function api_lists_ownerships ( $type )
2017-11-10 05:00:50 +00:00
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2018-04-07 13:54:26 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2018-04-07 13:54:26 +00:00
$uid = $user_info [ 'uid' ];
2018-07-20 12:19:26 +00:00
$groups = DBA :: select ( 'group' , [], [ 'deleted' => 0 , 'uid' => $uid ]);
2018-04-07 13:54:26 +00:00
// loop through all groups
2018-04-07 17:55:41 +00:00
$lists = [];
foreach ( $groups as $group ) {
if ( $group [ 'visible' ]) {
2018-04-07 13:54:26 +00:00
$mode = 'public' ;
} else {
$mode = 'private' ;
}
2018-04-07 17:55:41 +00:00
$lists [] = [
'name' => $group [ 'name' ],
'id' => intval ( $group [ 'id' ]),
'id_str' => ( string ) $group [ 'id' ],
2018-04-07 13:54:26 +00:00
'user' => $user_info ,
'mode' => $mode
];
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " lists " , $type , [ 'lists' => [ 'lists' => $lists ]]);
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2018-04-07 13:54:26 +00:00
api_register_func ( 'api/lists/ownerships' , 'api_lists_ownerships' , true );
/**
* Returns recent statuses from users in the specified group .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2018-04-07 13:54:26 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / create - manage - lists / api - reference / get - lists - ownerships
*/
function api_lists_statuses ( $type )
{
2021-11-18 05:47:06 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2018-04-07 13:54:26 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2018-04-07 13:54:26 +00:00
unset ( $_REQUEST [ " user_id " ]);
unset ( $_GET [ " user_id " ]);
unset ( $_REQUEST [ " screen_name " ]);
unset ( $_GET [ " screen_name " ]);
2018-04-07 22:55:05 +00:00
if ( empty ( $_REQUEST [ 'list_id' ])) {
2018-04-07 20:36:54 +00:00
throw new BadRequestException ( 'list_id not specified' );
}
2018-04-07 13:54:26 +00:00
// params
2019-10-13 02:01:34 +00:00
$count = $_REQUEST [ 'count' ] ? ? 20 ;
$page = $_REQUEST [ 'page' ] ? ? 1 ;
$since_id = $_REQUEST [ 'since_id' ] ? ? 0 ;
$max_id = $_REQUEST [ 'max_id' ] ? ? 0 ;
2018-11-30 14:06:22 +00:00
$exclude_replies = ( ! empty ( $_REQUEST [ 'exclude_replies' ]) ? 1 : 0 );
2019-10-13 02:01:34 +00:00
$conversation_id = $_REQUEST [ 'conversation_id' ] ? ? 0 ;
2018-04-07 13:54:26 +00:00
2019-10-13 02:01:34 +00:00
$start = max ( 0 , ( $page - 1 ) * $count );
2018-04-07 13:54:26 +00:00
2021-01-18 22:26:17 +00:00
$groups = DBA :: selectToArray ( 'group_member' , [ 'contact-id' ], [ 'gid' => 1 ]);
$gids = array_column ( $groups , 'contact-id' );
2021-11-17 22:44:52 +00:00
$condition = [ 'uid' => BaseApi :: getCurrentUserID (), 'gravity' => [ GRAVITY_PARENT , GRAVITY_COMMENT ], 'group-id' => $gids ];
2021-01-18 22:26:17 +00:00
$condition = DBA :: mergeConditions ( $condition , [ " `id` > ? " , $since_id ]);
2018-04-07 13:54:26 +00:00
2018-06-09 19:12:13 +00:00
if ( $max_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `id` <= ? " ;
2018-06-09 19:12:13 +00:00
$condition [] = $max_id ;
}
if ( $exclude_replies > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= ' AND `gravity` = ?' ;
2020-05-28 16:02:36 +00:00
$condition [] = GRAVITY_PARENT ;
2018-06-09 19:12:13 +00:00
}
if ( $conversation_id > 0 ) {
2021-01-16 22:57:36 +00:00
$condition [ 0 ] .= " AND `parent` = ? " ;
2018-06-09 19:12:13 +00:00
$condition [] = $conversation_id ;
}
2018-06-09 16:56:37 +00:00
$params = [ 'order' => [ 'id' => true ], 'limit' => [ $start , $count ]];
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition , $params );
2018-04-07 13:54:26 +00:00
2021-01-16 22:37:27 +00:00
$items = api_format_items ( Post :: toArray ( $statuses ), $user_info , false , $type );
2018-04-07 13:54:26 +00:00
2018-04-07 17:55:41 +00:00
$data = [ 'status' => $items ];
2018-04-07 13:54:26 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2018-04-07 13:54:26 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2018-04-07 13:54:26 +00:00
break ;
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " statuses " , $type , $data );
2018-04-07 13:54:26 +00:00
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/lists/statuses' , 'api_lists_statuses' , true );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Returns either the friends of the follower list
*
2017-12-24 02:20:50 +00:00
* Considers friends and followers lists to be private and won ' t return
2017-12-16 15:16:25 +00:00
* anything if any user_id parameter is passed .
*
* @ param string $qtype Either " friends " or " followers "
* @ return boolean | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
2017-12-16 15:16:25 +00:00
* @ throws ForbiddenException
2019-01-07 17:24:01 +00:00
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-11-10 05:00:50 +00:00
*/
2017-12-16 15:16:25 +00:00
function api_statuses_f ( $qtype )
2017-11-10 05:00:50 +00:00
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2017-12-16 15:16:25 +00:00
// pagination
2019-10-13 02:01:34 +00:00
$count = $_GET [ 'count' ] ? ? 20 ;
$page = $_GET [ 'page' ] ? ? 1 ;
$start = max ( 0 , ( $page - 1 ) * $count );
2017-12-16 15:16:25 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_GET [ 'cursor' ]) && $_GET [ 'cursor' ] == 'undefined' ) {
2017-11-10 05:00:50 +00:00
/* this is to stop Hotot to load friends multiple times
* I 'm not sure if I' m missing return something or
* is a bug in hotot . Workaround , meantime
*/
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/* $ret = Array ();
return array ( '$users' => $ret ); */
return false ;
}
2016-09-25 16:50:08 +00:00
2017-12-23 00:30:50 +00:00
$sql_extra = '' ;
2017-11-10 05:00:50 +00:00
if ( $qtype == 'friends' ) {
2018-07-25 02:53:46 +00:00
$sql_extra = sprintf ( " AND ( `rel` = %d OR `rel` = %d ) " , intval ( Contact :: SHARING ), intval ( Contact :: FRIEND ));
2017-12-23 00:30:50 +00:00
} elseif ( $qtype == 'followers' ) {
2018-07-25 02:53:46 +00:00
$sql_extra = sprintf ( " AND ( `rel` = %d OR `rel` = %d ) " , intval ( Contact :: FOLLOWER ), intval ( Contact :: FRIEND ));
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// friends and followers only for self
if ( $user_info [ 'self' ] == 0 ) {
$sql_extra = " AND false " ;
}
2016-09-25 16:50:08 +00:00
2017-12-18 14:18:31 +00:00
if ( $qtype == 'blocks' ) {
2017-12-18 22:51:03 +00:00
$sql_filter = 'AND `blocked` AND NOT `pending`' ;
} elseif ( $qtype == 'incoming' ) {
$sql_filter = 'AND `pending`' ;
2017-12-18 14:18:31 +00:00
} else {
2017-12-18 22:51:03 +00:00
$sql_filter = 'AND (NOT `blocked` OR `pending`)' ;
2017-12-18 14:18:31 +00:00
}
2021-10-09 21:16:15 +00:00
// @todo This query most likely can be replaced with a Contact::select...
$r = DBA :: toArray ( DBA :: p (
2021-11-17 21:28:51 +00:00
" SELECT `id`
2017-12-16 15:16:25 +00:00
FROM `contact`
2021-10-09 21:16:15 +00:00
WHERE `uid` = ?
2017-12-16 15:16:25 +00:00
AND NOT `self`
2017-12-18 22:51:03 +00:00
$sql_filter
2017-12-16 15:16:25 +00:00
$sql_extra
ORDER BY `nick`
2021-10-09 21:16:15 +00:00
LIMIT ? , ? " ,
2021-11-17 22:44:52 +00:00
BaseApi :: getCurrentUserID (),
2021-10-09 21:16:15 +00:00
$start ,
$count
));
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$ret = [];
2017-11-10 05:00:50 +00:00
foreach ( $r as $cid ) {
2021-11-17 22:44:52 +00:00
$user = DI :: twitterUser () -> createFromContactId ( $cid [ 'id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
// "uid" and "self" are only needed for some internal stuff, so remove it from here
unset ( $user [ " uid " ]);
unset ( $user [ " self " ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $user ) {
$ret [] = $user ;
2016-09-25 16:50:08 +00:00
}
}
2018-01-15 13:05:12 +00:00
return [ 'user' => $ret ];
2017-11-10 05:00:50 +00:00
}
2017-01-16 20:59:16 +00:00
2017-12-16 15:16:25 +00:00
/**
2020-01-19 06:05:23 +00:00
* Returns the list of friends of the provided user
2017-12-16 15:16:25 +00:00
*
* @ deprecated By Twitter API in favor of friends / list
*
* @ param string $type Either " json " or " xml "
* @ return boolean | string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
2017-12-16 15:16:25 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_statuses_friends ( $type )
{
2017-12-16 15:16:25 +00:00
$data = api_statuses_f ( " friends " );
2017-11-10 05:00:50 +00:00
if ( $data === false ) {
return false ;
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " users " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-16 15:16:25 +00:00
/**
2020-01-19 06:05:23 +00:00
* Returns the list of followers of the provided user
2017-12-16 15:16:25 +00:00
*
* @ deprecated By Twitter API in favor of friends / list
*
* @ param string $type Either " json " or " xml "
* @ return boolean | string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
2017-12-16 15:16:25 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_statuses_followers ( $type )
{
2017-12-16 15:16:25 +00:00
$data = api_statuses_f ( " followers " );
2017-11-10 05:00:50 +00:00
if ( $data === false ) {
return false ;
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " users " , $type , $data );
2017-11-10 05:00:50 +00:00
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/statuses/friends' , 'api_statuses_friends' , true );
api_register_func ( 'api/statuses/followers' , 'api_statuses_followers' , true );
2017-12-18 14:18:31 +00:00
/**
* Returns the list of blocked users
*
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / mute - block - report - users / api - reference / get - blocks - list
*
* @ param string $type Either " json " or " xml "
*
* @ return boolean | string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
2017-12-18 14:18:31 +00:00
*/
function api_blocks_list ( $type )
{
$data = api_statuses_f ( 'blocks' );
if ( $data === false ) {
return false ;
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " users " , $type , $data );
2017-12-18 14:18:31 +00:00
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/blocks/list' , 'api_blocks_list' , true );
2017-12-18 22:51:03 +00:00
/**
* Returns the list of pending users IDs
*
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / follow - search - get - users / api - reference / get - friendships - incoming
*
* @ param string $type Either " json " or " xml "
*
* @ return boolean | string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
2017-12-18 22:51:03 +00:00
*/
function api_friendships_incoming ( $type )
{
$data = api_statuses_f ( 'incoming' );
if ( $data === false ) {
return false ;
}
2018-01-15 13:05:12 +00:00
$ids = [];
2017-12-18 22:51:03 +00:00
foreach ( $data [ 'user' ] as $user ) {
$ids [] = $user [ 'id' ];
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " ids " , $type , [ 'id' => $ids ]);
2017-12-18 22:51:03 +00:00
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/friendships/incoming' , 'api_friendships_incoming' , true );
2017-12-24 02:20:50 +00:00
/**
* Sends a new direct message .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws NotFoundException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / direct - messages / sending - and - receiving / api - reference / new - message
*/
2017-11-10 05:00:50 +00:00
function api_direct_messages_new ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$uid = BaseApi :: getCurrentUserID ();
2021-11-17 21:28:51 +00:00
if ( empty ( $uid )) {
2017-12-23 23:27:45 +00:00
throw new ForbiddenException ();
}
2021-11-17 23:03:18 +00:00
2018-07-01 18:42:38 +00:00
if ( empty ( $_POST [ " text " ]) || empty ( $_POST [ " screen_name " ]) && empty ( $_POST [ " user_id " ])) {
2017-12-23 23:27:45 +00:00
return ;
}
2016-09-25 16:50:08 +00:00
2021-11-17 21:28:51 +00:00
$sender = DI :: twitterUser () -> createFromUserId ( $uid ) -> toArray ();
2016-09-25 16:50:08 +00:00
2018-07-01 18:42:38 +00:00
$recipient = null ;
if ( ! empty ( $_POST [ 'screen_name' ])) {
2021-11-17 22:44:52 +00:00
$contacts = Contact :: selectToArray ([ 'id' , 'nurl' , 'network' ], [ 'uid' => BaseApi :: getCurrentUserID (), 'nick' => $_POST [ 'screen_name' ]]);
2021-10-03 15:02:20 +00:00
if ( DBA :: isResult ( $contacts )) {
2018-07-01 18:43:22 +00:00
// Selecting the id by priority, friendica first
2021-10-03 15:02:20 +00:00
api_best_nickname ( $contacts );
2016-09-25 16:50:08 +00:00
2021-11-17 21:28:51 +00:00
$recipient = DI :: twitterUser () -> createFromContactId ( $contacts [ 0 ][ 'id' ], $uid ) -> toArray ();
2018-07-01 18:43:22 +00:00
}
2017-11-10 05:00:50 +00:00
} else {
2021-11-08 21:35:41 +00:00
$recipient = api_get_user ( $_POST [ 'user_id' ]);
2016-09-25 16:50:08 +00:00
}
2018-07-01 18:43:22 +00:00
if ( empty ( $recipient )) {
throw new NotFoundException ( 'Recipient not found' );
}
2017-11-10 05:00:50 +00:00
$replyto = '' ;
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_REQUEST [ 'replyto' ])) {
2021-11-17 22:44:52 +00:00
$mail = DBA :: selectFirst ( 'mail' , [ 'parent-uri' , 'title' ], [ 'uid' => BaseApi :: getCurrentUserID (), 'id' => $_REQUEST [ 'replyto' ]]);
2021-10-03 15:02:20 +00:00
$replyto = $mail [ 'parent-uri' ];
$sub = $mail [ 'title' ];
2017-11-10 05:00:50 +00:00
} else {
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_REQUEST [ 'title' ])) {
2017-11-10 05:00:50 +00:00
$sub = $_REQUEST [ 'title' ];
2017-04-05 20:07:55 +00:00
} else {
2017-11-10 05:00:50 +00:00
$sub = (( strlen ( $_POST [ 'text' ]) > 10 ) ? substr ( $_POST [ 'text' ], 0 , 10 ) . " ... " : $_POST [ 'text' ]);
2017-04-05 20:07:55 +00:00
}
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2018-01-15 17:14:09 +00:00
$id = Mail :: send ( $recipient [ 'cid' ], $_POST [ 'text' ], $sub , $replyto );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $id > - 1 ) {
2021-10-03 15:02:20 +00:00
$mail = DBA :: selectFirst ( 'mail' , [], [ 'id' => $id ]);
$ret = api_format_messages ( $mail , $recipient , $sender );
2017-11-10 05:00:50 +00:00
} else {
2021-10-03 15:02:20 +00:00
$ret = [ " error " => $id ];
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2018-01-15 13:05:12 +00:00
$data = [ 'direct_message' => $ret ];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $sender );
2018-11-13 22:23:16 +00:00
break ;
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct-messages " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/direct_messages/new' , 'api_direct_messages_new' , true , API_METHOD_POST );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* delete a direct_message from mail table through api
2017-11-10 05:00:50 +00:00
*
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
2018-04-09 17:34:02 +00:00
* @ return string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
* @ see https :// developer . twitter . com / en / docs / direct - messages / sending - and - receiving / api - reference / delete - message
2017-11-10 05:00:50 +00:00
*/
function api_direct_messages_destroy ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
//required
2019-10-13 02:01:34 +00:00
$id = $_REQUEST [ 'id' ] ? ? 0 ;
2017-11-10 05:00:50 +00:00
// optional
2019-10-13 02:01:34 +00:00
$parenturi = $_REQUEST [ 'friendica_parenturi' ] ? ? '' ;
2018-11-30 14:06:22 +00:00
$verbose = ( ! empty ( $_GET [ 'friendica_verbose' ]) ? strtolower ( $_GET [ 'friendica_verbose' ]) : " false " );
2017-11-10 05:00:50 +00:00
/// @todo optional parameter 'include_entities' from Twitter API not yet implemented
$uid = $user_info [ 'uid' ];
// error if no id or parenturi specified (for clients posting parent-uri as well)
if ( $verbose == " true " && ( $id == 0 || $parenturi == " " )) {
2018-01-15 13:05:12 +00:00
$answer = [ 'result' => 'error' , 'message' => 'message id or parenturi not specified' ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct_messages_delete " , $type , [ '$result' => $answer ]);
2017-11-10 05:00:50 +00:00
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
// BadRequestException if no id specified (for clients using Twitter API)
if ( $id == 0 ) {
throw new BadRequestException ( 'Message id not specified' );
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// add parent-uri to sql command if specified by calling app
2018-07-21 13:10:13 +00:00
$sql_extra = ( $parenturi != " " ? " AND `parent-uri` = ' " . DBA :: escape ( $parenturi ) . " ' " : " " );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// error message if specified id is not in database
2021-10-08 10:01:15 +00:00
if ( ! DBA :: exists ( 'mail' , [ " `uid` = ? AND `id` = ? " . $sql_extra , $uid , $id ])) {
2017-11-10 05:00:50 +00:00
if ( $verbose == " true " ) {
2018-01-15 13:05:12 +00:00
$answer = [ 'result' => 'error' , 'message' => 'message id not in database' ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct_messages_delete " , $type , [ '$result' => $answer ]);
2017-04-05 20:17:15 +00:00
}
2017-11-10 05:00:50 +00:00
/// @todo BadRequestException ok for Twitter API clients?
throw new BadRequestException ( 'message id not in database' );
}
// delete message
2021-10-08 10:01:15 +00:00
$result = DBA :: delete ( 'mail' , [ " `uid` = ? AND `id` = ? " . $sql_extra , $uid , $id ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $verbose == " true " ) {
if ( $result ) {
// return success
2018-01-15 13:05:12 +00:00
$answer = [ 'result' => 'ok' , 'message' => 'message deleted' ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct_message_delete " , $type , [ '$result' => $answer ]);
2017-11-10 05:00:50 +00:00
} else {
2018-01-15 13:05:12 +00:00
$answer = [ 'result' => 'error' , 'message' => 'unknown error' ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct_messages_delete " , $type , [ '$result' => $answer ]);
2017-04-05 20:17:15 +00:00
}
2017-11-10 05:00:50 +00:00
}
/// @todo return JSON data like Twitter API not yet implemented
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/direct_messages/destroy' , 'api_direct_messages_destroy' , true , API_METHOD_DELETE );
2016-09-25 16:50:08 +00:00
2018-09-14 17:35:24 +00:00
/**
* Unfollow Contact
*
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
* @ return string | array
2021-09-26 14:30:44 +00:00
* @ throws HTTPException\BadRequestException
* @ throws HTTPException\ExpectationFailedException
* @ throws HTTPException\ForbiddenException
* @ throws HTTPException\InternalServerErrorException
* @ throws HTTPException\NotFoundException
2019-01-07 17:24:01 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / follow - search - get - users / api - reference / post - friendships - destroy . html
2018-09-14 17:35:24 +00:00
*/
2018-09-13 21:23:53 +00:00
function api_friendships_destroy ( $type )
{
2021-11-17 22:44:52 +00:00
$uid = BaseApi :: getCurrentUserID ();
2018-09-14 07:28:14 +00:00
2018-09-15 09:06:55 +00:00
if ( $uid === false ) {
2021-09-26 14:30:44 +00:00
throw new HTTPException\ForbiddenException ();
}
$owner = User :: getOwnerDataById ( $uid );
if ( ! $owner ) {
Logger :: notice ( API_LOG_PREFIX . 'No owner {uid} found' , [ 'module' => 'api' , 'action' => 'friendships_destroy' , 'uid' => $uid ]);
throw new HTTPException\NotFoundException ( 'Error Processing Request' );
2018-09-14 07:28:14 +00:00
}
2018-09-13 21:23:53 +00:00
2019-10-13 02:01:34 +00:00
$contact_id = $_REQUEST [ 'user_id' ] ? ? 0 ;
2018-09-13 21:23:53 +00:00
2018-09-15 09:06:55 +00:00
if ( empty ( $contact_id )) {
2018-12-30 20:42:56 +00:00
Logger :: notice ( API_LOG_PREFIX . 'No user_id specified' , [ 'module' => 'api' , 'action' => 'friendships_destroy' ]);
2021-09-26 14:30:44 +00:00
throw new HTTPException\BadRequestException ( 'no user_id specified' );
2018-09-14 07:28:14 +00:00
}
2018-09-13 21:23:53 +00:00
2018-09-13 21:32:26 +00:00
// Get Contact by given id
2018-09-14 07:28:14 +00:00
$contact = DBA :: selectFirst ( 'contact' , [ 'url' ], [ 'id' => $contact_id , 'uid' => 0 , 'self' => false ]);
2018-09-13 21:23:53 +00:00
if ( ! DBA :: isResult ( $contact )) {
2021-09-26 14:30:44 +00:00
Logger :: notice ( API_LOG_PREFIX . 'No public contact found for ID {contact}' , [ 'module' => 'api' , 'action' => 'friendships_destroy' , 'contact' => $contact_id ]);
throw new HTTPException\NotFoundException ( 'no contact found to given ID' );
2018-09-13 21:23:53 +00:00
}
2021-09-26 14:30:44 +00:00
$url = $contact [ 'url' ];
2018-09-13 21:23:53 +00:00
$condition = [ " `uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?) " ,
2018-11-08 16:28:29 +00:00
$uid , Contact :: SHARING , Contact :: FRIEND , Strings :: normaliseLink ( $url ),
Strings :: normaliseLink ( $url ), $url ];
2018-09-13 21:23:53 +00:00
$contact = DBA :: selectFirst ( 'contact' , [], $condition );
if ( ! DBA :: isResult ( $contact )) {
2018-12-30 20:42:56 +00:00
Logger :: notice ( API_LOG_PREFIX . 'Not following contact' , [ 'module' => 'api' , 'action' => 'friendships_destroy' ]);
2021-09-26 14:30:44 +00:00
throw new HTTPException\NotFoundException ( 'Not following Contact' );
2018-09-13 21:23:53 +00:00
}
2021-09-26 14:30:44 +00:00
try {
2021-10-02 19:48:20 +00:00
$result = Contact :: terminateFriendship ( $owner , $contact );
2018-09-13 21:23:53 +00:00
2021-09-26 14:30:44 +00:00
if ( $result === null ) {
Logger :: notice ( API_LOG_PREFIX . 'Not supported for {network}' , [ 'module' => 'api' , 'action' => 'friendships_destroy' , 'network' => $contact [ 'network' ]]);
throw new HTTPException\ExpectationFailedException ( 'Unfollowing is currently not supported by this contact\'s network.' );
}
if ( $result === false ) {
throw new HTTPException\ServiceUnavailableException ( 'Unable to unfollow this contact, please retry in a few minutes or contact your administrator.' );
}
} catch ( Exception $e ) {
2021-10-02 19:48:20 +00:00
Logger :: error ( API_LOG_PREFIX . $e -> getMessage (), [ 'owner' => $owner , 'contact' => $contact ]);
2021-09-26 14:30:44 +00:00
throw new HTTPException\InternalServerErrorException ( 'Unable to unfollow this contact, please contact your administrator' );
2018-09-13 21:23:53 +00:00
}
2018-09-16 17:36:25 +00:00
// "uid" and "self" are only needed for some internal stuff, so remove it from here
2021-09-26 14:30:44 +00:00
unset ( $contact [ 'uid' ]);
unset ( $contact [ 'self' ]);
2018-09-16 17:36:25 +00:00
// Set screen_name since Twidere requests it
2021-09-26 14:30:44 +00:00
$contact [ 'screen_name' ] = $contact [ 'nick' ];
2018-09-16 17:36:25 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'friendships-destroy' , $type , [ 'user' => $contact ]);
2018-09-13 21:23:53 +00:00
}
2021-11-12 18:52:01 +00:00
2018-09-13 21:23:53 +00:00
api_register_func ( 'api/friendships/destroy' , 'api_friendships_destroy' , true , API_METHOD_POST );
2017-12-24 02:20:50 +00:00
/**
*
* @ param string $type Return type ( atom , rss , xml , json )
* @ param string $box
* @ param string $verbose
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_direct_messages_box ( $type , $box , $verbose )
{
2021-11-18 05:57:46 +00:00
if ( empty ( BaseApi :: getCurrentUserID ())) {
throw new ForbiddenException ();
}
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2017-11-10 05:00:50 +00:00
// params
2019-10-13 02:01:34 +00:00
$count = $_GET [ 'count' ] ? ? 20 ;
$page = $_REQUEST [ 'page' ] ? ? 1 ;
2017-04-05 20:17:15 +00:00
2019-10-13 02:01:34 +00:00
$since_id = $_REQUEST [ 'since_id' ] ? ? 0 ;
$max_id = $_REQUEST [ 'max_id' ] ? ? 0 ;
2017-04-05 20:17:15 +00:00
2019-10-13 02:01:34 +00:00
$user_id = $_REQUEST [ 'user_id' ] ? ? '' ;
$screen_name = $_REQUEST [ 'screen_name' ] ? ? '' ;
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// caller user info
unset ( $_REQUEST [ " user_id " ]);
unset ( $_GET [ " user_id " ]);
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
unset ( $_REQUEST [ " screen_name " ]);
unset ( $_GET [ " screen_name " ]);
2016-09-25 16:50:08 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2021-11-17 21:28:51 +00:00
2017-11-10 05:00:50 +00:00
$profile_url = $user_info [ " url " ];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// pagination
2019-10-13 02:01:34 +00:00
$start = max ( 0 , ( $page - 1 ) * $count );
2016-09-25 16:50:08 +00:00
2018-02-11 21:13:29 +00:00
$sql_extra = " " ;
2017-11-10 05:00:50 +00:00
// filters
if ( $box == " sentbox " ) {
2018-07-21 13:10:13 +00:00
$sql_extra = " `mail`.`from-url`=' " . DBA :: escape ( $profile_url ) . " ' " ;
2017-11-10 05:00:50 +00:00
} elseif ( $box == " conversation " ) {
2019-10-13 02:01:34 +00:00
$sql_extra = " `mail`.`parent-uri`=' " . DBA :: escape ( $_GET [ 'uri' ] ? ? '' ) . " ' " ;
2017-11-10 05:00:50 +00:00
} elseif ( $box == " all " ) {
$sql_extra = " true " ;
} elseif ( $box == " inbox " ) {
2018-07-21 13:10:13 +00:00
$sql_extra = " `mail`.`from-url`!=' " . DBA :: escape ( $profile_url ) . " ' " ;
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:17:15 +00:00
2017-11-10 05:00:50 +00:00
if ( $max_id > 0 ) {
$sql_extra .= ' AND `mail`.`id` <= ' . intval ( $max_id );
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $user_id != " " ) {
$sql_extra .= ' AND `mail`.`contact-id` = ' . intval ( $user_id );
} elseif ( $screen_name != " " ) {
2018-07-21 13:10:13 +00:00
$sql_extra .= " AND `contact`.`nick` = ' " . DBA :: escape ( $screen_name ) . " ' " ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-10-08 04:10:45 +00:00
$r = DBA :: toArray ( DBA :: p (
" SELECT `mail`.*, `contact`.`nurl` AS `contact-url` FROM `mail`,`contact` WHERE `mail`.`contact-id` = `contact`.`id` AND `mail`.`uid` = ? AND $sql_extra AND `mail`.`id` > ? ORDER BY `mail`.`id` DESC LIMIT ?,? " ,
2021-11-17 22:44:52 +00:00
BaseApi :: getCurrentUserID (),
2021-10-08 04:10:45 +00:00
$since_id ,
$start ,
$count
));
2018-07-21 12:46:04 +00:00
if ( $verbose == " true " && ! DBA :: isResult ( $r )) {
2018-01-15 13:05:12 +00:00
$answer = [ 'result' => 'error' , 'message' => 'no mails available' ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct_messages_all " , $type , [ '$result' => $answer ]);
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:17:15 +00:00
2018-01-15 13:05:12 +00:00
$ret = [];
2017-11-10 05:00:50 +00:00
foreach ( $r as $item ) {
if ( $box == " inbox " || $item [ 'from-url' ] != $profile_url ) {
$recipient = $user_info ;
2021-11-17 22:44:52 +00:00
$sender = DI :: twitterUser () -> createFromContactId ( $item [ 'contact-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
} elseif ( $box == " sentbox " || $item [ 'from-url' ] == $profile_url ) {
2021-11-17 22:44:52 +00:00
$recipient = DI :: twitterUser () -> createFromContactId ( $item [ 'contact-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
$sender = $user_info ;
}
2016-09-25 16:50:08 +00:00
2018-04-09 19:34:53 +00:00
if ( isset ( $recipient ) && isset ( $sender )) {
$ret [] = api_format_messages ( $item , $recipient , $sender );
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2018-01-15 13:05:12 +00:00
$data = [ 'direct_message' => $ret ];
2017-11-10 05:00:50 +00:00
switch ( $type ) {
case " atom " :
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
case " rss " :
2021-11-17 21:28:51 +00:00
$data = api_rss_extra ( $data , $user_info );
2018-11-13 22:23:16 +00:00
break ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct-messages " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2017-12-24 02:20:50 +00:00
/**
* Returns the most recent direct messages sent by the user .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / direct - messages / sending - and - receiving / api - reference / get - sent - message
*/
2017-11-10 05:00:50 +00:00
function api_direct_messages_sentbox ( $type )
{
2018-11-30 14:06:22 +00:00
$verbose = ! empty ( $_GET [ 'friendica_verbose' ]) ? strtolower ( $_GET [ 'friendica_verbose' ]) : " false " ;
2017-11-10 05:00:50 +00:00
return api_direct_messages_box ( $type , " sentbox " , $verbose );
}
2017-12-24 02:20:50 +00:00
/**
* Returns the most recent direct messages sent to the user .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
2017-12-24 02:20:50 +00:00
* @ see https :// developer . twitter . com / en / docs / direct - messages / sending - and - receiving / api - reference / get - messages
*/
2017-11-10 05:00:50 +00:00
function api_direct_messages_inbox ( $type )
{
2018-11-30 14:06:22 +00:00
$verbose = ! empty ( $_GET [ 'friendica_verbose' ]) ? strtolower ( $_GET [ 'friendica_verbose' ]) : " false " ;
2017-11-10 05:00:50 +00:00
return api_direct_messages_box ( $type , " inbox " , $verbose );
}
2017-12-24 02:20:50 +00:00
/**
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_direct_messages_all ( $type )
{
2018-11-30 14:06:22 +00:00
$verbose = ! empty ( $_GET [ 'friendica_verbose' ]) ? strtolower ( $_GET [ 'friendica_verbose' ]) : " false " ;
2017-11-10 05:00:50 +00:00
return api_direct_messages_box ( $type , " all " , $verbose );
}
2017-12-24 02:20:50 +00:00
/**
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_direct_messages_conversation ( $type )
{
2018-11-30 14:06:22 +00:00
$verbose = ! empty ( $_GET [ 'friendica_verbose' ]) ? strtolower ( $_GET [ 'friendica_verbose' ]) : " false " ;
2017-11-10 05:00:50 +00:00
return api_direct_messages_box ( $type , " conversation " , $verbose );
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/direct_messages/conversation' , 'api_direct_messages_conversation' , true );
api_register_func ( 'api/direct_messages/all' , 'api_direct_messages_all' , true );
api_register_func ( 'api/direct_messages/sent' , 'api_direct_messages_sentbox' , true );
api_register_func ( 'api/direct_messages' , 'api_direct_messages_inbox' , true );
/**
2020-01-19 06:05:23 +00:00
* list all photos of the authenticated user
2017-11-10 05:00:50 +00:00
*
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
2018-04-09 17:34:02 +00:00
* @ return string | array
2019-01-07 17:24:01 +00:00
* @ throws ForbiddenException
* @ throws InternalServerErrorException
2017-11-10 05:00:50 +00:00
*/
function api_fr_photos_list ( $type )
{
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () === false ) {
2017-11-10 05:00:50 +00:00
throw new ForbiddenException ();
}
2021-10-08 04:10:45 +00:00
$r = DBA :: toArray ( DBA :: p (
2017-11-10 05:00:50 +00:00
" SELECT `resource-id`, MAX(scale) AS `scale`, `album`, `filename`, `type`, MAX(`created`) AS `created`,
MAX ( `edited` ) AS `edited` , MAX ( `desc` ) AS `desc` FROM `photo`
2021-10-11 14:21:10 +00:00
WHERE `uid` = ? AND NOT `photo-type` IN ( ? , ? ) GROUP BY `resource-id` , `album` , `filename` , `type` " ,
local_user (), Photo :: CONTACT_AVATAR , Photo :: CONTACT_BANNER
2021-10-08 04:10:45 +00:00
));
2018-01-15 13:05:12 +00:00
$typetoext = [
2017-11-10 05:00:50 +00:00
'image/jpeg' => 'jpg' ,
'image/png' => 'png' ,
'image/gif' => 'gif'
2018-01-15 13:05:12 +00:00
];
$data = [ 'photo' => []];
2018-07-21 12:46:04 +00:00
if ( DBA :: isResult ( $r )) {
2017-11-10 05:00:50 +00:00
foreach ( $r as $rr ) {
2018-01-15 13:05:12 +00:00
$photo = [];
2017-11-10 05:00:50 +00:00
$photo [ 'id' ] = $rr [ 'resource-id' ];
$photo [ 'album' ] = $rr [ 'album' ];
$photo [ 'filename' ] = $rr [ 'filename' ];
$photo [ 'type' ] = $rr [ 'type' ];
2019-12-30 22:00:08 +00:00
$thumb = DI :: baseUrl () . " /photo/ " . $rr [ 'resource-id' ] . " - " . $rr [ 'scale' ] . " . " . $typetoext [ $rr [ 'type' ]];
2017-11-10 05:00:50 +00:00
$photo [ 'created' ] = $rr [ 'created' ];
$photo [ 'edited' ] = $rr [ 'edited' ];
$photo [ 'desc' ] = $rr [ 'desc' ];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $type == " xml " ) {
2018-01-15 13:05:12 +00:00
$data [ 'photo' ][] = [ " @attributes " => $photo , " 1 " => $thumb ];
2017-04-05 20:07:55 +00:00
} else {
2017-11-10 05:00:50 +00:00
$photo [ 'thumb' ] = $thumb ;
$data [ 'photo' ][] = $photo ;
2016-09-25 16:50:08 +00:00
}
}
2017-11-10 05:00:50 +00:00
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " photos " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* upload a new photo or change an existing photo
2017-11-10 05:00:50 +00:00
*
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
2018-04-09 17:34:02 +00:00
* @ return string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws NotFoundException
2017-11-10 05:00:50 +00:00
*/
function api_fr_photo_create_update ( $type )
{
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () === false ) {
2017-11-10 05:00:50 +00:00
throw new ForbiddenException ();
}
// input params
2019-10-13 02:01:34 +00:00
$photo_id = $_REQUEST [ 'photo_id' ] ? ? null ;
$desc = $_REQUEST [ 'desc' ] ? ? null ;
$album = $_REQUEST [ 'album' ] ? ? null ;
$album_new = $_REQUEST [ 'album_new' ] ? ? null ;
$allow_cid = $_REQUEST [ 'allow_cid' ] ? ? null ;
$deny_cid = $_REQUEST [ 'deny_cid' ] ? ? null ;
$allow_gid = $_REQUEST [ 'allow_gid' ] ? ? null ;
$deny_gid = $_REQUEST [ 'deny_gid' ] ? ? null ;
2020-12-22 03:21:42 +00:00
$visibility = ! $allow_cid && ! $deny_cid && ! $allow_gid && ! $deny_gid ;
2017-11-10 05:00:50 +00:00
// do several checks on input parameters
// we do not allow calls without album string
if ( $album == null ) {
throw new BadRequestException ( " no albumname specified " );
}
// if photo_id == null --> we are uploading a new photo
if ( $photo_id == null ) {
$mode = " create " ;
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// error if no media posted in create-mode
2018-11-30 14:06:22 +00:00
if ( empty ( $_FILES [ 'media' ])) {
2017-11-10 05:00:50 +00:00
// Output error
throw new BadRequestException ( " no media data submitted " );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
// album_new will be ignored in create-mode
$album_new = " " ;
} else {
$mode = " update " ;
2016-09-25 16:50:08 +00:00
2018-12-11 19:03:29 +00:00
// check if photo is existing in databasei
2021-11-17 22:44:52 +00:00
if ( ! Photo :: exists ([ 'resource-id' => $photo_id , 'uid' => BaseApi :: getCurrentUserID (), 'album' => $album ])) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( " photo not available " );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// checks on acl strings provided by clients
$acl_input_error = false ;
$acl_input_error |= check_acl_input ( $allow_cid );
$acl_input_error |= check_acl_input ( $deny_cid );
$acl_input_error |= check_acl_input ( $allow_gid );
$acl_input_error |= check_acl_input ( $deny_gid );
if ( $acl_input_error ) {
throw new BadRequestException ( " acl data invalid " );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
// now let's upload the new media in create-mode
if ( $mode == " create " ) {
$media = $_FILES [ 'media' ];
2021-10-11 14:21:10 +00:00
$data = save_media_to_database ( " photo " , $media , $type , $album , trim ( $allow_cid ), trim ( $deny_cid ), trim ( $allow_gid ), trim ( $deny_gid ), $desc , Photo :: DEFAULT , $visibility );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// return success of updating or error message
if ( ! is_null ( $data )) {
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " photo_create " , $type , $data );
2017-11-10 05:00:50 +00:00
} else {
throw new InternalServerErrorException ( " unknown error - uploading photo failed, see Friendica log for more information " );
}
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// now let's do the changes in update-mode
if ( $mode == " update " ) {
2018-12-11 19:03:29 +00:00
$updated_fields = [];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( ! is_null ( $desc )) {
2018-12-11 19:03:29 +00:00
$updated_fields [ 'desc' ] = $desc ;
2017-04-05 20:07:55 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( ! is_null ( $album_new )) {
2018-12-11 19:03:29 +00:00
$updated_fields [ 'album' ] = $album_new ;
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
if ( ! is_null ( $allow_cid )) {
$allow_cid = trim ( $allow_cid );
2018-12-11 19:03:29 +00:00
$updated_fields [ 'allow_cid' ] = $allow_cid ;
2017-04-05 20:07:55 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( ! is_null ( $deny_cid )) {
$deny_cid = trim ( $deny_cid );
2018-12-11 19:03:29 +00:00
$updated_fields [ 'deny_cid' ] = $deny_cid ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( ! is_null ( $allow_gid )) {
$allow_gid = trim ( $allow_gid );
2018-12-11 19:03:29 +00:00
$updated_fields [ 'allow_gid' ] = $allow_gid ;
2017-11-10 05:00:50 +00:00
}
2017-01-09 12:09:01 +00:00
2017-11-10 05:00:50 +00:00
if ( ! is_null ( $deny_gid )) {
$deny_gid = trim ( $deny_gid );
2018-12-11 19:03:29 +00:00
$updated_fields [ 'deny_gid' ] = $deny_gid ;
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
$result = false ;
2018-12-11 19:03:29 +00:00
if ( count ( $updated_fields ) > 0 ) {
2017-11-10 05:00:50 +00:00
$nothingtodo = false ;
2021-11-17 22:44:52 +00:00
$result = Photo :: update ( $updated_fields , [ 'uid' => BaseApi :: getCurrentUserID (), 'resource-id' => $photo_id , 'album' => $album ]);
2017-11-10 05:00:50 +00:00
} else {
$nothingtodo = true ;
}
2016-09-25 16:50:08 +00:00
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_FILES [ 'media' ])) {
2017-11-10 05:00:50 +00:00
$nothingtodo = false ;
$media = $_FILES [ 'media' ];
2021-10-11 14:21:10 +00:00
$data = save_media_to_database ( " photo " , $media , $type , $album , $allow_cid , $deny_cid , $allow_gid , $deny_gid , $desc , Photo :: DEFAULT , $visibility , $photo_id );
2017-11-10 05:00:50 +00:00
if ( ! is_null ( $data )) {
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " photo_update " , $type , $data );
2016-09-25 16:50:08 +00:00
}
}
2017-11-10 05:00:50 +00:00
// return success of updating or error message
if ( $result ) {
2018-01-15 13:05:12 +00:00
$answer = [ 'result' => 'updated' , 'message' => 'Image id `' . $photo_id . '` has been updated.' ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " photo_update " , $type , [ '$result' => $answer ]);
2017-11-10 05:00:50 +00:00
} else {
if ( $nothingtodo ) {
2018-01-15 13:05:12 +00:00
$answer = [ 'result' => 'cancelled' , 'message' => 'Nothing to update for image id `' . $photo_id . '`.' ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " photo_update " , $type , [ '$result' => $answer ]);
2017-11-10 05:00:50 +00:00
}
throw new InternalServerErrorException ( " unknown error - update photo entry in database failed " );
}
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
throw new InternalServerErrorException ( " unknown error - this error on uploading or updating a photo should never happen " );
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* returns the details of a specified photo id , if scale is given , returns the photo data in base 64
2017-11-10 05:00:50 +00:00
*
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
2017-12-25 20:14:02 +00:00
* @ return string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws InternalServerErrorException
* @ throws NotFoundException
2017-11-10 05:00:50 +00:00
*/
function api_fr_photo_detail ( $type )
{
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () === false ) {
2017-11-10 05:00:50 +00:00
throw new ForbiddenException ();
}
2018-11-30 14:06:22 +00:00
if ( empty ( $_REQUEST [ 'photo_id' ])) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( " No photo id. " );
}
2016-09-25 16:50:08 +00:00
2018-11-30 14:06:22 +00:00
$scale = ( ! empty ( $_REQUEST [ 'scale' ]) ? intval ( $_REQUEST [ 'scale' ]) : false );
2017-11-10 05:00:50 +00:00
$photo_id = $_REQUEST [ 'photo_id' ];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// prepare json/xml output with data from database for the requested photo
$data = prepare_photo_data ( $type , $scale , $photo_id );
2016-09-25 16:50:08 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " photo_detail " , $type , $data );
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* updates the profile image for the user ( either a specified profile or the default profile )
2017-11-10 05:00:50 +00:00
*
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
2017-12-24 02:20:50 +00:00
*
2018-04-09 17:34:02 +00:00
* @ return string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws NotFoundException
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / manage - account - settings / api - reference / post - account - update_profile_image
2017-11-10 05:00:50 +00:00
*/
function api_account_update_profile_image ( $type )
{
2021-11-17 22:44:52 +00:00
if ( BaseApi :: getCurrentUserID () === false ) {
2017-11-10 05:00:50 +00:00
throw new ForbiddenException ();
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
// input params
2019-10-13 02:01:34 +00:00
$profile_id = $_REQUEST [ 'profile_id' ] ? ? 0 ;
2017-04-05 20:17:15 +00:00
2017-11-10 05:00:50 +00:00
// error if image data is missing
2018-11-30 14:06:22 +00:00
if ( empty ( $_FILES [ 'image' ])) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( " no media data submitted " );
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
// check if specified profile id is valid
2018-05-04 21:43:29 +00:00
if ( $profile_id != 0 ) {
2021-11-17 22:44:52 +00:00
$profile = DBA :: selectFirst ( 'profile' , [ 'is-default' ], [ 'uid' => BaseApi :: getCurrentUserID (), 'id' => $profile_id ]);
2017-11-10 05:00:50 +00:00
// error message if specified profile id is not in database
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $profile )) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( " profile_id not available " );
2016-09-25 16:50:08 +00:00
}
2018-05-04 06:33:36 +00:00
$is_default_profile = $profile [ 'is-default' ];
2017-11-10 05:00:50 +00:00
} else {
$is_default_profile = 1 ;
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
// get mediadata from image or media (Twitter call api/account/update_profile_image provides image)
$media = null ;
2018-11-30 14:06:22 +00:00
if ( ! empty ( $_FILES [ 'image' ])) {
2017-11-10 05:00:50 +00:00
$media = $_FILES [ 'image' ];
2018-11-30 14:06:22 +00:00
} elseif ( ! empty ( $_FILES [ 'media' ])) {
2017-11-10 05:00:50 +00:00
$media = $_FILES [ 'media' ];
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
// save new profile image
2021-10-14 04:12:00 +00:00
$data = save_media_to_database ( " profileimage " , $media , $type , DI :: l10n () -> t ( Photo :: PROFILE_PHOTOS ), " " , " " , " " , " " , " " , Photo :: USER_AVATAR );
2017-11-10 05:00:50 +00:00
// get filetype
if ( is_array ( $media [ 'type' ])) {
$filetype = $media [ 'type' ][ 0 ];
} else {
$filetype = $media [ 'type' ];
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
if ( $filetype == " image/jpeg " ) {
$fileext = " jpg " ;
} elseif ( $filetype == " image/png " ) {
$fileext = " png " ;
2018-04-09 19:34:53 +00:00
} else {
throw new InternalServerErrorException ( 'Unsupported filetype' );
2017-05-01 20:19:48 +00:00
}
2018-05-13 14:46:58 +00:00
2017-11-10 05:00:50 +00:00
// change specified profile or all profiles to the new resource-id
if ( $is_default_profile ) {
2021-11-17 22:44:52 +00:00
$condition = [ " `profile` AND `resource-id` != ? AND `uid` = ? " , $data [ 'photo' ][ 'id' ], BaseApi :: getCurrentUserID ()];
2021-10-11 14:21:10 +00:00
Photo :: update ([ 'profile' => false , 'photo-type' => Photo :: DEFAULT ], $condition );
2017-11-10 05:00:50 +00:00
} else {
2019-12-30 22:00:08 +00:00
$fields = [ 'photo' => DI :: baseUrl () . '/photo/' . $data [ 'photo' ][ 'id' ] . '-4.' . $fileext ,
'thumb' => DI :: baseUrl () . '/photo/' . $data [ 'photo' ][ 'id' ] . '-5.' . $fileext ];
2021-11-17 22:44:52 +00:00
DBA :: update ( 'profile' , $fields , [ 'id' => $_REQUEST [ 'profile' ], 'uid' => BaseApi :: getCurrentUserID ()]);
2016-09-25 16:50:08 +00:00
}
2021-11-17 22:44:52 +00:00
Contact :: updateSelfFromUserID ( BaseApi :: getCurrentUserID (), true );
2017-05-01 20:19:48 +00:00
2017-11-10 05:00:50 +00:00
// Update global directory in background
2021-11-17 22:44:52 +00:00
Profile :: publishUpdate ( BaseApi :: getCurrentUserID ());
2017-05-01 20:19:48 +00:00
2017-11-10 05:00:50 +00:00
// output for client
if ( $data ) {
return api_account_verify_credentials ( $type );
} else {
// SaveMediaToDatabase failed for some reason
throw new InternalServerErrorException ( " image upload failed " );
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
}
// place api-register for photoalbum calls before 'api/friendica/photo', otherwise this function is never reached
api_register_func ( 'api/friendica/photos/list' , 'api_fr_photos_list' , true );
api_register_func ( 'api/friendica/photo/create' , 'api_fr_photo_create_update' , true , API_METHOD_POST );
api_register_func ( 'api/friendica/photo/update' , 'api_fr_photo_create_update' , true , API_METHOD_POST );
api_register_func ( 'api/friendica/photo' , 'api_fr_photo_detail' , true );
api_register_func ( 'api/account/update_profile_image' , 'api_account_update_profile_image' , true , API_METHOD_POST );
2017-12-22 23:46:01 +00:00
/**
* Update user profile
*
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-22 23:46:01 +00:00
*/
function api_account_update_profile ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2021-11-17 22:44:52 +00:00
$local_user = BaseApi :: getCurrentUserID ();
2021-11-17 21:28:51 +00:00
2021-11-17 22:44:52 +00:00
$api_user = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2017-12-22 23:46:01 +00:00
2017-12-26 00:05:00 +00:00
if ( ! empty ( $_POST [ 'name' ])) {
2018-07-20 12:19:26 +00:00
DBA :: update ( 'profile' , [ 'name' => $_POST [ 'name' ]], [ 'uid' => $local_user ]);
DBA :: update ( 'user' , [ 'username' => $_POST [ 'name' ]], [ 'uid' => $local_user ]);
2021-09-10 18:21:19 +00:00
Contact :: update ([ 'name' => $_POST [ 'name' ]], [ 'uid' => $local_user , 'self' => 1 ]);
Contact :: update ([ 'name' => $_POST [ 'name' ]], [ 'id' => $api_user [ 'id' ]]);
2017-12-22 23:46:01 +00:00
}
2017-12-26 00:05:00 +00:00
if ( isset ( $_POST [ 'description' ])) {
2018-07-20 12:19:26 +00:00
DBA :: update ( 'profile' , [ 'about' => $_POST [ 'description' ]], [ 'uid' => $local_user ]);
2021-09-10 18:21:19 +00:00
Contact :: update ([ 'about' => $_POST [ 'description' ]], [ 'uid' => $local_user , 'self' => 1 ]);
Contact :: update ([ 'about' => $_POST [ 'description' ]], [ 'id' => $api_user [ 'id' ]]);
2017-12-22 23:46:01 +00:00
}
2021-06-15 11:12:44 +00:00
Profile :: publishUpdate ( $local_user );
2017-12-22 23:46:01 +00:00
return api_account_verify_credentials ( $type );
}
/// @TODO move to top of file or somewhere better
api_register_func ( 'api/account/update_profile' , 'api_account_update_profile' , true , API_METHOD_POST );
2017-11-10 05:00:50 +00:00
2017-12-24 02:20:50 +00:00
/**
*
* @ param string $acl_string
2019-01-07 17:24:01 +00:00
* @ return bool
* @ throws Exception
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function check_acl_input ( $acl_string )
{
2019-10-13 02:01:34 +00:00
if ( empty ( $acl_string )) {
2017-11-10 05:00:50 +00:00
return false ;
2017-05-01 20:19:48 +00:00
}
2019-10-13 02:01:34 +00:00
2017-11-10 05:00:50 +00:00
$contact_not_found = false ;
// split <x><y><z> into array of cid's
preg_match_all ( " /<[A-Za-z0-9]+>/ " , $acl_string , $array );
// check for each cid if it is available on server
$cid_array = $array [ 0 ];
foreach ( $cid_array as $cid ) {
$cid = str_replace ( " < " , " " , $cid );
$cid = str_replace ( " > " , " " , $cid );
2021-11-17 22:44:52 +00:00
$condition = [ 'id' => $cid , 'uid' => BaseApi :: getCurrentUserID ()];
2018-08-19 12:46:11 +00:00
$contact_not_found |= ! DBA :: exists ( 'contact' , $condition );
2017-11-10 05:00:50 +00:00
}
return $contact_not_found ;
}
2017-12-24 02:20:50 +00:00
/**
* @ param string $mediatype
* @ param array $media
* @ param string $type
* @ param string $album
* @ param string $allow_cid
* @ param string $deny_cid
* @ param string $allow_gid
* @ param string $deny_gid
* @ param string $desc
2021-10-11 14:21:10 +00:00
* @ param integer $phototype
2017-12-24 02:20:50 +00:00
* @ param boolean $visibility
* @ param string $photo_id
2019-01-07 17:24:01 +00:00
* @ return array
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws NotFoundException
2019-10-13 02:01:34 +00:00
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
*/
2021-10-11 14:21:10 +00:00
function save_media_to_database ( $mediatype , $media , $type , $album , $allow_cid , $deny_cid , $allow_gid , $deny_gid , $desc , $phototype = 0 , $visibility = false , $photo_id = null )
2017-11-10 05:00:50 +00:00
{
$visitor = 0 ;
$src = " " ;
$filetype = " " ;
$filename = " " ;
$filesize = 0 ;
if ( is_array ( $media )) {
if ( is_array ( $media [ 'tmp_name' ])) {
$src = $media [ 'tmp_name' ][ 0 ];
} else {
$src = $media [ 'tmp_name' ];
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
if ( is_array ( $media [ 'name' ])) {
$filename = basename ( $media [ 'name' ][ 0 ]);
2017-05-01 20:19:48 +00:00
} else {
2017-11-10 05:00:50 +00:00
$filename = basename ( $media [ 'name' ]);
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
if ( is_array ( $media [ 'size' ])) {
$filesize = intval ( $media [ 'size' ][ 0 ]);
} else {
$filesize = intval ( $media [ 'size' ]);
2017-05-01 20:19:48 +00:00
}
if ( is_array ( $media [ 'type' ])) {
$filetype = $media [ 'type' ][ 0 ];
} else {
$filetype = $media [ 'type' ];
}
}
2020-04-01 05:42:44 +00:00
$filetype = Images :: getMimeTypeBySource ( $src , $filename , $filetype );
2021-10-03 19:43:49 +00:00
logger :: info (
2017-11-10 05:00:50 +00:00
" File upload src: " . $src . " - filename: " . $filename .
2021-10-03 19:43:49 +00:00
" - size: " . $filesize . " - type: " . $filetype );
2017-11-10 05:00:50 +00:00
// check if there was a php upload error
if ( $filesize == 0 && $media [ 'error' ] == 1 ) {
throw new InternalServerErrorException ( " image size exceeds PHP config settings, file was rejected by server " );
}
// check against max upload size within Friendica instance
2020-01-19 20:21:13 +00:00
$maximagesize = DI :: config () -> get ( 'system' , 'maximagesize' );
2017-12-22 23:10:32 +00:00
if ( $maximagesize && ( $filesize > $maximagesize )) {
2018-11-08 15:28:49 +00:00
$formattedBytes = Strings :: formatBytes ( $maximagesize );
2017-11-10 05:00:50 +00:00
throw new InternalServerErrorException ( " image size exceeds Friendica config setting (uploaded size: $formattedBytes ) " );
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
// create Photo instance with the data of the image
$imagedata = @ file_get_contents ( $src );
2017-12-07 13:56:11 +00:00
$Image = new Image ( $imagedata , $filetype );
2018-10-01 17:36:23 +00:00
if ( ! $Image -> isValid ()) {
2017-11-10 05:00:50 +00:00
throw new InternalServerErrorException ( " unable to process image data " );
}
2017-05-01 20:19:48 +00:00
2017-11-10 05:00:50 +00:00
// check orientation of image
2017-12-07 13:56:11 +00:00
$Image -> orient ( $src );
2017-11-10 05:00:50 +00:00
@ unlink ( $src );
2017-05-01 20:19:48 +00:00
2017-11-10 05:00:50 +00:00
// check max length of images on server
2020-01-19 20:21:13 +00:00
$max_length = DI :: config () -> get ( 'system' , 'max_image_length' );
2017-11-10 05:00:50 +00:00
if ( $max_length > 0 ) {
2017-12-07 13:56:11 +00:00
$Image -> scaleDown ( $max_length );
2021-10-03 19:43:49 +00:00
logger :: info ( " File upload: Scaling picture to new size " . $max_length );
2017-11-10 05:00:50 +00:00
}
2017-12-07 13:56:11 +00:00
$width = $Image -> getWidth ();
$height = $Image -> getHeight ();
2017-05-01 20:19:48 +00:00
2017-11-10 05:00:50 +00:00
// create a new resource-id if not already provided
2019-10-26 13:05:35 +00:00
$resource_id = ( $photo_id == null ) ? Photo :: newResource () : $photo_id ;
2017-05-01 20:19:48 +00:00
2017-11-10 05:00:50 +00:00
if ( $mediatype == " photo " ) {
// upload normal image (scales 0, 1, 2)
2021-10-03 19:43:49 +00:00
logger :: info ( " photo upload: starting new photo upload " );
2017-05-01 20:19:48 +00:00
2021-10-11 14:21:10 +00:00
$r = Photo :: store ( $Image , local_user (), $visitor , $resource_id , $filename , $album , 0 , Photo :: DEFAULT , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $desc );
2018-10-01 17:36:23 +00:00
if ( ! $r ) {
2021-10-03 19:43:49 +00:00
logger :: notice ( " photo upload: image upload with scale 0 (original size) failed " );
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
if ( $width > 640 || $height > 640 ) {
2017-12-07 13:56:11 +00:00
$Image -> scaleDown ( 640 );
2021-10-11 14:21:10 +00:00
$r = Photo :: store ( $Image , local_user (), $visitor , $resource_id , $filename , $album , 1 , Photo :: DEFAULT , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $desc );
2018-10-01 17:36:23 +00:00
if ( ! $r ) {
2021-10-03 19:43:49 +00:00
logger :: notice ( " photo upload: image upload with scale 1 (640x640) failed " );
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
}
2017-05-01 20:19:48 +00:00
2017-11-10 05:00:50 +00:00
if ( $width > 320 || $height > 320 ) {
2017-12-07 13:56:11 +00:00
$Image -> scaleDown ( 320 );
2021-10-11 14:21:10 +00:00
$r = Photo :: store ( $Image , local_user (), $visitor , $resource_id , $filename , $album , 2 , Photo :: DEFAULT , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $desc );
2018-10-01 17:36:23 +00:00
if ( ! $r ) {
2021-10-03 19:43:49 +00:00
logger :: notice ( " photo upload: image upload with scale 2 (320x320) failed " );
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
}
2021-10-03 19:43:49 +00:00
logger :: info ( " photo upload: new photo upload ended " );
2017-11-10 05:00:50 +00:00
} elseif ( $mediatype == " profileimage " ) {
// upload profile image (scales 4, 5, 6)
2021-10-03 19:43:49 +00:00
logger :: info ( " photo upload: starting new profile image upload " );
2017-05-01 20:19:48 +00:00
2018-10-23 14:36:57 +00:00
if ( $width > 300 || $height > 300 ) {
$Image -> scaleDown ( 300 );
2021-10-11 14:21:10 +00:00
$r = Photo :: store ( $Image , local_user (), $visitor , $resource_id , $filename , $album , 4 , $phototype , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $desc );
2018-10-01 17:36:23 +00:00
if ( ! $r ) {
2021-10-03 19:43:49 +00:00
logger :: notice ( " photo upload: profile image upload with scale 4 (300x300) failed " );
2017-05-01 20:19:48 +00:00
}
2017-11-10 05:00:50 +00:00
}
2017-05-01 20:19:48 +00:00
2017-11-10 05:00:50 +00:00
if ( $width > 80 || $height > 80 ) {
2017-12-07 13:56:11 +00:00
$Image -> scaleDown ( 80 );
2021-10-11 14:21:10 +00:00
$r = Photo :: store ( $Image , local_user (), $visitor , $resource_id , $filename , $album , 5 , $phototype , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $desc );
2018-10-01 17:36:23 +00:00
if ( ! $r ) {
2021-10-03 19:43:49 +00:00
logger :: notice ( " photo upload: profile image upload with scale 5 (80x80) failed " );
2017-05-01 20:19:48 +00:00
}
}
2017-11-10 05:00:50 +00:00
if ( $width > 48 || $height > 48 ) {
2017-12-07 13:56:11 +00:00
$Image -> scaleDown ( 48 );
2021-10-11 14:21:10 +00:00
$r = Photo :: store ( $Image , local_user (), $visitor , $resource_id , $filename , $album , 6 , $phototype , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $desc );
2018-10-01 17:36:23 +00:00
if ( ! $r ) {
2021-10-03 19:43:49 +00:00
logger :: notice ( " photo upload: profile image upload with scale 6 (48x48) failed " );
2017-05-01 20:19:48 +00:00
}
}
2017-12-07 13:56:11 +00:00
$Image -> __destruct ();
2021-10-03 19:43:49 +00:00
logger :: info ( " photo upload: new profile image upload ended " );
2017-05-01 20:19:48 +00:00
}
2020-06-24 12:11:47 +00:00
if ( ! empty ( $r )) {
2017-11-10 05:00:50 +00:00
// create entry in 'item'-table on new uploads to enable users to comment/like/dislike the photo
if ( $photo_id == null && $mediatype == " photo " ) {
2019-10-26 13:05:35 +00:00
post_photo_item ( $resource_id , $allow_cid , $deny_cid , $allow_gid , $deny_gid , $filetype , $visibility );
2017-11-10 05:00:50 +00:00
}
// on success return image data in json/xml format (like /api/friendica/photo does when no scale is given)
2019-10-26 13:05:35 +00:00
return prepare_photo_data ( $type , false , $resource_id );
2017-11-10 05:00:50 +00:00
} else {
throw new InternalServerErrorException ( " image upload failed " );
}
}
2017-12-24 02:20:50 +00:00
/**
*
* @ param string $hash
* @ param string $allow_cid
* @ param string $deny_cid
* @ param string $allow_gid
* @ param string $deny_gid
* @ param string $filetype
* @ param boolean $visibility
2019-01-07 17:24:01 +00:00
* @ throws InternalServerErrorException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function post_photo_item ( $hash , $allow_cid , $deny_cid , $allow_gid , $deny_gid , $filetype , $visibility = false )
{
// get data about the api authenticated user
2021-11-17 22:44:52 +00:00
$uri = Item :: newURI ( intval ( BaseApi :: getCurrentUserID ()));
$owner_record = DBA :: selectFirst ( 'contact' , [], [ 'uid' => BaseApi :: getCurrentUserID (), 'self' => true ]);
2017-11-10 05:00:50 +00:00
2018-01-15 13:05:12 +00:00
$arr = [];
2018-09-27 11:52:15 +00:00
$arr [ 'guid' ] = System :: createUUID ();
2021-11-17 22:44:52 +00:00
$arr [ 'uid' ] = intval ( BaseApi :: getCurrentUserID ());
2017-11-10 05:00:50 +00:00
$arr [ 'uri' ] = $uri ;
$arr [ 'type' ] = 'photo' ;
$arr [ 'wall' ] = 1 ;
$arr [ 'resource-id' ] = $hash ;
2018-08-19 12:46:11 +00:00
$arr [ 'contact-id' ] = $owner_record [ 'id' ];
$arr [ 'owner-name' ] = $owner_record [ 'name' ];
$arr [ 'owner-link' ] = $owner_record [ 'url' ];
$arr [ 'owner-avatar' ] = $owner_record [ 'thumb' ];
$arr [ 'author-name' ] = $owner_record [ 'name' ];
$arr [ 'author-link' ] = $owner_record [ 'url' ];
$arr [ 'author-avatar' ] = $owner_record [ 'thumb' ];
2017-11-10 05:00:50 +00:00
$arr [ 'title' ] = " " ;
$arr [ 'allow_cid' ] = $allow_cid ;
$arr [ 'allow_gid' ] = $allow_gid ;
$arr [ 'deny_cid' ] = $deny_cid ;
$arr [ 'deny_gid' ] = $deny_gid ;
$arr [ 'visible' ] = $visibility ;
$arr [ 'origin' ] = 1 ;
2018-01-15 13:05:12 +00:00
$typetoext = [
2017-04-05 20:07:55 +00:00
'image/jpeg' => 'jpg' ,
'image/png' => 'png' ,
'image/gif' => 'gif'
2018-01-15 13:05:12 +00:00
];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// adds link to the thumbnail scale photo
2019-12-30 22:00:08 +00:00
$arr [ 'body' ] = '[url=' . DI :: baseUrl () . '/photos/' . $owner_record [ 'nick' ] . '/image/' . $hash . ']'
. '[img]' . DI :: baseUrl () . '/photo/' . $hash . '-' . " 2 " . '.' . $typetoext [ $filetype ] . '[/img]'
2017-11-10 05:00:50 +00:00
. '[/url]' ;
// do the magic for storing the item in the database and trigger the federation to other contacts
2018-01-28 11:18:08 +00:00
Item :: insert ( $arr );
2017-11-10 05:00:50 +00:00
}
2017-12-24 02:20:50 +00:00
/**
*
* @ param string $type
* @ param int $scale
* @ param string $photo_id
*
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws NotFoundException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function prepare_photo_data ( $type , $scale , $photo_id )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2018-04-09 15:36:33 +00:00
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2018-04-09 15:36:33 +00:00
2017-11-10 05:00:50 +00:00
$scale_sql = ( $scale === false ? " " : sprintf ( " AND scale=%d " , intval ( $scale )));
$data_sql = ( $scale === false ? " " : " data, " );
// added allow_cid, allow_gid, deny_cid, deny_gid to output as string like stored in database
// clients needs to convert this in their way for further processing
2021-10-09 21:16:15 +00:00
$r = DBA :: toArray ( DBA :: p (
2021-10-09 21:30:14 +00:00
" SELECT $data_sql `resource-id`, `created`, `edited`, `title`, `desc`, `album`, `filename`,
2017-11-10 05:00:50 +00:00
`type` , `height` , `width` , `datasize` , `profile` , `allow_cid` , `deny_cid` , `allow_gid` , `deny_gid` ,
MIN ( `scale` ) AS `minscale` , MAX ( `scale` ) AS `maxscale`
2021-10-09 21:16:15 +00:00
FROM `photo` WHERE `uid` = ? AND `resource-id` = ? $scale_sql GROUP BY
2021-11-09 21:41:37 +00:00
`resource-id` , `created` , `edited` , `title` , `desc` , `album` , `filename` ,
`type` , `height` , `width` , `datasize` , `profile` , `allow_cid` , `deny_cid` , `allow_gid` , `deny_gid` " ,
2021-10-09 21:16:15 +00:00
local_user (),
$photo_id
));
2017-11-10 05:00:50 +00:00
2018-01-15 13:05:12 +00:00
$typetoext = [
2017-11-10 05:00:50 +00:00
'image/jpeg' => 'jpg' ,
'image/png' => 'png' ,
'image/gif' => 'gif'
2018-01-15 13:05:12 +00:00
];
2017-11-10 05:00:50 +00:00
// prepare output data for photo
2018-07-21 12:46:04 +00:00
if ( DBA :: isResult ( $r )) {
2018-01-15 13:05:12 +00:00
$data = [ 'photo' => $r [ 0 ]];
2017-11-10 05:00:50 +00:00
$data [ 'photo' ][ 'id' ] = $data [ 'photo' ][ 'resource-id' ];
if ( $scale !== false ) {
$data [ 'photo' ][ 'data' ] = base64_encode ( $data [ 'photo' ][ 'data' ]);
2016-09-25 16:50:08 +00:00
} else {
2017-11-10 05:00:50 +00:00
unset ( $data [ 'photo' ][ 'datasize' ]); //needed only with scale param
2016-09-25 16:50:08 +00:00
}
2017-05-01 20:19:48 +00:00
if ( $type == " xml " ) {
2018-01-15 13:05:12 +00:00
$data [ 'photo' ][ 'links' ] = [];
2017-11-10 05:00:50 +00:00
for ( $k = intval ( $data [ 'photo' ][ 'minscale' ]); $k <= intval ( $data [ 'photo' ][ 'maxscale' ]); $k ++ ) {
2018-01-15 13:05:12 +00:00
$data [ 'photo' ][ 'links' ][ $k . " :link " ][ " @attributes " ] = [ " type " => $data [ 'photo' ][ 'type' ],
2017-11-10 05:00:50 +00:00
" scale " => $k ,
2019-12-30 22:00:08 +00:00
" href " => DI :: baseUrl () . " /photo/ " . $data [ 'photo' ][ 'resource-id' ] . " - " . $k . " . " . $typetoext [ $data [ 'photo' ][ 'type' ]]];
2017-05-01 20:19:48 +00:00
}
} else {
2018-01-15 13:05:12 +00:00
$data [ 'photo' ][ 'link' ] = [];
2017-11-10 05:00:50 +00:00
// when we have profile images we could have only scales from 4 to 6, but index of array always needs to start with 0
$i = 0 ;
for ( $k = intval ( $data [ 'photo' ][ 'minscale' ]); $k <= intval ( $data [ 'photo' ][ 'maxscale' ]); $k ++ ) {
2019-12-30 22:00:08 +00:00
$data [ 'photo' ][ 'link' ][ $i ] = DI :: baseUrl () . " /photo/ " . $data [ 'photo' ][ 'resource-id' ] . " - " . $k . " . " . $typetoext [ $data [ 'photo' ][ 'type' ]];
2017-11-10 05:00:50 +00:00
$i ++ ;
2017-05-01 20:19:48 +00:00
}
}
2017-11-10 05:00:50 +00:00
unset ( $data [ 'photo' ][ 'resource-id' ]);
unset ( $data [ 'photo' ][ 'minscale' ]);
unset ( $data [ 'photo' ][ 'maxscale' ]);
} else {
throw new NotFoundException ();
}
// retrieve item element for getting activities (like, dislike etc.) related to photo
2021-11-17 22:44:52 +00:00
$condition = [ 'uid' => BaseApi :: getCurrentUserID (), 'resource-id' => $photo_id ];
2021-01-16 04:11:28 +00:00
$item = Post :: selectFirst ([ 'id' , 'uid' , 'uri' , 'parent' , 'allow_cid' , 'deny_cid' , 'allow_gid' , 'deny_gid' ], $condition );
2020-01-26 21:43:14 +00:00
if ( ! DBA :: isResult ( $item )) {
throw new NotFoundException ( 'Photo-related item not found.' );
}
2018-06-18 20:36:34 +00:00
$data [ 'photo' ][ 'friendica_activities' ] = api_format_items_activities ( $item , $type );
2017-11-10 05:00:50 +00:00
// retrieve comments on photo
2021-03-07 22:40:47 +00:00
$condition = [ " `parent` = ? AND `uid` = ? AND `gravity` IN (?, ?) " ,
2021-11-17 22:44:52 +00:00
$item [ 'parent' ], BaseApi :: getCurrentUserID (), GRAVITY_PARENT , GRAVITY_COMMENT ];
2018-06-09 16:56:37 +00:00
2021-11-17 22:44:52 +00:00
$statuses = Post :: selectForUser ( BaseApi :: getCurrentUserID (), [], $condition );
2017-11-10 05:00:50 +00:00
// prepare output of comments
2021-01-16 22:37:27 +00:00
$commentData = api_format_items ( Post :: toArray ( $statuses ), $user_info , false , $type );
2018-01-15 13:05:12 +00:00
$comments = [];
2017-11-10 05:00:50 +00:00
if ( $type == " xml " ) {
$k = 0 ;
foreach ( $commentData as $comment ) {
$comments [ $k ++ . " :comment " ] = $comment ;
2017-04-05 20:07:55 +00:00
}
2017-11-10 05:00:50 +00:00
} else {
foreach ( $commentData as $comment ) {
$comments [] = $comment ;
}
}
$data [ 'photo' ][ 'friendica_comments' ] = $comments ;
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// include info if rights on photo and rights on item are mismatching
2020-06-24 12:11:47 +00:00
$rights_mismatch = $data [ 'photo' ][ 'allow_cid' ] != $item [ 'allow_cid' ] ||
$data [ 'photo' ][ 'deny_cid' ] != $item [ 'deny_cid' ] ||
$data [ 'photo' ][ 'allow_gid' ] != $item [ 'allow_gid' ] ||
$data [ 'photo' ][ 'deny_gid' ] != $item [ 'deny_gid' ];
2017-11-10 05:00:50 +00:00
$data [ 'photo' ][ 'rights_mismatch' ] = $rights_mismatch ;
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
return $data ;
}
2016-09-25 16:50:08 +00:00
2019-04-24 04:26:23 +00:00
/**
* Return an item with announcer data if it had been announced
*
* @ param array $item Item array
* @ return array Item array with announce data
*/
function api_get_announce ( $item )
{
// Quit if the item already has got a different owner and author
if ( $item [ 'owner-id' ] != $item [ 'author-id' ]) {
return [];
}
// Don't change original or Diaspora posts
if ( $item [ 'origin' ] || in_array ( $item [ 'network' ], [ Protocol :: DIASPORA ])) {
return [];
}
// Quit if we do now the original author and it had been a post from a native network
if ( ! empty ( $item [ 'contact-uid' ]) && in_array ( $item [ 'network' ], Protocol :: NATIVE_SUPPORT )) {
return [];
}
$fields = [ 'author-id' , 'author-name' , 'author-link' , 'author-avatar' ];
2020-05-26 05:18:50 +00:00
$condition = [ 'parent-uri' => $item [ 'uri' ], 'gravity' => GRAVITY_ACTIVITY , 'uid' => [ 0 , $item [ 'uid' ]], 'vid' => Verb :: getID ( Activity :: ANNOUNCE )];
2021-01-16 22:37:27 +00:00
$announce = Post :: selectFirstForUser ( $item [ 'uid' ], $fields , $condition , [ 'order' => [ 'received' => true ]]);
2019-04-24 04:26:23 +00:00
if ( ! DBA :: isResult ( $announce )) {
return [];
}
return array_merge ( $item , $announce );
}
2017-12-24 02:20:50 +00:00
/**
*
* @ param array $item
*
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws Exception
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_in_reply_to ( $item )
{
2018-01-15 13:05:12 +00:00
$in_reply_to = [];
2016-11-04 22:45:20 +00:00
2017-11-10 05:00:50 +00:00
$in_reply_to [ 'status_id' ] = null ;
$in_reply_to [ 'user_id' ] = null ;
$in_reply_to [ 'status_id_str' ] = null ;
$in_reply_to [ 'user_id_str' ] = null ;
$in_reply_to [ 'screen_name' ] = null ;
2016-11-04 22:45:20 +00:00
2020-05-28 16:02:36 +00:00
if (( $item [ 'thr-parent' ] != $item [ 'uri' ]) && ( $item [ 'gravity' ] != GRAVITY_PARENT )) {
2021-01-16 04:11:28 +00:00
$parent = Post :: selectFirst ([ 'id' ], [ 'uid' => $item [ 'uid' ], 'uri' => $item [ 'thr-parent' ]]);
2018-07-21 12:46:04 +00:00
if ( DBA :: isResult ( $parent )) {
2018-06-18 20:36:34 +00:00
$in_reply_to [ 'status_id' ] = intval ( $parent [ 'id' ]);
2017-11-10 05:00:50 +00:00
} else {
$in_reply_to [ 'status_id' ] = intval ( $item [ 'parent' ]);
}
2016-11-04 22:45:20 +00:00
2017-11-10 05:00:50 +00:00
$in_reply_to [ 'status_id_str' ] = ( string ) intval ( $in_reply_to [ 'status_id' ]);
2016-11-04 22:45:20 +00:00
2018-06-18 20:36:34 +00:00
$fields = [ 'author-nick' , 'author-name' , 'author-id' , 'author-link' ];
2021-01-16 04:11:28 +00:00
$parent = Post :: selectFirst ( $fields , [ 'id' => $in_reply_to [ 'status_id' ]]);
2016-11-04 22:45:20 +00:00
2018-07-21 12:46:04 +00:00
if ( DBA :: isResult ( $parent )) {
2018-06-18 20:36:34 +00:00
$in_reply_to [ 'screen_name' ] = (( $parent [ 'author-nick' ]) ? $parent [ 'author-nick' ] : $parent [ 'author-name' ]);
$in_reply_to [ 'user_id' ] = intval ( $parent [ 'author-id' ]);
$in_reply_to [ 'user_id_str' ] = ( string ) intval ( $parent [ 'author-id' ]);
2016-11-04 22:45:20 +00:00
}
2017-11-10 05:00:50 +00:00
// There seems to be situation, where both fields are identical:
// https://github.com/friendica/friendica/issues/1010
// This is a bugfix for that.
if ( intval ( $in_reply_to [ 'status_id' ]) == intval ( $item [ 'id' ])) {
2018-12-30 20:42:56 +00:00
Logger :: warning ( API_LOG_PREFIX . 'ID {id} is similar to reply-to {reply-to}' , [ 'module' => 'api' , 'action' => 'in_reply_to' , 'id' => $item [ 'id' ], 'reply-to' => $in_reply_to [ 'status_id' ]]);
2017-11-10 05:00:50 +00:00
$in_reply_to [ 'status_id' ] = null ;
$in_reply_to [ 'user_id' ] = null ;
$in_reply_to [ 'status_id_str' ] = null ;
$in_reply_to [ 'user_id_str' ] = null ;
$in_reply_to [ 'screen_name' ] = null ;
}
2016-11-04 22:45:20 +00:00
}
2017-11-10 05:00:50 +00:00
return $in_reply_to ;
}
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
*
2018-05-10 13:13:08 +00:00
* @ param string $text
2017-12-24 02:20:50 +00:00
*
* @ return string
2019-01-07 17:24:01 +00:00
* @ throws InternalServerErrorException
2017-12-24 02:20:50 +00:00
*/
2018-05-10 13:13:08 +00:00
function api_clean_plain_items ( $text )
2017-11-10 05:00:50 +00:00
{
2019-10-13 02:01:34 +00:00
$include_entities = strtolower ( $_REQUEST [ 'include_entities' ] ? ? 'false' );
2016-09-25 16:50:08 +00:00
2018-05-10 13:13:08 +00:00
$text = BBCode :: cleanPictureLinks ( $text );
2017-11-10 05:00:50 +00:00
$URLSearchString = " ^ \ [ \ ] " ;
2016-09-25 16:50:08 +00:00
2018-05-10 13:13:08 +00:00
$text = preg_replace ( " /([!#@]) \ [url \ =([ $URLSearchString ]*) \ ](.*?) \ [ \ /url \ ]/ism " , '$1$3' , $text );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $include_entities == " true " ) {
2018-05-10 13:13:08 +00:00
$text = preg_replace ( " / \ [url \ =([ $URLSearchString ]*) \ ](.*?) \ [ \ /url \ ]/ism " , '[url=$1]$1[/url]' , $text );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
// Simplify "attachment" element
2019-12-02 21:19:47 +00:00
$text = BBCode :: removeAttachment ( $text );
2016-09-25 16:50:08 +00:00
2018-05-10 13:13:08 +00:00
return $text ;
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
*
* @ param array $contacts
*
2019-01-07 17:24:01 +00:00
* @ return void
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_best_nickname ( & $contacts )
{
2018-01-15 13:05:12 +00:00
$best_contact = [];
2016-09-25 16:50:08 +00:00
2018-02-11 21:13:29 +00:00
if ( count ( $contacts ) == 0 ) {
2017-11-10 05:00:50 +00:00
return ;
2017-12-23 23:27:45 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-23 23:27:45 +00:00
foreach ( $contacts as $contact ) {
2017-11-10 05:00:50 +00:00
if ( $contact [ " network " ] == " " ) {
$contact [ " network " ] = " dfrn " ;
2018-01-15 13:05:12 +00:00
$best_contact = [ $contact ];
2017-11-10 05:00:50 +00:00
}
2017-12-23 23:27:45 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-23 23:27:45 +00:00
if ( sizeof ( $best_contact ) == 0 ) {
foreach ( $contacts as $contact ) {
if ( $contact [ " network " ] == " dfrn " ) {
2018-01-15 13:05:12 +00:00
$best_contact = [ $contact ];
2017-12-23 23:27:45 +00:00
}
}
}
2016-09-25 16:50:08 +00:00
2017-12-23 23:27:45 +00:00
if ( sizeof ( $best_contact ) == 0 ) {
foreach ( $contacts as $contact ) {
if ( $contact [ " network " ] == " dspr " ) {
2018-01-15 13:05:12 +00:00
$best_contact = [ $contact ];
2017-12-23 23:27:45 +00:00
}
}
}
2016-09-25 16:50:08 +00:00
2017-12-23 23:27:45 +00:00
if ( sizeof ( $best_contact ) == 0 ) {
foreach ( $contacts as $contact ) {
if ( $contact [ " network " ] == " stat " ) {
2018-01-15 13:05:12 +00:00
$best_contact = [ $contact ];
2017-12-23 23:27:45 +00:00
}
}
}
2016-09-25 16:50:08 +00:00
2017-12-23 23:27:45 +00:00
if ( sizeof ( $best_contact ) == 0 ) {
foreach ( $contacts as $contact ) {
if ( $contact [ " network " ] == " pump " ) {
2018-01-15 13:05:12 +00:00
$best_contact = [ $contact ];
2017-12-23 23:27:45 +00:00
}
}
}
2016-09-25 16:50:08 +00:00
2017-12-23 23:27:45 +00:00
if ( sizeof ( $best_contact ) == 0 ) {
foreach ( $contacts as $contact ) {
if ( $contact [ " network " ] == " twit " ) {
2018-01-15 13:05:12 +00:00
$best_contact = [ $contact ];
2017-12-23 23:27:45 +00:00
}
}
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( sizeof ( $best_contact ) == 1 ) {
$contacts = $best_contact ;
} else {
2018-01-15 13:05:12 +00:00
$contacts = [ $contacts [ 0 ]];
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-24 02:20:50 +00:00
/**
* Return all or a specified group of the user with the containing contacts .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_friendica_group_show ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2019-10-13 02:01:34 +00:00
$gid = $_REQUEST [ 'gid' ] ? ? 0 ;
2017-11-10 05:00:50 +00:00
$uid = $user_info [ 'uid' ];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// get data of the specified group id or all groups if not specified
if ( $gid != 0 ) {
2021-10-03 15:02:20 +00:00
$groups = DBA :: selectToArray ( 'group' , [], [ 'deleted' => false , 'uid' => $uid , 'id' => $gid ]);
2016-09-25 16:50:08 +00:00
// error message if specified gid is not in database
2021-10-03 15:02:20 +00:00
if ( ! DBA :: isResult ( $groups )) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( " gid not available " );
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
} else {
2021-10-03 15:02:20 +00:00
$groups = DBA :: selectToArray ( 'group' , [], [ 'deleted' => false , 'uid' => $uid ]);
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
// loop through all groups and retrieve all members for adding data in the user array
2018-04-09 19:34:53 +00:00
$grps = [];
2021-10-03 15:02:20 +00:00
foreach ( $groups as $rr ) {
2020-08-04 18:22:19 +00:00
$members = Contact\Group :: getById ( $rr [ 'id' ]);
2018-01-15 13:05:12 +00:00
$users = [];
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
if ( $type == " xml " ) {
$user_element = " users " ;
$k = 0 ;
foreach ( $members as $member ) {
2021-11-17 22:44:52 +00:00
$user = DI :: twitterUser () -> createFromContactId ( $member [ 'contact-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
$users [ $k ++. " :user " ] = $user ;
}
} else {
$user_element = " user " ;
foreach ( $members as $member ) {
2021-11-17 22:44:52 +00:00
$user = DI :: twitterUser () -> createFromContactId ( $member [ 'contact-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
$users [] = $user ;
2016-09-25 16:50:08 +00:00
}
}
2018-01-15 13:05:12 +00:00
$grps [] = [ 'name' => $rr [ 'name' ], 'gid' => $rr [ 'id' ], $user_element => $users ];
2017-11-10 05:00:50 +00:00
}
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " groups " , $type , [ 'group' => $grps ]);
2017-11-10 05:00:50 +00:00
}
2021-11-12 18:52:01 +00:00
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/friendica/group_show' , 'api_friendica_group_show' , true );
2017-12-24 02:20:50 +00:00
/**
2018-04-07 13:54:26 +00:00
* Delete a group .
2017-12-24 02:20:50 +00:00
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2018-04-07 13:54:26 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / create - manage - lists / api - reference / post - lists - destroy
2017-12-24 02:20:50 +00:00
*/
2018-04-07 13:54:26 +00:00
function api_lists_destroy ( $type )
2017-11-10 05:00:50 +00:00
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2017-11-10 05:00:50 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2019-10-13 02:01:34 +00:00
$gid = $_REQUEST [ 'list_id' ] ? ? 0 ;
2017-11-10 05:00:50 +00:00
$uid = $user_info [ 'uid' ];
2018-04-07 13:54:26 +00:00
// error if no gid specified
if ( $gid == 0 ) {
throw new BadRequestException ( 'gid not specified' );
}
// get data of the specified group id
2018-07-20 12:19:26 +00:00
$group = DBA :: selectFirst ( 'group' , [], [ 'uid' => $uid , 'id' => $gid ]);
2018-04-07 13:54:26 +00:00
// error message if specified gid is not in database
2018-04-07 17:55:41 +00:00
if ( ! $group ) {
2018-04-07 13:54:26 +00:00
throw new BadRequestException ( 'gid not available' );
}
if ( Group :: remove ( $gid )) {
2018-04-07 17:55:41 +00:00
$list = [
'name' => $group [ 'name' ],
2018-04-07 13:54:26 +00:00
'id' => intval ( $gid ),
'id_str' => ( string ) $gid ,
'user' => $user_info
];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " lists " , $type , [ 'lists' => $list ]);
2018-04-07 13:54:26 +00:00
}
}
2021-11-12 18:52:01 +00:00
2018-04-07 13:54:26 +00:00
api_register_func ( 'api/lists/destroy' , 'api_lists_destroy' , true , API_METHOD_DELETE );
/**
* Add a new group to the database .
*
* @ param string $name Group name
2019-01-07 17:24:01 +00:00
* @ param int $uid User ID
2018-04-07 13:54:26 +00:00
* @ param array $users List of users to add to the group
*
* @ return array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
2018-04-07 13:54:26 +00:00
*/
function group_create ( $name , $uid , $users = [])
{
2017-11-10 05:00:50 +00:00
// error if no name specified
2017-12-23 23:27:45 +00:00
if ( $name == " " ) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( 'group name not specified' );
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
2021-10-09 21:16:15 +00:00
// error message if specified group name already exists
2021-10-03 15:02:20 +00:00
if ( DBA :: exists ( 'group' , [ 'uid' => $uid , 'name' => $name , 'deleted' => false ])) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( 'group name already exists' );
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
2021-10-03 15:02:20 +00:00
// Check if the group needs to be reactivated
if ( DBA :: exists ( 'group' , [ 'uid' => $uid , 'name' => $name , 'deleted' => true ])) {
2017-11-10 05:00:50 +00:00
$reactivate_group = true ;
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
// create group
2017-12-09 18:45:17 +00:00
$ret = Group :: create ( $uid , $name );
2017-11-10 05:00:50 +00:00
if ( $ret ) {
2017-12-09 18:45:17 +00:00
$gid = Group :: getIdByName ( $uid , $name );
2017-11-10 05:00:50 +00:00
} else {
throw new BadRequestException ( 'other API error' );
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
// add members
$erroraddinguser = false ;
2018-01-15 13:05:12 +00:00
$errorusers = [];
2017-11-10 05:00:50 +00:00
foreach ( $users as $user ) {
$cid = $user [ 'cid' ];
2021-10-03 15:02:20 +00:00
if ( DBA :: exists ( 'contact' , [ 'id' => $cid , 'uid' => $uid ])) {
2018-01-04 01:54:35 +00:00
Group :: addMember ( $gid , $cid );
2017-12-22 23:10:32 +00:00
} else {
2017-11-10 05:00:50 +00:00
$erroraddinguser = true ;
$errorusers [] = $cid ;
2016-09-25 16:50:08 +00:00
}
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
// return success message incl. missing users in array
2018-04-09 19:34:53 +00:00
$status = ( $erroraddinguser ? " missing user " : (( isset ( $reactivate_group ) && $reactivate_group ) ? " reactivated " : " ok " ));
2018-04-07 13:54:26 +00:00
return [ 'success' => true , 'gid' => $gid , 'name' => $name , 'status' => $status , 'wrong users' => $errorusers ];
}
/**
* Create the specified group with the posted array of contacts .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2018-04-07 13:54:26 +00:00
*/
function api_friendica_group_create ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2018-04-07 13:54:26 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2019-10-13 02:01:34 +00:00
$name = $_REQUEST [ 'name' ] ? ? '' ;
2018-04-07 13:54:26 +00:00
$uid = $user_info [ 'uid' ];
$json = json_decode ( $_POST [ 'json' ], true );
$users = $json [ 'user' ];
$success = group_create ( $name , $uid , $users );
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " group_create " , $type , [ 'result' => $success ]);
2017-11-10 05:00:50 +00:00
}
2021-11-12 18:52:01 +00:00
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/friendica/group_create' , 'api_friendica_group_create' , true , API_METHOD_POST );
2018-04-07 13:54:26 +00:00
/**
* Create a new group .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2018-04-07 13:54:26 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / create - manage - lists / api - reference / post - lists - create
*/
2018-04-07 20:15:35 +00:00
function api_lists_create ( $type )
2018-04-07 13:54:26 +00:00
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2018-04-07 13:54:26 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2019-10-13 02:01:34 +00:00
$name = $_REQUEST [ 'name' ] ? ? '' ;
2018-04-07 13:54:26 +00:00
$uid = $user_info [ 'uid' ];
$success = group_create ( $name , $uid );
if ( $success [ 'success' ]) {
$grp = [
'name' => $success [ 'name' ],
'id' => intval ( $success [ 'gid' ]),
'id_str' => ( string ) $success [ 'gid' ],
'user' => $user_info
];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " lists " , $type , [ 'lists' => $grp ]);
2018-04-07 13:54:26 +00:00
}
}
2021-11-12 18:52:01 +00:00
2018-04-07 20:15:35 +00:00
api_register_func ( 'api/lists/create' , 'api_lists_create' , true , API_METHOD_POST );
2017-11-10 05:00:50 +00:00
2017-12-24 02:20:50 +00:00
/**
* Update the specified group with the posted array of contacts .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-12-24 02:20:50 +00:00
*/
2017-11-10 05:00:50 +00:00
function api_friendica_group_update ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2017-11-10 05:00:50 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
$uid = $user_info [ 'uid' ];
2019-10-13 02:01:34 +00:00
$gid = $_REQUEST [ 'gid' ] ? ? 0 ;
$name = $_REQUEST [ 'name' ] ? ? '' ;
2017-11-10 05:00:50 +00:00
$json = json_decode ( $_POST [ 'json' ], true );
$users = $json [ 'user' ];
// error if no name specified
2017-12-23 23:27:45 +00:00
if ( $name == " " ) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( 'group name not specified' );
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
// error if no gid specified
2017-12-23 23:27:45 +00:00
if ( $gid == " " ) {
2017-11-10 05:00:50 +00:00
throw new BadRequestException ( 'gid not specified' );
2017-12-23 23:27:45 +00:00
}
2017-11-10 05:00:50 +00:00
// remove members
2020-08-04 18:22:19 +00:00
$members = Contact\Group :: getById ( $gid );
2017-11-10 05:00:50 +00:00
foreach ( $members as $member ) {
$cid = $member [ 'id' ];
2016-09-25 16:50:08 +00:00
foreach ( $users as $user ) {
2017-11-10 05:00:50 +00:00
$found = ( $user [ 'cid' ] == $cid ? true : false );
}
2018-04-09 19:34:53 +00:00
if ( ! isset ( $found ) || ! $found ) {
2021-11-07 14:00:47 +00:00
$gid = Group :: getIdByName ( $uid , $name );
Group :: removeMember ( $gid , $cid );
2016-09-25 16:50:08 +00:00
}
}
2017-11-10 05:00:50 +00:00
// add members
$erroraddinguser = false ;
2018-01-15 13:05:12 +00:00
$errorusers = [];
2017-11-10 05:00:50 +00:00
foreach ( $users as $user ) {
$cid = $user [ 'cid' ];
2016-09-25 16:50:08 +00:00
2021-10-03 15:02:20 +00:00
if ( DBA :: exists ( 'contact' , [ 'id' => $cid , 'uid' => $uid ])) {
2018-01-04 01:54:35 +00:00
Group :: addMember ( $gid , $cid );
2016-09-25 16:50:08 +00:00
} else {
2017-11-10 05:00:50 +00:00
$erroraddinguser = true ;
$errorusers [] = $cid ;
2016-09-25 16:50:08 +00:00
}
}
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
// return success message incl. missing users in array
$status = ( $erroraddinguser ? " missing user " : " ok " );
2018-01-15 13:05:12 +00:00
$success = [ 'success' => true , 'gid' => $gid , 'name' => $name , 'status' => $status , 'wrong users' => $errorusers ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " group_update " , $type , [ 'result' => $success ]);
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/friendica/group_update' , 'api_friendica_group_update' , true , API_METHOD_POST );
2016-09-25 16:50:08 +00:00
2018-04-07 13:54:26 +00:00
/**
* Update information about a group .
*
* @ param string $type Return type ( atom , rss , xml , json )
*
* @ return array | string
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2018-04-07 13:54:26 +00:00
* @ see https :// developer . twitter . com / en / docs / accounts - and - users / create - manage - lists / api - reference / post - lists - update
*/
function api_lists_update ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2018-04-07 13:54:26 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2019-10-13 02:01:34 +00:00
$gid = $_REQUEST [ 'list_id' ] ? ? 0 ;
$name = $_REQUEST [ 'name' ] ? ? '' ;
2018-04-07 13:54:26 +00:00
$uid = $user_info [ 'uid' ];
// error if no gid specified
if ( $gid == 0 ) {
throw new BadRequestException ( 'gid not specified' );
}
// get data of the specified group id
2018-07-20 12:19:26 +00:00
$group = DBA :: selectFirst ( 'group' , [], [ 'uid' => $uid , 'id' => $gid ]);
2018-04-07 13:54:26 +00:00
// error message if specified gid is not in database
2018-04-07 17:55:41 +00:00
if ( ! $group ) {
2018-04-07 13:54:26 +00:00
throw new BadRequestException ( 'gid not available' );
}
if ( Group :: update ( $gid , $name )) {
2018-04-07 17:55:41 +00:00
$list = [
2018-04-07 13:54:26 +00:00
'name' => $name ,
'id' => intval ( $gid ),
'id_str' => ( string ) $gid ,
'user' => $user_info
];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " lists " , $type , [ 'lists' => $list ]);
2018-04-07 13:54:26 +00:00
}
}
api_register_func ( 'api/lists/update' , 'api_lists_update' , true , API_METHOD_POST );
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* Set notification as seen and returns associated item ( if possible )
2017-11-10 05:00:50 +00:00
*
2020-01-19 06:05:23 +00:00
* POST request with 'id' param as notification id
2017-12-24 02:20:50 +00:00
*
2017-11-10 05:00:50 +00:00
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
2018-04-09 17:34:02 +00:00
* @ return string | array
2019-01-07 17:24:01 +00:00
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-11-10 05:00:50 +00:00
*/
function api_friendica_notification_seen ( $type )
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_WRITE );
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2016-09-25 16:50:08 +00:00
2021-07-25 14:27:13 +00:00
if ( DI :: args () -> getArgc () !== 4 ) {
2021-09-18 05:08:29 +00:00
throw new BadRequestException ( 'Invalid argument count' );
2017-12-23 23:27:45 +00:00
}
2016-09-25 16:50:08 +00:00
2021-09-18 05:08:29 +00:00
$id = intval ( $_REQUEST [ 'id' ] ? ? 0 );
2016-09-25 16:50:08 +00:00
2020-01-25 01:01:49 +00:00
try {
2021-09-18 05:08:29 +00:00
$Notify = DI :: notify () -> selectOneById ( $id );
2021-11-17 22:44:52 +00:00
if ( $Notify -> uid !== BaseApi :: getCurrentUserID ()) {
2021-09-18 05:08:29 +00:00
throw new NotFoundException ();
}
2020-01-25 01:01:49 +00:00
2021-09-18 05:08:29 +00:00
if ( $Notify -> uriId ) {
2021-09-18 04:03:32 +00:00
DI :: notification () -> setAllSeenForUser ( $Notify -> uid , [ 'target-uri-id' => $Notify -> uriId ]);
2021-09-18 05:08:29 +00:00
}
$Notify -> setSeen ();
DI :: notify () -> save ( $Notify );
2020-01-25 01:01:49 +00:00
2021-09-18 05:08:29 +00:00
if ( $Notify -> otype === Notification\ObjectType :: ITEM ) {
2021-11-17 22:44:52 +00:00
$item = Post :: selectFirstForUser ( BaseApi :: getCurrentUserID (), [], [ 'id' => $Notify -> iid , 'uid' => BaseApi :: getCurrentUserID ()]);
2020-01-25 01:01:49 +00:00
if ( DBA :: isResult ( $item )) {
// we found the item, return it to the user
$ret = api_format_items ([ $item ], $user_info , false , $type );
$data = [ 'status' => $ret ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'status' , $type , $data );
2020-01-25 01:01:49 +00:00
}
// the item can't be found, but we set the notification as seen, so we count this as a success
2016-09-25 16:50:08 +00:00
}
2021-09-18 05:08:29 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( 'result' , $type , [ 'result' => 'success' ]);
2020-01-25 01:01:49 +00:00
} catch ( NotFoundException $e ) {
2020-01-31 21:25:09 +00:00
throw new BadRequestException ( 'Invalid argument' , $e );
2020-01-31 20:34:12 +00:00
} catch ( Exception $e ) {
2020-01-31 21:25:09 +00:00
throw new InternalServerErrorException ( 'Internal Server exception' , $e );
2017-11-10 05:00:50 +00:00
}
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/friendica/notification/seen' , 'api_friendica_notification_seen' , true , API_METHOD_POST );
2016-09-25 16:50:08 +00:00
2017-11-10 05:00:50 +00:00
/**
2020-01-19 06:05:23 +00:00
* search for direct_messages containing a searchstring through api
2017-11-10 05:00:50 +00:00
*
2019-01-07 17:24:01 +00:00
* @ param string $type Known types are 'atom' , 'rss' , 'xml' and 'json'
2018-02-11 21:13:29 +00:00
* @ param string $box
2018-04-09 17:34:02 +00:00
* @ return string | array ( success : success = true if found and search_result contains found messages ,
2017-11-10 05:00:50 +00:00
* success = false if nothing was found , search_result = 'nothing found' ,
2019-01-07 17:24:01 +00:00
* error : result = error with error message )
* @ throws BadRequestException
* @ throws ForbiddenException
* @ throws ImagickException
* @ throws InternalServerErrorException
* @ throws UnauthorizedException
2017-11-10 05:00:50 +00:00
*/
2018-02-11 21:13:29 +00:00
function api_friendica_direct_messages_search ( $type , $box = " " )
2017-11-10 05:00:50 +00:00
{
2021-11-17 21:28:51 +00:00
BaseApi :: checkAllowedScope ( BaseApi :: SCOPE_READ );
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
// params
2021-11-17 22:44:52 +00:00
$user_info = DI :: twitterUser () -> createFromUserId ( BaseApi :: getCurrentUserID ()) -> toArray ();
2019-10-13 02:01:34 +00:00
$searchstring = $_REQUEST [ 'searchstring' ] ? ? '' ;
2017-11-10 05:00:50 +00:00
$uid = $user_info [ 'uid' ];
2017-04-05 20:07:55 +00:00
2017-11-10 05:00:50 +00:00
// error if no searchstring specified
if ( $searchstring == " " ) {
2018-01-15 13:05:12 +00:00
$answer = [ 'result' => 'error' , 'message' => 'searchstring not specified' ];
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct_messages_search " , $type , [ '$result' => $answer ]);
2017-11-10 05:00:50 +00:00
}
// get data for the specified searchstring
2021-10-09 21:16:15 +00:00
$r = DBA :: toArray ( DBA :: p (
" SELECT `mail`.*, `contact`.`nurl` AS `contact-url` FROM `mail`,`contact` WHERE `mail`.`contact-id` = `contact`.`id` AND `mail`.`uid` = ? AND `body` LIKE ? ORDER BY `mail`.`id` DESC " ,
$uid ,
'%' . $searchstring . '%'
));
2017-11-10 05:00:50 +00:00
$profile_url = $user_info [ " url " ];
// message if nothing was found
2018-07-21 12:46:04 +00:00
if ( ! DBA :: isResult ( $r )) {
2018-01-15 13:05:12 +00:00
$success = [ 'success' => false , 'search_results' => 'problem with query' ];
2017-11-10 05:00:50 +00:00
} elseif ( count ( $r ) == 0 ) {
2018-01-15 13:05:12 +00:00
$success = [ 'success' => false , 'search_results' => 'nothing found' ];
2017-11-10 05:00:50 +00:00
} else {
2018-01-15 13:05:12 +00:00
$ret = [];
2017-11-10 05:00:50 +00:00
foreach ( $r as $item ) {
if ( $box == " inbox " || $item [ 'from-url' ] != $profile_url ) {
$recipient = $user_info ;
2021-11-17 22:44:52 +00:00
$sender = DI :: twitterUser () -> createFromContactId ( $item [ 'contact-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
} elseif ( $box == " sentbox " || $item [ 'from-url' ] == $profile_url ) {
2021-11-17 22:44:52 +00:00
$recipient = DI :: twitterUser () -> createFromContactId ( $item [ 'contact-id' ], BaseApi :: getCurrentUserID ()) -> toArray ();
2017-11-10 05:00:50 +00:00
$sender = $user_info ;
2016-09-25 16:50:08 +00:00
}
2018-04-09 19:34:53 +00:00
if ( isset ( $recipient ) && isset ( $sender )) {
$ret [] = api_format_messages ( $item , $recipient , $sender );
}
2017-11-10 05:00:50 +00:00
}
2018-01-15 13:05:12 +00:00
$success = [ 'success' => true , 'search_results' => $ret ];
2016-09-25 16:50:08 +00:00
}
2017-04-05 20:07:55 +00:00
2021-11-12 18:59:16 +00:00
return DI :: apiResponse () -> formatData ( " direct_message_search " , $type , [ '$result' => $success ]);
2017-11-10 05:00:50 +00:00
}
2016-09-25 16:50:08 +00:00
2017-12-25 20:12:08 +00:00
/// @TODO move to top of file or somewhere better
2017-11-10 05:00:50 +00:00
api_register_func ( 'api/friendica/direct_messages_search' , 'api_friendica_direct_messages_search' , true );
2016-09-25 16:50:08 +00:00
2018-12-21 16:00:56 +00:00
/*
2020-01-19 06:05:23 +00:00
* Number of comments
2018-12-21 16:00:56 +00:00
*
2020-01-19 06:05:23 +00:00
* Bind comment numbers ( friendica_comments : Int ) on each statuses page of * _timeline / favorites / search
2018-12-21 16:00:56 +00:00
*
* @ param object $data [ Status , Status ]
*
* @ return void
*/
2021-04-26 06:50:12 +00:00
function bindComments ( & $data )
2018-12-21 17:42:39 +00:00
{
if ( count ( $data ) == 0 ) {
return ;
}
2021-04-26 06:50:12 +00:00
2018-12-21 16:00:56 +00:00
$ids = [];
$comments = [];
2018-12-21 17:42:39 +00:00
foreach ( $data as $item ) {
$ids [] = $item [ 'id' ];
}
2018-12-21 16:00:56 +00:00
2018-12-22 03:39:16 +00:00
$idStr = DBA :: escape ( implode ( ', ' , $ids ));
2021-02-22 19:47:08 +00:00
$sql = " SELECT `parent`, COUNT(*) as comments FROM `post-user-view` WHERE `parent` IN ( $idStr ) AND `deleted` = ? AND `gravity`= ? GROUP BY `parent` " ;
2018-12-21 17:42:39 +00:00
$items = DBA :: p ( $sql , 0 , GRAVITY_COMMENT );
$itemsData = DBA :: toArray ( $items );
2018-12-21 16:00:56 +00:00
2018-12-21 17:42:39 +00:00
foreach ( $itemsData as $item ) {
$comments [ $item [ 'parent' ]] = $item [ 'comments' ];
2018-12-21 16:00:56 +00:00
}
2018-12-21 17:42:39 +00:00
foreach ( $data as $idx => $item ) {
2018-12-21 16:00:56 +00:00
$id = $item [ 'id' ];
$data [ $idx ][ 'friendica_comments' ] = isset ( $comments [ $id ]) ? $comments [ $id ] : 0 ;
}
}
2016-09-25 16:50:08 +00:00
/*
2017-04-05 20:07:55 +00:00
@ TODO Maybe open to implement ?
2016-09-25 16:50:08 +00:00
To . Do :
2017-12-23 23:27:45 +00:00
[ pagename ] => api / 1.1 / statuses / lookup . json
[ id ] => 605138389168451584
[ include_cards ] => true
[ cards_platform ] => Android - 12
[ include_entities ] => true
[ include_my_retweet ] => 1
[ include_rts ] => 1
[ include_reply_count ] => true
[ include_descendent_reply_count ] => true
2016-09-25 16:50:08 +00:00
( ? )
Not implemented by now :
statuses / retweets_of_me
friendships / create
friendships / destroy
friendships / exists
friendships / show
account / update_location
account / update_profile_background_image
blocks / create
blocks / destroy
friendica / profile / update
friendica / profile / create
friendica / profile / delete
Not implemented in status . net :
statuses / retweeted_to_me
statuses / retweeted_by_me
direct_messages / destroy
account / end_session
account / update_delivery_device
notifications / follow
notifications / leave
blocks / exists
blocks / blocking
lists
*/