Merge pull request #12854 from annando/api-edit
Adding and removing of pictures via API is now possible
This commit is contained in:
commit
a41e2eb50b
5 changed files with 188 additions and 68 deletions
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
*
|
||||
|
|
|
@ -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']]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue