X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fapi.php;h=115869fe27baeb96ba65138e868e62a23cb7433f;hb=3e2fa7867c20a7b2abaf2f1c4f9c452654c0fbab;hp=fdebdd48bc25eb37f223c2d9967890af1f0767f2;hpb=97f8ea2c3ac25ffef3b623c1889c43284d58461c;p=friendica.git diff --git a/include/api.php b/include/api.php index fdebdd48bc..115869fe27 100644 --- a/include/api.php +++ b/include/api.php @@ -1,6 +1,6 @@ setForUser($a, $record); + // Don't refresh the login date more often than twice a day to spare database writes + $login_refresh = strcmp(DateTimeFormat::utc('now - 12 hours'), $record['login_date']) > 0; + + DI::auth()->setForUser($a, $record, false, false, $login_refresh); $_SESSION["allow_api"] = true; @@ -307,22 +311,22 @@ function api_call(App $a, App\Arguments $args = null) } $type = "json"; - if (strpos($args->getQueryString(), ".xml") > 0) { + if (strpos($args->getCommand(), ".xml") > 0) { $type = "xml"; } - if (strpos($args->getQueryString(), ".json") > 0) { + if (strpos($args->getCommand(), ".json") > 0) { $type = "json"; } - if (strpos($args->getQueryString(), ".rss") > 0) { + if (strpos($args->getCommand(), ".rss") > 0) { $type = "rss"; } - if (strpos($args->getQueryString(), ".atom") > 0) { + if (strpos($args->getCommand(), ".atom") > 0) { $type = "atom"; } try { foreach ($API as $p => $info) { - if (strpos($args->getQueryString(), $p) === 0) { + if (strpos($args->getCommand(), $p) === 0) { if (!api_check_method($info['method'])) { throw new MethodNotAllowedException(); } @@ -331,16 +335,16 @@ function api_call(App $a, App\Arguments $args = null) if (!empty($info['auth']) && api_user() === false) { api_login($a); + Logger::info(API_LOG_PREFIX . 'username {username}', ['module' => 'api', 'action' => 'call', 'username' => $a->user['username']]); } - Logger::info(API_LOG_PREFIX . 'username {username}', ['module' => 'api', 'action' => 'call', 'username' => $a->user['username']]); Logger::debug(API_LOG_PREFIX . 'parameters', ['module' => 'api', 'action' => 'call', 'parameters' => $_REQUEST]); $stamp = microtime(true); $return = call_user_func($info['func'], $type); $duration = floatval(microtime(true) - $stamp); - Logger::info(API_LOG_PREFIX . 'username {username}', ['module' => 'api', 'action' => 'call', 'username' => $a->user['username'], 'duration' => round($duration, 2)]); + Logger::info(API_LOG_PREFIX . 'duration {duration}', ['module' => 'api', 'action' => 'call', 'duration' => round($duration, 2)]); DI::profiler()->saveLog(DI::logger(), API_LOG_PREFIX . 'performance'); @@ -380,7 +384,7 @@ function api_call(App $a, App\Arguments $args = null) } Logger::warning(API_LOG_PREFIX . 'not implemented', ['module' => 'api', 'action' => 'call', 'query' => DI::args()->getQueryString()]); - throw new NotImplementedException(); + throw new NotFoundException(); } catch (HTTPException $e) { header("HTTP/1.1 {$e->getCode()} {$e->httpdesc}"); return api_error($type, $e, $args); @@ -623,7 +627,7 @@ function api_get_user(App $a, $contact_id = null) 'name' => $contact["name"], 'screen_name' => (($contact['nick']) ? $contact['nick'] : $contact['name']), 'location' => ($contact["location"] != "") ? $contact["location"] : ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol']), - 'description' => BBCode::toPlaintext($contact["about"]), + 'description' => BBCode::toPlaintext($contact["about"] ?? ''), 'profile_image_url' => $contact["micro"], 'profile_image_url_https' => $contact["micro"], 'profile_image_url_profile_size' => $contact["thumb"], @@ -650,8 +654,8 @@ function api_get_user(App $a, $contact_id = null) 'notifications' => false, 'statusnet_profile_url' => $contact["url"], 'uid' => 0, - 'cid' => Contact::getIdForURL($contact["url"], api_user(), true), - 'pid' => Contact::getIdForURL($contact["url"], 0, true), + 'cid' => Contact::getIdForURL($contact["url"], api_user(), false), + 'pid' => Contact::getIdForURL($contact["url"], 0, false), 'self' => 0, 'network' => $contact["network"], ]; @@ -675,7 +679,7 @@ function api_get_user(App $a, $contact_id = null) $countfollowers = 0; $starred = 0; - $pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, true); + $pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, false); if (!empty($profile['about'])) { $description = $profile['about']; @@ -697,7 +701,7 @@ function api_get_user(App $a, $contact_id = null) 'name' => (($uinfo[0]['name']) ? $uinfo[0]['name'] : $uinfo[0]['nick']), 'screen_name' => (($uinfo[0]['nick']) ? $uinfo[0]['nick'] : $uinfo[0]['name']), 'location' => $location, - 'description' => BBCode::toPlaintext($description), + 'description' => BBCode::toPlaintext($description ?? ''), 'profile_image_url' => $uinfo[0]['micro'], 'profile_image_url_https' => $uinfo[0]['micro'], 'profile_image_url_profile_size' => $uinfo[0]["thumb"], @@ -727,7 +731,7 @@ function api_get_user(App $a, $contact_id = null) 'statusnet_profile_url' => $uinfo[0]['url'], 'uid' => intval($uinfo[0]['uid']), 'cid' => intval($uinfo[0]['cid']), - 'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, true), + 'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, false), 'self' => $uinfo[0]['self'], 'network' => $uinfo[0]['network'], ]; @@ -1114,8 +1118,8 @@ function api_statuses_update($type) if ($throttle_day > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60); - $condition = ["`uid` = ? AND `wall` AND `received` > ?", api_user(), $datefrom]; - $posts_day = DBA::count('thread', $condition); + $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, api_user(), $datefrom]; + $posts_day = Post::count($condition); if ($posts_day > $throttle_day) { Logger::log('Daily posting limit reached for user '.api_user(), Logger::DEBUG); @@ -1128,8 +1132,8 @@ function api_statuses_update($type) if ($throttle_week > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*7); - $condition = ["`uid` = ? AND `wall` AND `received` > ?", api_user(), $datefrom]; - $posts_week = DBA::count('thread', $condition); + $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, api_user(), $datefrom]; + $posts_week = Post::count($condition); if ($posts_week > $throttle_week) { Logger::log('Weekly posting limit reached for user '.api_user(), Logger::DEBUG); @@ -1142,8 +1146,8 @@ function api_statuses_update($type) if ($throttle_month > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*30); - $condition = ["`uid` = ? AND `wall` AND `received` > ?", api_user(), $datefrom]; - $posts_month = DBA::count('thread', $condition); + $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, api_user(), $datefrom]; + $posts_month = Post::count($condition); if ($posts_month > $throttle_month) { Logger::log('Monthly posting limit reached for user '.api_user(), Logger::DEBUG); @@ -1240,7 +1244,7 @@ function api_media_upload() "image_type" => $media["type"], "friendica_preview_url" => $media["preview"]]; - Logger::log("Media uploaded: " . print_r($returndata, true), Logger::DEBUG); + Logger::info('Media uploaded', ['return' => $returndata]); return ["media" => $returndata]; } @@ -1310,7 +1314,7 @@ api_register_func('api/media/metadata/create', 'api_media_metadata_create', true /** * @param string $type Return format (atom, rss, xml, json) * @param int $item_id - * @return string + * @return array|string * @throws Exception */ function api_status_show($type, $item_id) @@ -1360,7 +1364,7 @@ function api_get_last_status($ownerId, $uid) */ function api_get_item(array $condition) { - $item = Item::selectFirst(Item::DISPLAY_FIELDLIST, $condition, ['order' => ['id' => true]]); + $item = Post::selectFirst(Item::DISPLAY_FIELDLIST, $condition, ['order' => ['id' => true]]); return $item; } @@ -1558,7 +1562,7 @@ function api_search($type) $params['group_by'] = ['uri-id']; } else { $condition = ["`id` > ? - " . ($exclude_replies ? " AND `id` = `parent` " : ' ') . " + " . ($exclude_replies ? " AND `gravity` = " . GRAVITY_PARENT : ' ') . " AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `body` LIKE CONCAT('%',?,'%')", $since_id, api_user(), $_REQUEST['q']]; @@ -1578,13 +1582,13 @@ function api_search($type) } if (!empty($id)) { - $statuses = Item::select([], ['id' => $id]); + $statuses = Post::select([], ['id' => $id]); } } - $statuses = $statuses ?: Item::selectForUser(api_user(), [], $condition, $params); + $statuses = $statuses ?: Post::selectForUser(api_user(), [], $condition, $params); - $data['status'] = api_format_items(Item::inArray($statuses), $user_info); + $data['status'] = api_format_items(Post::toArray($statuses), $user_info); bindComments($data['status']); @@ -1638,25 +1642,26 @@ function api_statuses_home_timeline($type) $start = max(0, ($page - 1) * $count); - $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `item`.`id` > ?", + $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ?", api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id]; if ($max_id > 0) { - $condition[0] .= " AND `item`.`id` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } if ($exclude_replies) { - $condition[0] .= ' AND `item`.`parent` = `item`.`id`'; + $condition[0] .= ' AND `gravity` = ?'; + $condition[] = GRAVITY_PARENT; } if ($conversation_id > 0) { - $condition[0] .= " AND `item`.`parent` = ?"; + $condition[0] .= " AND `parent` = ?"; $condition[] = $conversation_id; } $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectForUser(api_user(), [], $condition, $params); + $statuses = Post::selectForUser(api_user(), [], $condition, $params); - $items = Item::inArray($statuses); + $items = Post::toArray($statuses); $ret = api_format_items($items, $user_info, false, $type); @@ -1667,7 +1672,7 @@ function api_statuses_home_timeline($type) } if (!empty($idarray)) { - $unseen = Item::exists(['unseen' => true, 'id' => $idarray]); + $unseen = Post::exists(['unseen' => true, 'id' => $idarray]); if ($unseen) { Item::update(['unseen' => false], ['unseen' => true, 'id' => $idarray]); } @@ -1726,35 +1731,35 @@ function api_statuses_public_timeline($type) $start = max(0, ($page - 1) * $count); if ($exclude_replies && !$conversation_id) { - $condition = ["`gravity` IN (?, ?) AND `iid` > ? AND `private` = ? AND `wall` AND NOT `author`.`hidden`", - GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC]; + $condition = ["`gravity` = ? AND `id` > ? AND `private` = ? AND `wall` AND NOT `author-hidden`", + GRAVITY_PARENT, $since_id, Item::PUBLIC]; if ($max_id > 0) { - $condition[0] .= " AND `thread`.`iid` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } - $params = ['order' => ['iid' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Post::selectForUser(api_user(), [], $condition, $params); - $r = Item::inArray($statuses); + $r = Post::toArray($statuses); } else { - $condition = ["`gravity` IN (?, ?) AND `id` > ? AND `private` = ? AND `wall` AND `item`.`origin` AND NOT `author`.`hidden`", + $condition = ["`gravity` IN (?, ?) AND `id` > ? AND `private` = ? AND `wall` AND `origin` AND NOT `author-hidden`", GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC]; if ($max_id > 0) { - $condition[0] .= " AND `item`.`id` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } if ($conversation_id > 0) { - $condition[0] .= " AND `item`.`parent` = ?"; + $condition[0] .= " AND `parent` = ?"; $condition[] = $conversation_id; } $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectForUser(api_user(), [], $condition, $params); + $statuses = Post::selectForUser(api_user(), [], $condition, $params); - $r = Item::inArray($statuses); + $r = Post::toArray($statuses); } $ret = api_format_items($r, $user_info, false, $type); @@ -1805,18 +1810,18 @@ function api_statuses_networkpublic_timeline($type) $start = max(0, ($page - 1) * $count); - $condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `thread`.`iid` > ? AND `private` = ?", + $condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `id` > ? AND `private` = ?", GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC]; if ($max_id > 0) { - $condition[0] .= " AND `thread`.`iid` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } - $params = ['order' => ['iid' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Post::toArray(Post::selectForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params)); - $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); + $ret = api_format_items($statuses, $user_info, false, $type); bindComments($ret); @@ -1874,14 +1879,14 @@ function api_statuses_show($type) $conversation = !empty($_REQUEST['conversation']); // try to fetch the item for the local user - or the public item, if there is no local one - $uri_item = Item::selectFirst(['uri'], ['id' => $id]); + $uri_item = Post::selectFirst(['uri-id'], ['id' => $id]); if (!DBA::isResult($uri_item)) { - throw new BadRequestException("There is no status with this id."); + throw new BadRequestException(sprintf("There is no status with the id %d", $id)); } - $item = Item::selectFirst(['id'], ['uri' => $uri_item['uri'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); + $item = Post::selectFirst(['id'], ['uri-id' => $uri_item['uri-id'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); if (!DBA::isResult($item)) { - throw new BadRequestException("There is no status with this id."); + throw new BadRequestException(sprintf("There is no status with the uri-id %d for the given user.", $uri_item['uri-id'])); } $id = $item['id']; @@ -1894,14 +1899,14 @@ function api_statuses_show($type) $params = []; } - $statuses = Item::selectForUser(api_user(), [], $condition, $params); + $statuses = Post::selectForUser(api_user(), [], $condition, $params); /// @TODO How about copying this to above methods which don't check $r ? if (!DBA::isResult($statuses)) { - throw new BadRequestException("There is no status with this id."); + throw new BadRequestException(sprintf("There is no status or conversation with the id %d.", $id)); } - $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); + $ret = api_format_items(Post::toArray($statuses), $user_info, false, $type); if ($conversation) { $data = ['status' => $ret]; @@ -1957,34 +1962,34 @@ function api_conversation_show($type) Logger::info(API_LOG_PREFIX . '{subaction}', ['module' => 'api', 'action' => 'conversation', 'subaction' => 'show', 'id' => $id]); // try to fetch the item for the local user - or the public item, if there is no local one - $item = Item::selectFirst(['parent-uri'], ['id' => $id]); + $item = Post::selectFirst(['parent-uri-id'], ['id' => $id]); if (!DBA::isResult($item)) { throw new BadRequestException("There is no status with this id."); } - $parent = Item::selectFirst(['id'], ['uri' => $item['parent-uri'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); + $parent = Post::selectFirst(['id'], ['uri-id' => $item['parent-uri-id'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); if (!DBA::isResult($parent)) { throw new BadRequestException("There is no status with this id."); } $id = $parent['id']; - $condition = ["`parent` = ? AND `uid` IN (0, ?) AND `gravity` IN (?, ?) AND `item`.`id` > ?", + $condition = ["`parent` = ? AND `uid` IN (0, ?) AND `gravity` IN (?, ?) AND `id` > ?", $id, api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id]; if ($max_id > 0) { - $condition[0] .= " AND `item`.`id` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectForUser(api_user(), [], $condition, $params); + $statuses = Post::selectForUser(api_user(), [], $condition, $params); if (!DBA::isResult($statuses)) { throw new BadRequestException("There is no status with id $id."); } - $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); + $ret = api_format_items(Post::toArray($statuses), $user_info, false, $type); $data = ['status' => $ret]; return api_format_data("statuses", $type, $data); @@ -2033,35 +2038,40 @@ function api_statuses_repeat($type) Logger::log('API: api_statuses_repeat: '.$id); - $fields = ['uri-id', 'body', 'title', 'attach', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink']; - $item = Item::selectFirst($fields, ['id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED]]); - - if (DBA::isResult($item) && $item['body'] != "") { - if (strpos($item['body'], "[/share]") !== false) { - $pos = strpos($item['body'], "[share"); - $post = substr($item['body'], $pos); + $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', local_user())) { + throw new InternalServerErrorException(); + } + + $item_id = $id; } else { - $post = share_header($item['author-name'], $item['author-link'], $item['author-avatar'], $item['guid'], $item['created'], $item['plink']); + 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"; + if (!empty($item['title'])) { + $post .= '[h3]' . $item['title'] . "[/h3]\n"; + } + + $post .= $item['body']; + $post .= "[/share]"; } + $_REQUEST['body'] = $post; + $_REQUEST['profile_uid'] = api_user(); + $_REQUEST['api_source'] = true; - $post .= $item['body']; - $post .= "[/share]"; - } - $_REQUEST['body'] = $post; - $_REQUEST['attach'] = $item['attach']; - $_REQUEST['profile_uid'] = api_user(); - $_REQUEST['api_source'] = true; + if (empty($_REQUEST['source'])) { + $_REQUEST["source"] = api_source(); + } - if (empty($_REQUEST['source'])) { - $_REQUEST["source"] = api_source(); + $item_id = item_post($a); } - - $item_id = item_post($a); - - /// @todo Copy tags from the original post to the new one } else { throw new ForbiddenException(); } @@ -2152,44 +2162,34 @@ function api_statuses_mentions($type) // get last network messages // params - $since_id = $_REQUEST['since_id'] ?? 0; - $max_id = $_REQUEST['max_id'] ?? 0; - $count = $_REQUEST['count'] ?? 20; - $page = $_REQUEST['page'] ?? 1; + $since_id = intval($_REQUEST['since_id'] ?? 0); + $max_id = intval($_REQUEST['max_id'] ?? 0); + $count = intval($_REQUEST['count'] ?? 20); + $page = intval($_REQUEST['page'] ?? 1); $start = max(0, ($page - 1) * $count); - $query = "SELECT `item`.`id` FROM `user-item` - INNER JOIN `item` ON `item`.`id` = `user-item`.`iid` AND `item`.`gravity` IN (?, ?) - WHERE (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) AND - `user-item`.`uid` = ? AND `user-item`.`notification-type` & ? != 0 - AND `user-item`.`iid` > ?"; + $query = "`gravity` IN (?, ?) AND `uri-id` IN + (SELECT `uri-id` FROM `post-user-notification` WHERE `uid` = ? AND `notification-type` & ? != 0 ORDER BY `uri-id`) + AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `id` > ?"; + $condition = [GRAVITY_PARENT, GRAVITY_COMMENT, api_user(), - UserItem::NOTIF_EXPLICIT_TAGGED | UserItem::NOTIF_IMPLICIT_TAGGED | - UserItem::NOTIF_THREAD_COMMENT | UserItem::NOTIF_DIRECT_COMMENT | - UserItem::NOTIF_DIRECT_THREAD_COMMENT, - $since_id]; + Post\UserNotification::NOTIF_EXPLICIT_TAGGED | Post\UserNotification::NOTIF_IMPLICIT_TAGGED | + Post\UserNotification::NOTIF_THREAD_COMMENT | Post\UserNotification::NOTIF_DIRECT_COMMENT | + Post\UserNotification::NOTIF_DIRECT_THREAD_COMMENT, + api_user(), $since_id]; if ($max_id > 0) { - $query .= " AND `item`.`id` <= ?"; + $query .= " AND `id` <= ?"; $condition[] = $max_id; } - $query .= " ORDER BY `user-item`.`iid` DESC LIMIT ?, ?"; - $condition[] = $start; - $condition[] = $count; - - $useritems = DBA::p($query, $condition); - $itemids = []; - while ($useritem = DBA::fetch($useritems)) { - $itemids[] = $useritem['id']; - } - DBA::close($useritems); + array_unshift($condition, $query); $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectForUser(api_user(), [], ['id' => $itemids], $params); + $statuses = Post::selectForUser(api_user(), [], $condition, $params); - $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); + $ret = api_format_items(Post::toArray($statuses), $user_info, false, $type); $data = ['status' => $ret]; switch ($type) { @@ -2228,12 +2228,7 @@ function api_statuses_user_timeline($type) throw new ForbiddenException(); } - Logger::log( - "api_statuses_user_timeline: api_user: ". api_user() . - "\nuser_info: ".print_r($user_info, true) . - "\n_REQUEST: ".print_r($_REQUEST, true), - Logger::DEBUG - ); + Logger::info('api_statuses_user_timeline', ['api_user' => api_user(), 'user_info' => $user_info, '_REQUEST' => $_REQUEST]); $since_id = $_REQUEST['since_id'] ?? 0; $max_id = $_REQUEST['max_id'] ?? 0; @@ -2246,31 +2241,31 @@ function api_statuses_user_timeline($type) $start = max(0, ($page - 1) * $count); - $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `item`.`id` > ? AND `item`.`contact-id` = ?", + $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ? AND `contact-id` = ?", api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, $user_info['cid']]; if ($user_info['self'] == 1) { - $condition[0] .= ' AND `item`.`wall` '; + $condition[0] .= ' AND `wall` '; } if ($exclude_replies) { - $condition[0] .= ' AND `item`.`parent` = `item`.`id`'; + $condition[0] .= ' AND `gravity` = ?'; + $condition[] = GRAVITY_PARENT; } if ($conversation_id > 0) { - $condition[0] .= " AND `item`.`parent` = ?"; + $condition[0] .= " AND `parent` = ?"; $condition[] = $conversation_id; } if ($max_id > 0) { - $condition[0] .= " AND `item`.`id` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } - $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectForUser(api_user(), [], $condition, $params); + $statuses = Post::selectForUser(api_user(), [], $condition, $params); - $ret = api_format_items(Item::inArray($statuses), $user_info, true, $type); + $ret = api_format_items(Post::toArray($statuses), $user_info, true, $type); bindComments($ret); @@ -2328,7 +2323,7 @@ function api_favorites_create_destroy($type) $itemid = intval($_REQUEST['id'] ?? 0); } - $item = Item::selectFirstForUser(api_user(), [], ['id' => $itemid, 'uid' => api_user()]); + $item = Post::selectFirstForUser(api_user(), [], ['id' => $itemid, 'uid' => api_user()]); if (!DBA::isResult($item)) { throw new BadRequestException("Invalid item."); @@ -2418,13 +2413,13 @@ function api_favorites($type) $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; if ($max_id > 0) { - $condition[0] .= " AND `item`.`id` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } - $statuses = Item::selectForUser(api_user(), [], $condition, $params); + $statuses = Post::selectForUser(api_user(), [], $condition, $params); - $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); + $ret = api_format_items(Post::toArray($statuses), $user_info, false, $type); } bindComments($ret); @@ -2491,10 +2486,10 @@ function api_format_messages($item, $recipient, $sender) if ($_GET['getText'] == 'html') { $ret['text'] = BBCode::convert($item['body'], false); } elseif ($_GET['getText'] == 'plain') { - $ret['text'] = trim(HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, 2, true), 0)); + $ret['text'] = trim(HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, BBCode::API, true), 0)); } } else { - $ret['text'] = $item['title'] . "\n" . HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, 2, true), 0); + $ret['text'] = $item['title'] . "\n" . HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, BBCode::API, true), 0); } if (!empty($_GET['getUserObjects']) && $_GET['getUserObjects'] == 'false') { unset($ret['sender']); @@ -2520,7 +2515,7 @@ function api_convert_item($item) $attachments = api_get_attachments($body); // Workaround for ostatus messages where the title is identically to the body - $html = BBCode::convert(api_clean_plain_items($body), false, 2, true); + $html = BBCode::convert(api_clean_plain_items($body), false, BBCode::API, true); $statusbody = trim(HTML::toPlaintext($html, 0)); // handle data: images @@ -2865,9 +2860,9 @@ function api_format_items_activities($item, $type = "json") ]; $condition = ['uid' => $item['uid'], 'thr-parent' => $item['uri'], 'gravity' => GRAVITY_ACTIVITY]; - $ret = Item::selectForUser($item['uid'], ['author-id', 'verb'], $condition); + $ret = Post::selectForUser($item['uid'], ['author-id', 'verb'], $condition); - while ($parent_item = Item::fetch($ret)) { + while ($parent_item = Post::fetch($ret)) { // not used as result should be structured like other user data //builtin_activity_puller($i, $activities); @@ -2935,6 +2930,10 @@ function api_format_items($items, $user_info, $filter_user = false, $type = "jso $ret = []; + if (empty($items)) { + return $ret; + } + foreach ((array)$items as $item) { list($status_user, $author_user, $owner_user) = api_item_get_user($a, $item); @@ -3027,7 +3026,7 @@ function api_format_item($item, $type = "json", $status_user = null, $author_use $retweeted_item = []; $quoted_item = []; - if ($item["id"] == $item["parent"]) { + if ($item['gravity'] == GRAVITY_PARENT) { $body = $item['body']; $retweeted_item = api_share_as_retweet($item); if ($body != $item['body']) { @@ -3296,25 +3295,28 @@ function api_lists_statuses($type) $start = max(0, ($page - 1) * $count); - $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ? AND `group_member`.`gid` = ?", - api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, $_REQUEST['list_id']]; + $groups = DBA::selectToArray('group_member', ['contact-id'], ['gid' => 1]); + $gids = array_column($groups, 'contact-id'); + $condition = ['uid' => api_user(), 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'group-id' => $gids]; + $condition = DBA::mergeConditions($condition, ["`id` > ?", $since_id]); if ($max_id > 0) { - $condition[0] .= " AND `item`.`id` <= ?"; + $condition[0] .= " AND `id` <= ?"; $condition[] = $max_id; } if ($exclude_replies > 0) { - $condition[0] .= ' AND `item`.`parent` = `item`.`id`'; + $condition[0] .= ' AND `gravity` = ?'; + $condition[] = GRAVITY_PARENT; } if ($conversation_id > 0) { - $condition[0] .= " AND `item`.`parent` = ?"; + $condition[0] .= " AND `parent` = ?"; $condition[] = $conversation_id; } $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; - $statuses = Item::selectForUser(api_user(), [], $condition, $params); + $statuses = Post::selectForUser(api_user(), [], $condition, $params); - $items = api_format_items(Item::inArray($statuses), $user_info, false, $type); + $items = api_format_items(Post::toArray($statuses), $user_info, false, $type); $data = ['status' => $items]; switch ($type) { @@ -3576,96 +3578,6 @@ function api_statusnet_version($type) api_register_func('api/gnusocial/version', 'api_statusnet_version', false); api_register_func('api/statusnet/version', 'api_statusnet_version', false); -/** - * - * @param string $type Return type (atom, rss, xml, json) - * - * @param int $rel A contact relationship constant - * @return array|string|void - * @throws BadRequestException - * @throws ForbiddenException - * @throws ImagickException - * @throws InternalServerErrorException - * @throws UnauthorizedException - * @todo use api_format_data() to return data - */ -function api_ff_ids($type, int $rel) -{ - if (!api_user()) { - throw new ForbiddenException(); - } - - $a = DI::app(); - - api_get_user($a); - - $stringify_ids = $_REQUEST['stringify_ids'] ?? false; - - $contacts = DBA::p("SELECT `pcontact`.`id` - FROM `contact` - INNER JOIN `contact` AS `pcontact` - ON `contact`.`nurl` = `pcontact`.`nurl` - AND `pcontact`.`uid` = 0 - WHERE `contact`.`uid` = ? - AND NOT `contact`.`self` - AND `contact`.`rel` IN (?, ?)", - api_user(), - $rel, - Contact::FRIEND - ); - - $ids = []; - foreach (DBA::toArray($contacts) as $contact) { - if ($stringify_ids) { - $ids[] = $contact['id']; - } else { - $ids[] = intval($contact['id']); - } - } - - return api_format_data('ids', $type, ['id' => $ids]); -} - -/** - * Returns the ID of every user the user is following. - * - * @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/accounts-and-users/follow-search-get-users/api-reference/get-friends-ids - */ -function api_friends_ids($type) -{ - return api_ff_ids($type, Contact::SHARING); -} - -/** - * Returns the ID of every user following the user. - * - * @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/accounts-and-users/follow-search-get-users/api-reference/get-followers-ids - */ -function api_followers_ids($type) -{ - return api_ff_ids($type, Contact::FOLLOWER); -} - -/// @TODO move to top of file or somewhere better -api_register_func('api/friends/ids', 'api_friends_ids', true); -api_register_func('api/followers/ids', 'api_followers_ids', true); - /** * Sends a new direct message. * @@ -4161,26 +4073,18 @@ function api_fr_photoalbum_delete($type) throw new BadRequestException("no albumname specified"); } // check if album is existing - $r = q( - "SELECT DISTINCT `resource-id` FROM `photo` WHERE `uid` = %d AND `album` = '%s'", - intval(api_user()), - DBA::escape($album) - ); - if (!DBA::isResult($r)) { + + $photos = DBA::selectToArray('photo', ['resource-id'], ['uid' => api_user(), 'album' => $album], ['group_by' => ['resource-id']]); + if (!DBA::isResult($photos)) { throw new BadRequestException("album not available"); } + $resourceIds = array_column($photos, 'resource-id'); + // 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() performs the federation of the deletion to other networks - foreach ($r as $rr) { - $condition = ['uid' => local_user(), 'resource-id' => $rr['resource-id'], 'type' => 'photo']; - $photo_item = Item::selectFirstForUser(local_user(), ['id'], $condition); - - if (!DBA::isResult($photo_item)) { - throw new InternalServerErrorException("problem with deleting items occured"); - } - Item::deleteForUser(['id' => $photo_item['id']], api_user()); - } + $condition = ['uid' => api_user(), 'resource-id' => $resourceIds, 'type' => 'photo']; + Item::deleteForUser($condition, api_user()); // now let's delete all photos from the album $result = Photo::delete(['uid' => api_user(), 'album' => $album]); @@ -4309,7 +4213,7 @@ function api_fr_photo_create_update($type) $deny_cid = $_REQUEST['deny_cid' ] ?? null; $allow_gid = $_REQUEST['allow_gid'] ?? null; $deny_gid = $_REQUEST['deny_gid' ] ?? null; - $visibility = !empty($_REQUEST['visibility']) && $_REQUEST['visibility'] !== "false"; + $visibility = !$allow_cid && !$deny_cid && !$allow_gid && !$deny_gid; // do several checks on input parameters // we do not allow calls without album string @@ -4457,19 +4361,13 @@ function api_fr_photo_delete($type) // return success of deletion or error message if ($result) { - // retrieve the id of the parent element (the photo element) - $condition = ['uid' => local_user(), 'resource-id' => $photo_id, 'type' => 'photo']; - $photo_item = Item::selectFirstForUser(local_user(), ['id'], $condition); - - if (!DBA::isResult($photo_item)) { - throw new InternalServerErrorException("problem with deleting items occured"); - } // 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) - Item::deleteForUser(['id' => $photo_item['id']], api_user()); + $condition = ['uid' => api_user(), 'resource-id' => $photo_id, 'type' => 'photo']; + Item::deleteForUser($condition, api_user()); - $answer = ['result' => 'deleted', 'message' => 'photo with id `' . $photo_id . '` has been deleted from server.']; - return api_format_data("photo_delete", $type, ['$result' => $answer]); + $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"); } @@ -4828,7 +4726,7 @@ function save_media_to_database($mediatype, $media, $type, $album, $allow_cid, $ Logger::log("photo upload: new profile image upload ended", Logger::DEBUG); } - if (isset($r) && $r) { + 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); @@ -4861,7 +4759,6 @@ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $f $arr['guid'] = System::createUUID(); $arr['uid'] = intval(api_user()); $arr['uri'] = $uri; - $arr['parent-uri'] = $uri; $arr['type'] = 'photo'; $arr['wall'] = 1; $arr['resource-id'] = $hash; @@ -4975,8 +4872,8 @@ function prepare_photo_data($type, $scale, $photo_id) } // retrieve item element for getting activities (like, dislike etc.) related to photo - $condition = ['uid' => local_user(), 'resource-id' => $photo_id, 'type' => 'photo']; - $item = Item::selectFirstForUser(local_user(), ['id'], $condition); + $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.'); } @@ -4984,13 +4881,13 @@ function prepare_photo_data($type, $scale, $photo_id) $data['photo']['friendica_activities'] = api_format_items_activities($item, $type); // retrieve comments on photo - $condition = ["`parent` = ? AND `uid` = ? AND (`gravity` IN (?, ?) OR `type`='photo')", - $item[0]['parent'], api_user(), GRAVITY_PARENT, GRAVITY_COMMENT]; + $condition = ["`parent` = ? AND `uid` = ? AND `gravity` IN (?, ?)", + $item['parent'], api_user(), GRAVITY_PARENT, GRAVITY_COMMENT]; - $statuses = Item::selectForUser(api_user(), [], $condition); + $statuses = Post::selectForUser(api_user(), [], $condition); // prepare output of comments - $commentData = api_format_items(Item::inArray($statuses), $user_info, false, $type); + $commentData = api_format_items(Post::toArray($statuses), $user_info, false, $type); $comments = []; if ($type == "xml") { $k = 0; @@ -5005,10 +4902,10 @@ function prepare_photo_data($type, $scale, $photo_id) $data['photo']['friendica_comments'] = $comments; // include info if rights on photo and rights on item are mismatching - $rights_mismatch = $data['photo']['allow_cid'] != $item[0]['allow_cid'] || - $data['photo']['deny_cid'] != $item[0]['deny_cid'] || - $data['photo']['allow_gid'] != $item[0]['allow_gid'] || - $data['photo']['deny_cid'] != $item[0]['deny_cid']; + $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; @@ -5102,9 +4999,8 @@ function api_get_announce($item) } $fields = ['author-id', 'author-name', 'author-link', 'author-avatar']; - $activity = Item::activityToIndex(Activity::ANNOUNCE); - $condition = ['parent-uri' => $item['uri'], 'gravity' => GRAVITY_ACTIVITY, 'uid' => [0, $item['uid']], 'activity' => $activity]; - $announce = Item::selectFirstForUser($item['uid'], $fields, $condition, ['order' => ['received' => true]]); + $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 []; } @@ -5155,7 +5051,7 @@ function api_share_as_retweet(&$item) $reshared_item["share-pre-body"] = $reshared['comment']; $reshared_item["body"] = $reshared['shared']; - $reshared_item["author-id"] = Contact::getIdForURL($reshared['profile'], 0, true); + $reshared_item["author-id"] = Contact::getIdForURL($reshared['profile'], 0, false); $reshared_item["author-name"] = $reshared['author']; $reshared_item["author-link"] = $reshared['profile']; $reshared_item["author-avatar"] = $reshared['avatar']; @@ -5173,7 +5069,7 @@ function api_share_as_retweet(&$item) } if (!empty($condition)) { - $original_item = Item::selectFirst([], $condition); + $original_item = Post::selectFirst([], $condition); if (DBA::isResult($original_item)) { $reshared_item = array_merge($reshared_item, $original_item); } @@ -5199,8 +5095,8 @@ function api_in_reply_to($item) $in_reply_to['user_id_str'] = null; $in_reply_to['screen_name'] = null; - if (($item['thr-parent'] != $item['uri']) && (intval($item['parent']) != intval($item['id']))) { - $parent = Item::selectFirst(['id'], ['uid' => $item['uid'], 'uri' => $item['thr-parent']]); + 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 { @@ -5210,7 +5106,7 @@ function api_in_reply_to($item) $in_reply_to['status_id_str'] = (string) intval($in_reply_to['status_id']); $fields = ['author-nick', 'author-name', 'author-id', 'author-link']; - $parent = Item::selectFirst($fields, ['id' => $in_reply_to['status_id']]); + $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']); @@ -5374,7 +5270,7 @@ function api_friendica_group_show($type) // loop through all groups and retrieve all members for adding data in the user array $grps = []; foreach ($r as $rr) { - $members = Contact::getByGroupId($rr['id']); + $members = Contact\Group::getById($rr['id']); $users = []; if ($type == "xml") { @@ -5699,7 +5595,7 @@ function api_friendica_group_update($type) } // remove members - $members = Contact::getByGroupId($gid); + $members = Contact\Group::getById($gid); foreach ($members as $member) { $cid = $member['id']; foreach ($users as $user) { @@ -5813,7 +5709,7 @@ function api_friendica_activity($type) $id = $_REQUEST['id'] ?? 0; - $res = Item::performActivity($id, $verb); + $res = Item::performActivity($id, $verb, api_user()); if ($res) { if ($type == "xml") { @@ -5911,8 +5807,8 @@ function api_friendica_notification_seen($type) $notify = DI::notify()->getByID($id, api_user()); DI::notify()->setSeen(true, $notify); - if ($notify->otype === Notify\ObjectType::ITEM) { - $item = Item::selectFirstForUser(api_user(), [], ['id' => $notify->iid, 'uid' => api_user()]); + if ($notify->otype === Notification\ObjectType::ITEM) { + $item = Post::selectFirstForUser(api_user(), [], ['id' => $notify->iid, 'uid' => api_user()]); if (DBA::isResult($item)) { // we found the item, return it to the user $ret = api_format_items([$item], $user_info, false, $type); @@ -6111,7 +6007,7 @@ function bindComments(&$data) } $idStr = DBA::escape(implode(', ', $ids)); - $sql = "SELECT `parent`, COUNT(*) as comments FROM `item` WHERE `parent` IN ($idStr) AND `deleted` = ? AND `gravity`= ? GROUP BY `parent`"; + $sql = "SELECT `parent`, COUNT(*) as comments FROM `post-user-view` WHERE `parent` IN ($idStr) AND `deleted` = ? AND `gravity`= ? GROUP BY `parent`"; $items = DBA::p($sql, 0, GRAVITY_COMMENT); $itemsData = DBA::toArray($items);