use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
use Friendica\Core\Logger;
-use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Network\HTTPException\ForbiddenException;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Network\HTTPException\NotFoundException;
-use Friendica\Network\HTTPException\TooManyRequestsException;
use Friendica\Network\HTTPException\UnauthorizedException;
use Friendica\Object\Image;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Images;
use Friendica\Util\Strings;
-require_once __DIR__ . '/../mod/item.php';
-
$API = [];
/**
$arr = [];
$arr['guid'] = System::createUUID();
- $arr['uid'] = intval($uid);
+ $arr['uid'] = $uid;
$arr['uri'] = $uri;
- $arr['type'] = 'photo';
+ $arr['post-type'] = Item::PT_IMAGE;
$arr['wall'] = 1;
$arr['resource-id'] = $hash;
$arr['contact-id'] = $owner_record['id'];
$arr['author-name'] = $owner_record['name'];
$arr['author-link'] = $owner_record['url'];
$arr['author-avatar'] = $owner_record['thumb'];
- $arr['title'] = "";
+ $arr['title'] = '';
$arr['allow_cid'] = $allow_cid;
$arr['allow_gid'] = $allow_gid;
$arr['deny_cid'] = $deny_cid;
$arr['visible'] = $visibility;
$arr['origin'] = 1;
- $typetoext = [
- 'image/jpeg' => 'jpg',
- 'image/png' => 'png',
- 'image/gif' => 'gif'
- ];
+ $typetoext = Images::supportedTypes();
// adds link to the thumbnail scale photo
$arr['body'] = '[url=' . DI::baseUrl() . '/photos/' . $owner_record['nick'] . '/image/' . $hash . ']'
* TWITTER API
*/
-/**
- * Updates the user’s current status.
- *
- * @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
- */
-function api_statuses_update($type)
-{
- BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
- $uid = BaseApi::getCurrentUserID();
-
- $a = DI::app();
-
- // convert $_POST array items to the form we use for web posts.
- if (!empty($_REQUEST['htmlstatus'])) {
- $txt = $_REQUEST['htmlstatus'];
- if ((strpos($txt, '<') !== false) || (strpos($txt, '>') !== false)) {
- $txt = HTML::toBBCodeVideo($txt);
-
- $config = HTMLPurifier_Config::createDefault();
- $config->set('Cache.DefinitionImpl', null);
-
- $purifier = new HTMLPurifier($config);
- $txt = $purifier->purify($txt);
-
- $_REQUEST['body'] = HTML::toBBCode($txt);
- }
- } else {
- $_REQUEST['body'] = $_REQUEST['status'] ?? null;
- }
-
- $_REQUEST['title'] = $_REQUEST['title'] ?? null;
-
- $parent = $_REQUEST['in_reply_to_status_id'] ?? null;
-
- // Twidere sends "-1" if it is no reply ...
- if ($parent == -1) {
- $parent = "";
- }
-
- if (ctype_digit($parent)) {
- $_REQUEST['parent'] = $parent;
- } else {
- $_REQUEST['parent_uri'] = $parent;
- }
-
- if (!empty($_REQUEST['lat']) && !empty($_REQUEST['long'])) {
- $_REQUEST['coord'] = sprintf("%s %s", $_REQUEST['lat'], $_REQUEST['long']);
- }
- $_REQUEST['profile_uid'] = $uid;
-
- if (!$parent) {
- // Check for throttling (maximum posts per day, week and month)
- $throttle_day = DI::config()->get('system', 'throttle_limit_day');
- if ($throttle_day > 0) {
- $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60);
-
- $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
- $posts_day = Post::count($condition);
-
- if ($posts_day > $throttle_day) {
- logger::info('Daily posting limit reached for user ' . $uid);
- // die(api_error($type, DI::l10n()->t("Daily posting limit of %d posts reached. The post was rejected.", $throttle_day));
- 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));
- }
- }
-
- $throttle_week = DI::config()->get('system', 'throttle_limit_week');
- if ($throttle_week > 0) {
- $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*7);
-
- $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
- $posts_week = Post::count($condition);
-
- if ($posts_week > $throttle_week) {
- logger::info('Weekly posting limit reached for user ' . $uid);
- // die(api_error($type, DI::l10n()->t("Weekly posting limit of %d posts reached. The post was rejected.", $throttle_week)));
- 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));
- }
- }
-
- $throttle_month = DI::config()->get('system', 'throttle_limit_month');
- if ($throttle_month > 0) {
- $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*30);
-
- $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
- $posts_month = Post::count($condition);
-
- if ($posts_month > $throttle_month) {
- logger::info('Monthly posting limit reached for user ' . $uid);
- // 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));
- }
- }
- }
-
- if (!empty($_REQUEST['media_ids'])) {
- $ids = explode(',', $_REQUEST['media_ids']);
- } elseif (!empty($_FILES['media'])) {
- // upload the image if we have one
- $picture = Photo::upload($uid, $_FILES['media']);
- if (is_array($picture)) {
- $ids[] = $picture['id'];
- }
- }
-
- $attachments = [];
- $ressources = [];
-
- if (!empty($ids)) {
- foreach ($ids as $id) {
- $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` = ?
- ORDER BY `photo`.`width` DESC LIMIT 2", $id, $uid));
-
- if (!empty($media)) {
- $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'];
- }
- $attachments[] = $attachment;
- }
- }
-
- // We have to avoid that the post is rejected because of an empty body
- if (empty($_REQUEST['body'])) {
- $_REQUEST['body'] = '[hr]';
- }
- }
-
- if (!empty($attachments)) {
- $_REQUEST['attachments'] = $attachments;
- }
-
- // set this so that the item_post() function is quiet and doesn't redirect or emit json
-
- $_REQUEST['api_source'] = true;
-
- if (empty($_REQUEST['source'])) {
- $_REQUEST['source'] = BaseApi::getCurrentApplication()['name'] ?: 'API';
- }
-
- // call out normal post function
- $item_id = item_post($a);
-
- 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) {
- Photo::setPermissionForRessource($ressource, $uid, $item['allow_cid'], $item['allow_gid'], $item['deny_cid'], $item['deny_gid']);
- }
- }
-
- $include_entities = strtolower(($_REQUEST['include_entities'] ?? 'false') == 'true');
-
- // output the post that we just posted.
- $status_info = DI::twitterStatus()->createFromItemId($item_id, $uid, $include_entities)->toArray();
- return DI::apiResponse()->formatData('statuses', $type, ['status' => $status_info]);
-}
-
-api_register_func('api/statuses/update', 'api_statuses_update', true);
-api_register_func('api/statuses/update_with_media', 'api_statuses_update', true);
-api_register_func('api/statuses/mediap', 'api_statuses_update', true);
-
-/**
- * Repeats a status.
- *
- * @param string $type Return type (atom, rss, xml, json)
- *
- * @return array|string
- * @throws BadRequestException
- * @throws ForbiddenException
- * @throws ImagickException
- * @throws InternalServerErrorException
- * @throws UnauthorizedException
- * @see https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-retweet-id
- */
-function api_statuses_repeat($type)
-{
- BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
- $uid = BaseApi::getCurrentUserID();
-
- // params
- $id = intval(DI::args()->getArgv()[3] ?? 0);
-
- if ($id == 0) {
- $id = intval($_REQUEST['id'] ?? 0);
- }
-
- // Hotot workaround
- if ($id == 0) {
- $id = intval(DI::args()->getArgv()[4] ?? 0);
- }
-
- logger::notice('API: api_statuses_repeat: ' . $id);
-
- $fields = ['uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
- $item = Post::selectFirst($fields, ['id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
-
- if (DBA::isResult($item) && !empty($item['body'])) {
- if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) {
- if (!Item::performActivity($id, 'announce', $uid)) {
- throw new InternalServerErrorException();
- }
-
- $item_id = $id;
- } else {
- 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";
- }
-
- $post .= $item['body'];
- $post .= "[/share]";
- }
- $_REQUEST['body'] = $post;
- $_REQUEST['profile_uid'] = $uid;
- $_REQUEST['api_source'] = true;
-
- if (empty($_REQUEST['source'])) {
- $_REQUEST['source'] = BaseApi::getCurrentApplication()['name'] ?: 'API';
- }
-
- $item_id = item_post(DI::app());
- }
- } else {
- throw new ForbiddenException();
- }
-
- $include_entities = strtolower(($_REQUEST['include_entities'] ?? 'false') == 'true');
-
- // output the post that we just posted.
- $status_info = DI::twitterStatus()->createFromItemId($item_id, $uid, $include_entities)->toArray();
- return DI::apiResponse()->formatData('statuses', $type, ['status' => $status_info]);
-}
-
-api_register_func('api/statuses/retweet', 'api_statuses_repeat', true);
-
/**
* Returns all lists the user subscribes to.
*
$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;
+ // Pictures uploaded via API never get posted as a visible status
+ // See https://github.com/friendica/friendica/issues/10990
+ $visibility = false;
// do several checks on input parameters
// we do not allow calls without album string