2021-05-08 05:55:06 +00:00
< ? php
/**
* @ copyright Copyright ( C ) 2010 - 2021 , the Friendica project
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
*/
namespace Friendica\Module\Api\Mastodon ;
2021-05-15 22:40:57 +00:00
use Friendica\Content\Text\BBCode ;
use Friendica\Content\Text\Markdown ;
2021-05-08 05:55:06 +00:00
use Friendica\Core\System ;
2021-05-15 22:40:57 +00:00
use Friendica\Database\DBA ;
2021-05-08 05:55:06 +00:00
use Friendica\DI ;
2021-05-15 22:40:57 +00:00
use Friendica\Model\Contact ;
use Friendica\Model\Group ;
2021-05-15 10:08:47 +00:00
use Friendica\Model\Item ;
2021-05-16 14:30:15 +00:00
use Friendica\Model\Photo ;
2021-05-15 10:08:47 +00:00
use Friendica\Model\Post ;
2021-05-15 22:40:57 +00:00
use Friendica\Model\User ;
2021-05-08 05:55:06 +00:00
use Friendica\Module\BaseApi ;
2021-05-15 22:40:57 +00:00
use Friendica\Protocol\Activity ;
use Friendica\Util\Images ;
2021-05-08 05:55:06 +00:00
/**
* @ see https :// docs . joinmastodon . org / methods / statuses /
*/
class Statuses extends BaseApi
{
2021-05-13 21:15:32 +00:00
public static function post ( array $parameters = [])
{
2021-06-08 12:00:22 +00:00
self :: checkAllowedScope ( self :: SCOPE_WRITE );
2021-05-15 22:40:57 +00:00
$uid = self :: getCurrentUserID ();
2021-05-28 06:10:32 +00:00
$request = self :: getRequest ([
'status' => '' , // Text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.
'media_ids' => [], // Array of Attachment ids to be attached as media. If provided, status becomes optional, and poll cannot be used.
'poll' => [], // Poll data. If provided, media_ids cannot be used, and poll[expires_in] must be provided.
'in_reply_to_id' => 0 , // ID of the status being replied to, if status is a reply
'sensitive' => false , // Mark status and attached media as sensitive?
'spoiler_text' => '' , // Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field.
2021-05-29 12:32:31 +00:00
'visibility' => '' , // Visibility of the posted status. One of: "public", "unlisted", "private" or "direct".
2021-05-28 06:10:32 +00:00
'scheduled_at' => '' , // ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future.
'language' => '' , // ISO 639 language code for this status.
]);
2021-05-15 22:40:57 +00:00
$owner = User :: getOwnerDataById ( $uid );
// The imput is defined as text. So we can use Markdown for some enhancements
2021-05-28 06:10:32 +00:00
$body = Markdown :: toBBCode ( $request [ 'status' ]);
2021-05-15 22:40:57 +00:00
$body = BBCode :: expandTags ( $body );
$item = [];
$item [ 'uid' ] = $uid ;
$item [ 'verb' ] = Activity :: POST ;
$item [ 'contact-id' ] = $owner [ 'id' ];
$item [ 'author-id' ] = $item [ 'owner-id' ] = Contact :: getPublicIdByUserId ( $uid );
2021-05-28 06:10:32 +00:00
$item [ 'title' ] = $request [ 'spoiler_text' ];
2021-05-15 22:40:57 +00:00
$item [ 'body' ] = $body ;
if ( ! empty ( self :: getCurrentApplication ()[ 'name' ])) {
$item [ 'app' ] = self :: getCurrentApplication ()[ 'name' ];
}
if ( empty ( $item [ 'app' ])) {
$item [ 'app' ] = 'API' ;
}
2021-05-28 06:10:32 +00:00
switch ( $request [ 'visibility' ]) {
2021-05-15 22:40:57 +00:00
case 'public' :
$item [ 'allow_cid' ] = '' ;
$item [ 'allow_gid' ] = '' ;
$item [ 'deny_cid' ] = '' ;
$item [ 'deny_gid' ] = '' ;
$item [ 'private' ] = Item :: PUBLIC ;
break ;
case 'unlisted' :
$item [ 'allow_cid' ] = '' ;
$item [ 'allow_gid' ] = '' ;
$item [ 'deny_cid' ] = '' ;
$item [ 'deny_gid' ] = '' ;
$item [ 'private' ] = Item :: UNLISTED ;
break ;
case 'private' :
if ( ! empty ( $owner [ 'allow_cid' ] . $owner [ 'allow_gid' ] . $owner [ 'deny_cid' ] . $owner [ 'deny_gid' ])) {
$item [ 'allow_cid' ] = $owner [ 'allow_cid' ];
$item [ 'allow_gid' ] = $owner [ 'allow_gid' ];
$item [ 'deny_cid' ] = $owner [ 'deny_cid' ];
$item [ 'deny_gid' ] = $owner [ 'deny_gid' ];
} else {
$item [ 'allow_cid' ] = '' ;
$item [ 'allow_gid' ] = [ Group :: FOLLOWERS ];
$item [ 'deny_cid' ] = '' ;
$item [ 'deny_gid' ] = '' ;
}
$item [ 'private' ] = Item :: PRIVATE ;
break ;
case 'direct' :
// Direct messages are currently unsupported
DI :: mstdnError () -> InternalError ( 'Direct messages are currently unsupported' );
2021-05-28 06:10:32 +00:00
break ;
2021-05-15 22:40:57 +00:00
default :
$item [ 'allow_cid' ] = $owner [ 'allow_cid' ];
$item [ 'allow_gid' ] = $owner [ 'allow_gid' ];
$item [ 'deny_cid' ] = $owner [ 'deny_cid' ];
$item [ 'deny_gid' ] = $owner [ 'deny_gid' ];
if ( ! empty ( $item [ 'allow_cid' ] . $item [ 'allow_gid' ] . $item [ 'deny_cid' ] . $item [ 'deny_gid' ])) {
$item [ 'private' ] = Item :: PRIVATE ;
} elseif ( DI :: pConfig () -> get ( $uid , 'system' , 'unlisted' )) {
$item [ 'private' ] = Item :: UNLISTED ;
} else {
$item [ 'private' ] = Item :: PUBLIC ;
}
break ;
}
2021-05-28 06:10:32 +00:00
if ( ! empty ( $request [ 'language' ])) {
$item [ 'language' ] = json_encode ([ $request [ 'language' ] => 1 ]);
2021-05-15 22:40:57 +00:00
}
2021-05-28 06:10:32 +00:00
if ( $request [ 'in_reply_to_id' ]) {
$parent = Post :: selectFirst ([ 'uri' ], [ 'uri-id' => $request [ 'in_reply_to_id' ], 'uid' => [ 0 , $uid ]]);
2021-05-15 22:40:57 +00:00
$item [ 'thr-parent' ] = $parent [ 'uri' ];
$item [ 'gravity' ] = GRAVITY_COMMENT ;
$item [ 'object-type' ] = Activity\ObjectType :: COMMENT ;
} else {
2021-07-08 13:47:46 +00:00
self :: checkThrottleLimit ();
2021-05-15 22:40:57 +00:00
$item [ 'gravity' ] = GRAVITY_PARENT ;
$item [ 'object-type' ] = Activity\ObjectType :: NOTE ;
}
2021-05-28 06:10:32 +00:00
if ( ! empty ( $request [ 'media_ids' ])) {
2021-05-15 22:40:57 +00:00
$item [ 'object-type' ] = Activity\ObjectType :: IMAGE ;
$item [ 'post-type' ] = Item :: PT_IMAGE ;
$item [ 'attachments' ] = [];
2021-05-28 06:10:32 +00:00
foreach ( $request [ 'media_ids' ] as $id ) {
2021-05-15 22:40:57 +00:00
$media = DBA :: toArray ( DBA :: p ( " SELECT `resource-id`, `scale`, `type`, `desc`, `filename`, `datasize`, `width`, `height` FROM `photo`
WHERE `resource-id` IN ( SELECT `resource-id` FROM `photo` WHERE `id` = ? ) AND `photo` . `uid` = ?
ORDER BY `photo` . `width` DESC LIMIT 2 " , $id , $uid ));
2021-05-28 06:10:32 +00:00
2021-05-15 22:40:57 +00:00
if ( empty ( $media )) {
continue ;
}
2021-05-16 14:30:15 +00:00
Photo :: setPermissionForRessource ( $media [ 0 ][ 'resource-id' ], $uid , $item [ 'allow_cid' ], $item [ 'allow_gid' ], $item [ 'deny_cid' ], $item [ 'deny_gid' ]);
2021-05-15 22:40:57 +00:00
$ressources [] = $media [ 0 ][ 'resource-id' ];
$phototypes = Images :: supportedTypes ();
$ext = $phototypes [ $media [ 0 ][ 'type' ]];
2021-05-28 06:10:32 +00:00
2021-05-15 22:40:57 +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-28 06:10:32 +00:00
2021-05-15 22:40:57 +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' ];
}
$item [ 'attachments' ][] = $attachment ;
}
}
$id = Item :: insert ( $item , true );
if ( ! empty ( $id )) {
$item = Post :: selectFirst ([ 'uri-id' ], [ 'id' => $id ]);
if ( ! empty ( $item [ 'uri-id' ])) {
2021-05-28 06:10:32 +00:00
System :: jsonExit ( DI :: mstdnStatus () -> createFromUriId ( $item [ 'uri-id' ], $uid ));
2021-05-15 22:40:57 +00:00
}
}
DI :: mstdnError () -> InternalError ();
2021-05-13 21:15:32 +00:00
}
2021-05-08 09:14:19 +00:00
public static function delete ( array $parameters = [])
{
2021-06-08 12:00:22 +00:00
self :: checkAllowedScope ( self :: SCOPE_READ );
2021-05-15 10:08:47 +00:00
$uid = self :: getCurrentUserID ();
if ( empty ( $parameters [ 'id' ])) {
DI :: mstdnError () -> UnprocessableEntity ();
}
$item = Post :: selectFirstForUser ( $uid , [ 'id' ], [ 'uri-id' => $parameters [ 'id' ], 'uid' => $uid ]);
if ( empty ( $item [ 'id' ])) {
DI :: mstdnError () -> RecordNotFound ();
}
if ( ! Item :: markForDeletionById ( $item [ 'id' ])) {
DI :: mstdnError () -> RecordNotFound ();
}
System :: jsonExit ([]);
2021-05-08 09:14:19 +00:00
}
2021-05-08 05:55:06 +00:00
/**
* @ param array $parameters
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function rawContent ( array $parameters = [])
{
2021-06-08 08:28:14 +00:00
$uid = self :: getCurrentUserID ();
2021-05-08 05:55:06 +00:00
if ( empty ( $parameters [ 'id' ])) {
2021-05-12 14:00:15 +00:00
DI :: mstdnError () -> UnprocessableEntity ();
2021-05-08 05:55:06 +00:00
}
2021-06-08 08:28:14 +00:00
System :: jsonExit ( DI :: mstdnStatus () -> createFromUriId ( $parameters [ 'id' ], $uid ));
2021-05-08 05:55:06 +00:00
}
}