-/**
- * upload a new photo or change an existing photo
- *
- * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
- * @return string|array
- * @throws BadRequestException
- * @throws ForbiddenException
- * @throws ImagickException
- * @throws InternalServerErrorException
- * @throws NotFoundException
- */
-function api_fr_photo_create_update($type)
-{
- if (api_user() === false) {
- throw new ForbiddenException();
- }
- // input params
- $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;
- $visibility = !$allow_cid && !$deny_cid && !$allow_gid && !$deny_gid;
-
- // 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";
-
- // error if no media posted in create-mode
- if (empty($_FILES['media'])) {
- // Output error
- throw new BadRequestException("no media data submitted");
- }
-
- // album_new will be ignored in create-mode
- $album_new = "";
- } else {
- $mode = "update";
-
- // check if photo is existing in databasei
- if (!Photo::exists(['resource-id' => $photo_id, 'uid' => api_user(), 'album' => $album])) {
- throw new BadRequestException("photo not available");
- }
- }
-
- // 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");
- }
- // now let's upload the new media in create-mode
- if ($mode == "create") {
- $media = $_FILES['media'];
- $data = save_media_to_database("photo", $media, $type, $album, trim($allow_cid), trim($deny_cid), trim($allow_gid), trim($deny_gid), $desc, $visibility);
-
- // return success of updating or error message
- if (!is_null($data)) {
- return api_format_data("photo_create", $type, $data);
- } else {
- throw new InternalServerErrorException("unknown error - uploading photo failed, see Friendica log for more information");
- }
- }
-
- // now let's do the changes in update-mode
- if ($mode == "update") {
- $updated_fields = [];
-
- if (!is_null($desc)) {
- $updated_fields['desc'] = $desc;
- }
-
- if (!is_null($album_new)) {
- $updated_fields['album'] = $album_new;
- }
-
- if (!is_null($allow_cid)) {
- $allow_cid = trim($allow_cid);
- $updated_fields['allow_cid'] = $allow_cid;
- }
-
- if (!is_null($deny_cid)) {
- $deny_cid = trim($deny_cid);
- $updated_fields['deny_cid'] = $deny_cid;
- }
-
- if (!is_null($allow_gid)) {
- $allow_gid = trim($allow_gid);
- $updated_fields['allow_gid'] = $allow_gid;
- }
-
- if (!is_null($deny_gid)) {
- $deny_gid = trim($deny_gid);
- $updated_fields['deny_gid'] = $deny_gid;
- }
-
- $result = false;
- if (count($updated_fields) > 0) {
- $nothingtodo = false;
- $result = Photo::update($updated_fields, ['uid' => api_user(), 'resource-id' => $photo_id, 'album' => $album]);
- } else {
- $nothingtodo = true;
- }
-
- if (!empty($_FILES['media'])) {
- $nothingtodo = false;
- $media = $_FILES['media'];
- $data = save_media_to_database("photo", $media, $type, $album, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $desc, 0, $visibility, $photo_id);
- if (!is_null($data)) {
- return api_format_data("photo_update", $type, $data);
- }
- }
-
- // return success of updating or error message
- if ($result) {
- $answer = ['result' => 'updated', 'message' => 'Image id `' . $photo_id . '` has been updated.'];
- return api_format_data("photo_update", $type, ['$result' => $answer]);
- } else {
- if ($nothingtodo) {
- $answer = ['result' => 'cancelled', 'message' => 'Nothing to update for image id `' . $photo_id . '`.'];
- return api_format_data("photo_update", $type, ['$result' => $answer]);
- }
- throw new InternalServerErrorException("unknown error - update photo entry in database failed");
- }
- }
- throw new InternalServerErrorException("unknown error - this error on uploading or updating a photo should never happen");
-}
-
-/**
- * delete a single photo from the database through api
- *
- * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
- * @return string|array
- * @throws BadRequestException
- * @throws ForbiddenException
- * @throws InternalServerErrorException
- */
-function api_fr_photo_delete($type)
-{
- if (api_user() === false) {
- throw new ForbiddenException();
- }
-
- // input params
- $photo_id = $_REQUEST['photo_id'] ?? null;
-
- // do several checks on input parameters
- // we do not allow calls without photo id
- if ($photo_id == null) {
- throw new BadRequestException("no photo_id specified");
- }
-
- // check if photo is existing in database
- if (!Photo::exists(['resource-id' => $photo_id, 'uid' => api_user()])) {
- throw new BadRequestException("photo not available");
- }
-
- // now we can perform on the deletion of the photo
- $result = Photo::delete(['uid' => api_user(), 'resource-id' => $photo_id]);
-
- // return success of deletion or error message
- if ($result) {
- // function for setting the items to "deleted = 1" which ensures that comments, likes etc. are not shown anymore
- // to the user and the contacts of the users (drop_items() do all the necessary magic to avoid orphans in database and federate deletion)
- $condition = ['uid' => api_user(), 'resource-id' => $photo_id, 'type' => 'photo'];
- Item::deleteForUser($condition, api_user());
-
- $result = ['result' => 'deleted', 'message' => 'photo with id `' . $photo_id . '` has been deleted from server.'];
- return api_format_data("photo_delete", $type, ['$result' => $result]);
- } else {
- throw new InternalServerErrorException("unknown error on deleting photo from database table");
- }
-}
-
-
-/**
- * returns the details of a specified photo id, if scale is given, returns the photo data in base 64
- *
- * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
- * @return string|array
- * @throws BadRequestException
- * @throws ForbiddenException
- * @throws InternalServerErrorException
- * @throws NotFoundException
- */
-function api_fr_photo_detail($type)
-{
- if (api_user() === false) {
- throw new ForbiddenException();
- }
- if (empty($_REQUEST['photo_id'])) {
- throw new BadRequestException("No photo id.");
- }
-
- $scale = (!empty($_REQUEST['scale']) ? intval($_REQUEST['scale']) : false);
- $photo_id = $_REQUEST['photo_id'];
-
- // prepare json/xml output with data from database for the requested photo
- $data = prepare_photo_data($type, $scale, $photo_id);
-
- return api_format_data("photo_detail", $type, $data);
-}
-
-
-/**
- * updates the profile image for the user (either a specified profile or the default profile)
- *
- * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
- *
- * @return string|array
- * @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
- */
-function api_account_update_profile_image($type)
-{
- if (api_user() === false) {
- throw new ForbiddenException();
- }
- // input params
- $profile_id = $_REQUEST['profile_id'] ?? 0;
-
- // error if image data is missing
- if (empty($_FILES['image'])) {
- throw new BadRequestException("no media data submitted");
- }
-
- // check if specified profile id is valid
- if ($profile_id != 0) {
- $profile = DBA::selectFirst('profile', ['is-default'], ['uid' => api_user(), 'id' => $profile_id]);
- // error message if specified profile id is not in database
- if (!DBA::isResult($profile)) {
- throw new BadRequestException("profile_id not available");
- }
- $is_default_profile = $profile['is-default'];
- } else {
- $is_default_profile = 1;
- }
-
- // get mediadata from image or media (Twitter call api/account/update_profile_image provides image)
- $media = null;
- if (!empty($_FILES['image'])) {
- $media = $_FILES['image'];
- } elseif (!empty($_FILES['media'])) {
- $media = $_FILES['media'];
- }
- // save new profile image
- $data = save_media_to_database("profileimage", $media, $type, DI::l10n()->t('Profile Photos'), "", "", "", "", "", $is_default_profile);
-
- // get filetype
- if (is_array($media['type'])) {
- $filetype = $media['type'][0];
- } else {
- $filetype = $media['type'];
- }
- if ($filetype == "image/jpeg") {
- $fileext = "jpg";
- } elseif ($filetype == "image/png") {
- $fileext = "png";
- } else {
- throw new InternalServerErrorException('Unsupported filetype');
- }
-
- // change specified profile or all profiles to the new resource-id
- if ($is_default_profile) {
- $condition = ["`profile` AND `resource-id` != ? AND `uid` = ?", $data['photo']['id'], api_user()];
- Photo::update(['profile' => false], $condition);
- } else {
- $fields = ['photo' => DI::baseUrl() . '/photo/' . $data['photo']['id'] . '-4.' . $fileext,
- 'thumb' => DI::baseUrl() . '/photo/' . $data['photo']['id'] . '-5.' . $fileext];
- DBA::update('profile', $fields, ['id' => $_REQUEST['profile'], 'uid' => api_user()]);
- }
-
- Contact::updateSelfFromUserID(api_user(), true);
-
- // Update global directory in background
- Profile::publishUpdate(api_user());
-
- // output for client
- if ($data) {
- return api_account_verify_credentials($type);
- } else {
- // SaveMediaToDatabase failed for some reason
- throw new InternalServerErrorException("image upload failed");
- }
-}
-
-// place api-register for photoalbum calls before 'api/friendica/photo', otherwise this function is never reached
-api_register_func('api/friendica/photoalbum/delete', 'api_fr_photoalbum_delete', true, API_METHOD_DELETE);
-api_register_func('api/friendica/photoalbum/update', 'api_fr_photoalbum_update', true, API_METHOD_POST);
-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/delete', 'api_fr_photo_delete', true, API_METHOD_DELETE);
-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);
-
-/**
- * Update user profile
- *
- * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
- *
- * @return array|string
- * @throws BadRequestException
- * @throws ForbiddenException
- * @throws ImagickException
- * @throws InternalServerErrorException
- * @throws UnauthorizedException
- */
-function api_account_update_profile($type)
-{
- $local_user = api_user();
- $api_user = api_get_user(DI::app());
-
- if (!empty($_POST['name'])) {
- DBA::update('profile', ['name' => $_POST['name']], ['uid' => $local_user]);
- DBA::update('user', ['username' => $_POST['name']], ['uid' => $local_user]);
- Contact::update(['name' => $_POST['name']], ['uid' => $local_user, 'self' => 1]);
- Contact::update(['name' => $_POST['name']], ['id' => $api_user['id']]);
- }
-
- if (isset($_POST['description'])) {
- DBA::update('profile', ['about' => $_POST['description']], ['uid' => $local_user]);
- Contact::update(['about' => $_POST['description']], ['uid' => $local_user, 'self' => 1]);
- Contact::update(['about' => $_POST['description']], ['id' => $api_user['id']]);
- }
-
- Profile::publishUpdate($local_user);
-
- 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);
-
-/**
- *
- * @param string $acl_string
- * @return bool
- * @throws Exception
- */
-function check_acl_input($acl_string)
-{
- if (empty($acl_string)) {
- return false;
- }
-
- $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);
- $condition = ['id' => $cid, 'uid' => api_user()];
- $contact_not_found |= !DBA::exists('contact', $condition);
- }
- return $contact_not_found;
-}
-
-/**
- * @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
- * @param integer $profile
- * @param boolean $visibility
- * @param string $photo_id
- * @return array
- * @throws BadRequestException
- * @throws ForbiddenException
- * @throws ImagickException
- * @throws InternalServerErrorException
- * @throws NotFoundException
- * @throws UnauthorizedException
- */
-function save_media_to_database($mediatype, $media, $type, $album, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $desc, $profile = 0, $visibility = false, $photo_id = null)
-{
- $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'];
- }
- if (is_array($media['name'])) {
- $filename = basename($media['name'][0]);
- } else {
- $filename = basename($media['name']);
- }
- if (is_array($media['size'])) {
- $filesize = intval($media['size'][0]);
- } else {
- $filesize = intval($media['size']);
- }
- if (is_array($media['type'])) {
- $filetype = $media['type'][0];
- } else {
- $filetype = $media['type'];
- }
- }
-
- $filetype = Images::getMimeTypeBySource($src, $filename, $filetype);
-
- Logger::log(
- "File upload src: " . $src . " - filename: " . $filename .
- " - size: " . $filesize . " - type: " . $filetype,
- Logger::DEBUG
- );
-
- // 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
- $maximagesize = DI::config()->get('system', 'maximagesize');
- if ($maximagesize && ($filesize > $maximagesize)) {
- $formattedBytes = Strings::formatBytes($maximagesize);
- throw new InternalServerErrorException("image size exceeds Friendica config setting (uploaded size: $formattedBytes)");
- }
-
- // create Photo instance with the data of the image
- $imagedata = @file_get_contents($src);
- $Image = new Image($imagedata, $filetype);
- if (!$Image->isValid()) {
- throw new InternalServerErrorException("unable to process image data");
- }
-
- // check orientation of image
- $Image->orient($src);
- @unlink($src);
-
- // check max length of images on server
- $max_length = DI::config()->get('system', 'max_image_length');
- if (!$max_length) {
- $max_length = MAX_IMAGE_LENGTH;
- }
- if ($max_length > 0) {
- $Image->scaleDown($max_length);
- Logger::log("File upload: Scaling picture to new size " . $max_length, Logger::DEBUG);
- }
- $width = $Image->getWidth();
- $height = $Image->getHeight();
-
- // create a new resource-id if not already provided
- $resource_id = ($photo_id == null) ? Photo::newResource() : $photo_id;
-
- if ($mediatype == "photo") {
- // upload normal image (scales 0, 1, 2)
- Logger::log("photo upload: starting new photo upload", Logger::DEBUG);
-
- $r = Photo::store($Image, local_user(), $visitor, $resource_id, $filename, $album, 0, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (!$r) {
- Logger::log("photo upload: image upload with scale 0 (original size) failed");
- }
- if ($width > 640 || $height > 640) {
- $Image->scaleDown(640);
- $r = Photo::store($Image, local_user(), $visitor, $resource_id, $filename, $album, 1, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (!$r) {
- Logger::log("photo upload: image upload with scale 1 (640x640) failed");
- }
- }
-
- if ($width > 320 || $height > 320) {
- $Image->scaleDown(320);
- $r = Photo::store($Image, local_user(), $visitor, $resource_id, $filename, $album, 2, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (!$r) {
- Logger::log("photo upload: image upload with scale 2 (320x320) failed");
- }
- }
- Logger::log("photo upload: new photo upload ended", Logger::DEBUG);
- } elseif ($mediatype == "profileimage") {
- // upload profile image (scales 4, 5, 6)
- Logger::log("photo upload: starting new profile image upload", Logger::DEBUG);
-
- if ($width > 300 || $height > 300) {
- $Image->scaleDown(300);
- $r = Photo::store($Image, local_user(), $visitor, $resource_id, $filename, $album, 4, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (!$r) {
- Logger::log("photo upload: profile image upload with scale 4 (300x300) failed");
- }
- }
-
- if ($width > 80 || $height > 80) {
- $Image->scaleDown(80);
- $r = Photo::store($Image, local_user(), $visitor, $resource_id, $filename, $album, 5, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (!$r) {
- Logger::log("photo upload: profile image upload with scale 5 (80x80) failed");
- }
- }
-
- if ($width > 48 || $height > 48) {
- $Image->scaleDown(48);
- $r = Photo::store($Image, local_user(), $visitor, $resource_id, $filename, $album, 6, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (!$r) {
- Logger::log("photo upload: profile image upload with scale 6 (48x48) failed");
- }
- }
- $Image->__destruct();
- Logger::log("photo upload: new profile image upload ended", Logger::DEBUG);
- }
-
- if (!empty($r)) {
- // create entry in 'item'-table on new uploads to enable users to comment/like/dislike the photo
- if ($photo_id == null && $mediatype == "photo") {
- post_photo_item($resource_id, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $filetype, $visibility);
- }
- // on success return image data in json/xml format (like /api/friendica/photo does when no scale is given)
- return prepare_photo_data($type, false, $resource_id);
- } else {
- throw new InternalServerErrorException("image upload failed");
- }
-}
-
-/**
- *
- * @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
- * @throws InternalServerErrorException
- */
-function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $filetype, $visibility = false)
-{
- // get data about the api authenticated user
- $uri = Item::newURI(intval(api_user()));
- $owner_record = DBA::selectFirst('contact', [], ['uid' => api_user(), 'self' => true]);
-
- $arr = [];
- $arr['guid'] = System::createUUID();
- $arr['uid'] = intval(api_user());
- $arr['uri'] = $uri;
- $arr['type'] = 'photo';
- $arr['wall'] = 1;
- $arr['resource-id'] = $hash;
- $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'];
- $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;
-
- $typetoext = [
- 'image/jpeg' => 'jpg',
- 'image/png' => 'png',
- 'image/gif' => 'gif'
- ];
-
- // adds link to the thumbnail scale photo
- $arr['body'] = '[url=' . DI::baseUrl() . '/photos/' . $owner_record['nick'] . '/image/' . $hash . ']'
- . '[img]' . DI::baseUrl() . '/photo/' . $hash . '-' . "2" . '.'. $typetoext[$filetype] . '[/img]'
- . '[/url]';
-
- // do the magic for storing the item in the database and trigger the federation to other contacts
- Item::insert($arr);
-}
-
-/**
- *
- * @param string $type
- * @param int $scale
- * @param string $photo_id
- *
- * @return array
- * @throws BadRequestException
- * @throws ForbiddenException
- * @throws ImagickException
- * @throws InternalServerErrorException
- * @throws NotFoundException
- * @throws UnauthorizedException
- */
-function prepare_photo_data($type, $scale, $photo_id)
-{
- $a = DI::app();
- $user_info = api_get_user($a);
-
- if ($user_info === false) {
- throw new ForbiddenException();
- }
-
- $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
- $r = q(
- "SELECT %s `resource-id`, `created`, `edited`, `title`, `desc`, `album`, `filename`,
- `type`, `height`, `width`, `datasize`, `profile`, `allow_cid`, `deny_cid`, `allow_gid`, `deny_gid`,
- MIN(`scale`) AS `minscale`, MAX(`scale`) AS `maxscale`
- FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' %s GROUP BY
- `resource-id`, `created`, `edited`, `title`, `desc`, `album`, `filename`,
- `type`, `height`, `width`, `datasize`, `profile`, `allow_cid`, `deny_cid`, `allow_gid`, `deny_gid`",
- $data_sql,
- intval(local_user()),
- DBA::escape($photo_id),
- $scale_sql
- );
-
- $typetoext = [
- 'image/jpeg' => 'jpg',
- 'image/png' => 'png',
- 'image/gif' => 'gif'
- ];
-
- // prepare output data for photo
- if (DBA::isResult($r)) {
- $data = ['photo' => $r[0]];
- $data['photo']['id'] = $data['photo']['resource-id'];
- if ($scale !== false) {
- $data['photo']['data'] = base64_encode($data['photo']['data']);
- } else {
- unset($data['photo']['datasize']); //needed only with scale param
- }
- if ($type == "xml") {
- $data['photo']['links'] = [];
- for ($k = intval($data['photo']['minscale']); $k <= intval($data['photo']['maxscale']); $k++) {
- $data['photo']['links'][$k . ":link"]["@attributes"] = ["type" => $data['photo']['type'],
- "scale" => $k,
- "href" => DI::baseUrl() . "/photo/" . $data['photo']['resource-id'] . "-" . $k . "." . $typetoext[$data['photo']['type']]];
- }
- } else {
- $data['photo']['link'] = [];
- // 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++) {
- $data['photo']['link'][$i] = DI::baseUrl() . "/photo/" . $data['photo']['resource-id'] . "-" . $k . "." . $typetoext[$data['photo']['type']];
- $i++;
- }
- }
- 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
- $condition = ['uid' => api_user(), 'resource-id' => $photo_id];
- $item = Post::selectFirst(['id', 'uid', 'uri', 'parent', 'allow_cid', 'deny_cid', 'allow_gid', 'deny_gid'], $condition);
- if (!DBA::isResult($item)) {
- throw new NotFoundException('Photo-related item not found.');
- }
-
- $data['photo']['friendica_activities'] = api_format_items_activities($item, $type);
-
- // retrieve comments on photo
- $condition = ["`parent` = ? AND `uid` = ? AND `gravity` IN (?, ?)",
- $item['parent'], api_user(), GRAVITY_PARENT, GRAVITY_COMMENT];
-
- $statuses = Post::selectForUser(api_user(), [], $condition);
-
- // prepare output of comments
- $commentData = api_format_items(Post::toArray($statuses), $user_info, false, $type);
- $comments = [];
- if ($type == "xml") {
- $k = 0;
- foreach ($commentData as $comment) {
- $comments[$k++ . ":comment"] = $comment;
- }
- } else {
- foreach ($commentData as $comment) {
- $comments[] = $comment;
- }
- }
- $data['photo']['friendica_comments'] = $comments;
-
- // include info if rights on photo and rights on item are mismatching
- $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'];
- $data['photo']['rights_mismatch'] = $rights_mismatch;
-
- return $data;
-}
-
-/**
- * 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'];
- $condition = ['parent-uri' => $item['uri'], 'gravity' => GRAVITY_ACTIVITY, 'uid' => [0, $item['uid']], 'vid' => Verb::getID(Activity::ANNOUNCE)];
- $announce = Post::selectFirstForUser($item['uid'], $fields, $condition, ['order' => ['received' => true]]);
- if (!DBA::isResult($announce)) {
- return [];
- }
-
- return array_merge($item, $announce);
-}
-
-/**
- *
- * @param array $item
- *
- * @return array
- * @throws Exception
- */
-function api_in_reply_to($item)
-{
- $in_reply_to = [];
-
- $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;
-
- if (($item['thr-parent'] != $item['uri']) && ($item['gravity'] != GRAVITY_PARENT)) {
- $parent = Post::selectFirst(['id'], ['uid' => $item['uid'], 'uri' => $item['thr-parent']]);
- if (DBA::isResult($parent)) {
- $in_reply_to['status_id'] = intval($parent['id']);
- } else {
- $in_reply_to['status_id'] = intval($item['parent']);
- }
-
- $in_reply_to['status_id_str'] = (string) intval($in_reply_to['status_id']);
-
- $fields = ['author-nick', 'author-name', 'author-id', 'author-link'];
- $parent = Post::selectFirst($fields, ['id' => $in_reply_to['status_id']]);
-
- if (DBA::isResult($parent)) {
- $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']);
- }
-
- // 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'])) {
- 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']]);
- $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;
- }
- }
-
- return $in_reply_to;
-}
-
-/**
- *
- * @param string $text
- *
- * @return string
- * @throws InternalServerErrorException
- */
-function api_clean_plain_items($text)
-{
- $include_entities = strtolower($_REQUEST['include_entities'] ?? 'false');
-
- $text = BBCode::cleanPictureLinks($text);
- $URLSearchString = "^\[\]";
-
- $text = preg_replace("/([!#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$1$3', $text);
-
- if ($include_entities == "true") {
- $text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '[url=$1]$1[/url]', $text);
- }
-
- // Simplify "attachment" element
- $text = BBCode::removeAttachment($text);
-
- return $text;
-}
-
-/**
- *
- * @param array $contacts
- *
- * @return void
- */
-function api_best_nickname(&$contacts)
-{
- $best_contact = [];
-
- if (count($contacts) == 0) {
- return;
- }
-
- foreach ($contacts as $contact) {
- if ($contact["network"] == "") {
- $contact["network"] = "dfrn";
- $best_contact = [$contact];
- }
- }
-
- if (sizeof($best_contact) == 0) {
- foreach ($contacts as $contact) {
- if ($contact["network"] == "dfrn") {
- $best_contact = [$contact];
- }
- }
- }
-
- if (sizeof($best_contact) == 0) {
- foreach ($contacts as $contact) {
- if ($contact["network"] == "dspr") {
- $best_contact = [$contact];
- }
- }
- }
-
- if (sizeof($best_contact) == 0) {
- foreach ($contacts as $contact) {
- if ($contact["network"] == "stat") {
- $best_contact = [$contact];
- }
- }
- }
-
- if (sizeof($best_contact) == 0) {
- foreach ($contacts as $contact) {
- if ($contact["network"] == "pump") {
- $best_contact = [$contact];
- }
- }