Merge pull request #12854 from annando/api-edit

Adding and removing of pictures via API is now possible
This commit is contained in:
Hypolite Petovan 2023-03-15 06:59:44 -04:00 committed by GitHub
commit a41e2eb50b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 188 additions and 68 deletions

View file

@ -51,41 +51,64 @@ class Attachment extends BaseFactory
{
$attachments = [];
foreach (Post\Media::getByURIId($uriId, [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE]) as $attachment) {
$filetype = !empty($attachment['mimetype']) ? strtolower(substr($attachment['mimetype'], 0, strpos($attachment['mimetype'], '/'))) : '';
if (($filetype == 'audio') || ($attachment['type'] == Post\Media::AUDIO)) {
$type = 'audio';
} elseif (($filetype == 'video') || ($attachment['type'] == Post\Media::VIDEO)) {
$type = 'video';
} elseif ($attachment['mimetype'] == 'image/gif') {
$type = 'gifv';
} elseif (($filetype == 'image') || ($attachment['type'] == Post\Media::IMAGE)) {
$type = 'image';
} else {
$type = 'unknown';
}
$remote = $attachment['url'];
if ($type == 'image') {
$url = Post\Media::getPreviewUrlForId($attachment['id']);
$preview = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_SMALL);
} else {
$url = $attachment['url'];
if (!empty($attachment['preview'])) {
$preview = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_SMALL);
} else {
$preview = '';
}
}
$object = new \Friendica\Object\Api\Mastodon\Attachment($attachment, $type, $url, $preview, $remote);
$attachments[] = $object->toArray();
$attachments[] = $this->createFromMediaArray($attachment);
}
return $attachments;
}
/**
* @param int $id id of the media
* @return \Friendica\Object\Api\Mastodon\Attachment
* @throws HTTPException\InternalServerErrorException
*/
public function createFromId(int $id): \Friendica\Object\Api\Mastodon\Attachment
{
$attachment = Post\Media::getById($id);
if (empty($attachment)) {
return [];
}
return $this->createFromMediaArray($attachment);
}
/**
* @param array $attachment
* @return \Friendica\Object\Api\Mastodon\Attachment
* @throws HTTPException\InternalServerErrorException
*/
private function createFromMediaArray(array $attachment): \Friendica\Object\Api\Mastodon\Attachment
{
$filetype = !empty($attachment['mimetype']) ? strtolower(substr($attachment['mimetype'], 0, strpos($attachment['mimetype'], '/'))) : '';
if (($filetype == 'audio') || ($attachment['type'] == Post\Media::AUDIO)) {
$type = 'audio';
} elseif (($filetype == 'video') || ($attachment['type'] == Post\Media::VIDEO)) {
$type = 'video';
} elseif ($attachment['mimetype'] == 'image/gif') {
$type = 'gifv';
} elseif (($filetype == 'image') || ($attachment['type'] == Post\Media::IMAGE)) {
$type = 'image';
} else {
$type = 'unknown';
}
$remote = $attachment['url'];
if ($type == 'image') {
$url = Post\Media::getPreviewUrlForId($attachment['id']);
$preview = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_SMALL);
} else {
$url = $attachment['url'];
if (!empty($attachment['preview'])) {
$preview = Post\Media::getPreviewUrlForId($attachment['id'], Proxy::SIZE_SMALL);
} else {
$preview = '';
}
}
return new \Friendica\Object\Api\Mastodon\Attachment($attachment, $type, $url, $preview, $remote);
}
/**
* @param int $id id of the photo
*

View file

@ -41,7 +41,7 @@ class StatusSource extends BaseFactory
$post = Post::selectFirst(['uri-id', 'raw-body', 'body', 'title'], ['uri-id' => $uriId, 'uid' => [0, $uid]]);
$spoiler_text = $post['title'] ?: BBCode::toPlaintext(BBCode::getAbstract($post['body'], Protocol::ACTIVITYPUB));
$body = BBCode::toMarkdown($post['body']);
$body = BBCode::toMarkdown(Post\Media::removeFromEndOfBody($post['body']));
return new \Friendica\Object\Api\Mastodon\StatusSource($post['uri-id'], $body, $spoiler_text);
}

View file

@ -731,6 +731,30 @@ class Media
return DBA::selectToArray('post-media', [], $condition, ['order' => ['id']]);
}
/**
* Retrieves the media attachment with the provided media id.
*
* @param int $id id
* @return array|bool Array on success, false on error
* @throws \Exception
*/
public static function getById(int $id)
{
return DBA::selectFirst('post-media', [], ['id' => $id]);
}
/**
* Update post-media entries
*
* @param array $fields
* @param int $id
* @return bool
*/
public static function updateById(array $fields, int $id): bool
{
return DBA::update('post-media', $fields, ['id' => $id]);
}
/**
* Checks if media attachments are associated with the provided item ID.
*
@ -755,7 +779,7 @@ class Media
*
* @param int $uri_id URI id
* @param array $types Media types
* @return bool Whether media attachment exists
* @return bool result of deletion
* @throws \Exception
*/
public static function deleteByURIId(int $uri_id, array $types = []): bool
@ -769,6 +793,18 @@ class Media
return DBA::delete('post-media', $condition);
}
/**
* Delete media by id
*
* @param int $id media id
* @return bool result of deletion
* @throws \Exception
*/
public static function deleteById(int $id): bool
{
return DBA::delete('post-media', ['id' => $id]);
}
/**
* Split the attachment media in the three segments "visual", "link" and "additional"
*

View file

@ -25,6 +25,7 @@ use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Model\Photo;
use Friendica\Model\Post;
use Friendica\Module\BaseApi;
/**
@ -71,7 +72,15 @@ class Media extends BaseApi
$photo = Photo::selectFirst(['resource-id'], ['id' => $this->parameters['id'], 'uid' => $uid]);
if (empty($photo['resource-id'])) {
DI::mstdnError()->RecordNotFound();
$media = Post\Media::getById($this->parameters['id']);
if (empty($media['uri-id'])) {
DI::mstdnError()->RecordNotFound();
}
if (!Post::exists(['uri-id' => $media['uri-id'], 'uid' => $uid, 'origin' => true])) {
DI::mstdnError()->RecordNotFound();
}
Post\Media::updateById(['description' => $request['description']], $this->parameters['id']);
System::jsonExit(DI::mstdnAttachment()->createFromId($this->parameters['id']));
}
Photo::update(['desc' => $request['description']], ['resource-id' => $photo['resource-id']]);

View file

@ -53,6 +53,8 @@ class Statuses extends BaseApi
$request = $this->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.
'in_reply_to_id' => 0, // ID of the status being replied to, if status is a reply
'spoiler_text' => '', // Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field.
'language' => '', // ISO 639 language code for this status.
'friendica' => [],
@ -68,7 +70,7 @@ class Statuses extends BaseApi
'origin' => true,
];
$post = Post::selectFirst(['uri-id', 'id', 'gravity'], $condition);
$post = Post::selectFirst(['uri-id', 'id', 'gravity', 'uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'], $condition);
if (empty($post['id'])) {
throw new HTTPException\NotFoundException('Item with URI ID ' . $this->parameters['id'] . ' not found for user ' . $uid . '.');
}
@ -95,6 +97,43 @@ class Statuses extends BaseApi
}
}
if (!empty($request['media_ids'])) {
/*
The provided ids in the request value consists of these two sources:
- The id in the "photo" table for newly uploaded media
- The id in the "post-media" table for already attached media
Because of this we have to add all media that isn't already attached.
Also we have to delete all media that isn't provided anymore.
There is a possible situation where the newly uploaded media
could have the same id as an existing, but deleted media.
We can't do anything about this, but the probability for this is extremely low.
*/
$media_ids = [];
$existing_media = array_column(Post\Media::getByURIId($post['uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE]), 'id');
foreach ($request['media_ids'] as $media) {
if (!in_array($media, $existing_media)) {
$media_ids[] = $media;
}
}
foreach ($existing_media as $media) {
if (!in_array($media, $request['media_ids'])) {
Post\Media::deleteById($media);
}
}
$item = $this->storeMediaIds($media_ids, array_merge($post, $item));
foreach ($item['attachments'] as $attachment) {
$attachment['uri-id'] = $post['uri-id'];
Post\Media::insert($attachment);
}
unset($item['attachments']);
}
if (!Item::isValid($item)) {
throw new \Exception('Missing parameters in definitien');
}
@ -240,40 +279,7 @@ class Statuses extends BaseApi
$item = DI::contentItem()->expandTags($item, $request['visibility'] == 'direct');
if (!empty($request['media_ids'])) {
$item['object-type'] = Activity\ObjectType::IMAGE;
$item['post-type'] = Item::PT_IMAGE;
$item['attachments'] = [];
foreach ($request['media_ids'] as $id) {
$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));
if (empty($media)) {
continue;
}
Photo::setPermissionForRessource($media[0]['resource-id'], $uid, $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
$ressources[] = $media[0]['resource-id'];
$phototypes = Images::supportedTypes();
$ext = $phototypes[$media[0]['type']];
$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']];
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;
}
$item = $this->storeMediaIds($request['media_ids'], $item);
}
if (!empty($request['scheduled_at'])) {
@ -340,4 +346,50 @@ class Statuses extends BaseApi
return 'API';
}
}
/**
* Store provided media ids in the item array and adjust permissions
*
* @param array $media_ids
* @param array $item
* @return array
*/
private function storeMediaIds(array $media_ids, array $item): array
{
$item['object-type'] = Activity\ObjectType::IMAGE;
$item['post-type'] = Item::PT_IMAGE;
$item['attachments'] = [];
foreach ($media_ids as $id) {
$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, $item['uid']));
if (empty($media)) {
continue;
}
Photo::setPermissionForRessource($media[0]['resource-id'], $item['uid'], $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
$ressources[] = $media[0]['resource-id'];
$phototypes = Images::supportedTypes();
$ext = $phototypes[$media[0]['type']];
$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']];
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;
}
return $item;
}
}