X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fapi.php;h=a70c18f3ca6a149417d50ea813c5d3eb2e2b12e2;hb=8329705ebab61159b114b274e794bf6c3d6a6a95;hp=2d41cb986a4e5706c42fa6205bc8926816ae4221;hpb=27d94023eef0263a3ce9750f79a73ac941a25304;p=friendica.git diff --git a/include/api.php b/include/api.php index 2d41cb986a..a70c18f3ca 100644 --- a/include/api.php +++ b/include/api.php @@ -223,7 +223,7 @@ function api_login(App $a) $record = $addon_auth['user_record']; } else { $user_id = User::authenticate(trim($user), trim($password)); - if ($user_id) { + if ($user_id !== false) { $record = dba::selectFirst('user', [], ['uid' => $user_id]); } } @@ -267,7 +267,7 @@ function api_check_method($method) * @brief Main API entry point * * @param object $a App - * @return string API call result + * @return string|array API call result */ function api_call(App $a) { @@ -387,9 +387,7 @@ function api_call(App $a) break; case "json": header("Content-Type: application/json"); - foreach ($return as $rr) { - $json = json_encode($rr); - } + $json = json_encode(end($return)); if (x($_GET, 'callback')) { $json = $_GET['callback'] . "(" . $json . ")"; } @@ -421,7 +419,7 @@ function api_call(App $a) * * @param string $type Return type (xml, json, rss, as) * @param object $e HTTPException Error object - * @return string error message formatted as $type + * @return string|array error message formatted as $type */ function api_error($type, $e) { @@ -531,7 +529,7 @@ function api_get_user(App $a, $contact_id = null) // Searching for contact id with uid = 0 if (!is_null($contact_id) && (intval($contact_id) != 0)) { - $user = dbesc(api_unique_id_to_nurl($contact_id)); + $user = dbesc(api_unique_id_to_nurl(intval($contact_id))); if ($user == "") { throw new BadRequestException("User not found."); @@ -577,16 +575,14 @@ function api_get_user(App $a, $contact_id = null) $argid = count($called_api); list($user, $null) = explode(".", $a->argv[$argid]); if (is_numeric($user)) { - $user = dbesc(api_unique_id_to_nurl($user)); - - if ($user == "") { - return false; - } + $user = dbesc(api_unique_id_to_nurl(intval($user))); - $url = $user; - $extra_query = "AND `contact`.`nurl` = '%s' "; - if (api_user() !== false) { - $extra_query .= "AND `contact`.`uid`=" . intval(api_user()); + if ($user != "") { + $url = $user; + $extra_query = "AND `contact`.`nurl` = '%s' "; + if (api_user() !== false) { + $extra_query .= "AND `contact`.`uid`=" . intval(api_user()); + } } } else { $user = dbesc($user); @@ -620,7 +616,9 @@ function api_get_user(App $a, $contact_id = null) ); // Selecting the id by priority, friendica first - api_best_nickname($uinfo); + if (is_array($uinfo)) { + api_best_nickname($uinfo); + } // if the contact wasn't found, fetch it from the contacts with uid = 0 if (!DBM::is_result($uinfo)) { @@ -647,6 +645,8 @@ function api_get_user(App $a, $contact_id = null) 'description' => $r[0]["about"], 'profile_image_url' => $r[0]["micro"], 'profile_image_url_https' => $r[0]["micro"], + 'profile_image_url_profile_size' => $r[0]["thumb"], + 'profile_image_url_large' => $r[0]["photo"], 'url' => $r[0]["url"], 'protected' => false, 'followers_count' => 0, @@ -782,6 +782,8 @@ function api_get_user(App $a, $contact_id = null) 'description' => $description, 'profile_image_url' => $uinfo[0]['micro'], 'profile_image_url_https' => $uinfo[0]['micro'], + 'profile_image_url_profile_size' => $uinfo[0]["thumb"], + 'profile_image_url_large' => $uinfo[0]["photo"], 'url' => $uinfo[0]['url'], 'protected' => false, 'followers_count' => intval($countfollowers), @@ -977,7 +979,7 @@ function api_create_xml($data, $root_element) * @param string $type Return type (atom, rss, xml, json) * @param array $data JSON style array * - * @return (string|object|array) XML data or JSON data + * @return (string|array) XML data or JSON data */ function api_format_data($root_element, $type, $data) { @@ -988,6 +990,7 @@ function api_format_data($root_element, $type, $data) $ret = api_create_xml($data, $root_element); break; case "json": + default: $ret = $data; break; } @@ -1425,7 +1428,7 @@ function api_status_show($type) $status_info["entities"] = $converted["entities"]; } - if (($lastwall['item_network'] != "") && ($status["source"] == 'web')) { + if (($lastwall['item_network'] != "") && ($status_info["source"] == 'web')) { $status_info["source"] = ContactSelector::networkToName($lastwall['item_network'], $user_info['url']); } elseif (($lastwall['item_network'] != "") && (ContactSelector::networkToName($lastwall['item_network'], $user_info['url']) != $status_info["source"])) { $status_info["source"] = trim($status_info["source"].' ('.ContactSelector::networkToName($lastwall['item_network'], $user_info['url']).')'); @@ -1434,15 +1437,15 @@ function api_status_show($type) // "uid" and "self" are only needed for some internal stuff, so remove it from here unset($status_info["user"]["uid"]); unset($status_info["user"]["self"]); - } - logger('status_info: '.print_r($status_info, true), LOGGER_DEBUG); + logger('status_info: '.print_r($status_info, true), LOGGER_DEBUG); - if ($type == "raw") { - return $status_info; - } + if ($type == "raw") { + return $status_info; + } - return api_format_data("statuses", $type, ['status' => $status_info]); + return api_format_data("statuses", $type, ['status' => $status_info]); + } } /** @@ -1626,6 +1629,13 @@ api_register_func('api/users/lookup', 'api_users_lookup', true); */ function api_search($type) { + $a = get_app(); + $user_info = api_get_user($a); + + if (api_user() === false || $user_info === false) { + throw new ForbiddenException(); + } + $data = []; $sql_extra = ''; @@ -1653,7 +1663,7 @@ function api_search($type) $r = dba::p( "SELECT ".item_fieldlists()." - FROM `item` ".item_joins()." + FROM `item` ".item_joins(api_user())." WHERE ".item_condition()." AND (`item`.`uid` = 0 OR (`item`.`uid` = ? AND NOT `item`.`global`)) AND `item`.`body` LIKE CONCAT('%',?,'%') $sql_extra @@ -1664,7 +1674,7 @@ function api_search($type) $since_id ); - $data['status'] = api_format_items(dba::inArray($r), api_get_user(get_app())); + $data['status'] = api_format_items(dba::inArray($r), $user_info); return api_format_data("statuses", $type, $data); } @@ -1686,8 +1696,9 @@ api_register_func('api/search', 'api_search', true); function api_statuses_home_timeline($type) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } @@ -1697,8 +1708,7 @@ function api_statuses_home_timeline($type) unset($_REQUEST["screen_name"]); unset($_GET["screen_name"]); - $user_info = api_get_user($a); - // get last newtork messages + // get last network messages // params $count = (x($_REQUEST, 'count') ? $_REQUEST['count'] : 20); @@ -1753,13 +1763,10 @@ function api_statuses_home_timeline($type) $idarray[] = intval($item["id"]); } - $idlist = implode(",", $idarray); - - if ($idlist != "") { - $unseen = q("SELECT `id` FROM `item` WHERE `unseen` AND `id` IN (%s)", $idlist); - + if (!empty($idarray)) { + $unseen = dba::exists('item', ['unseen' => true, 'id' => $idarray]); if ($unseen) { - q("UPDATE `item` SET `unseen` = 0 WHERE `unseen` AND `id` IN (%s)", $idlist); + Item::update(['unseen' => false], ['unseen' => true, 'id' => $idarray]); } } @@ -1788,13 +1795,13 @@ api_register_func('api/statuses/friends_timeline', 'api_statuses_home_timeline', function api_statuses_public_timeline($type) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } - $user_info = api_get_user($a); - // get last newtork messages + // get last network messages // params $count = (x($_REQUEST, 'count') ? $_REQUEST['count'] : 20); @@ -1820,7 +1827,7 @@ function api_statuses_public_timeline($type) "SELECT " . item_fieldlists() . " FROM `thread` STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - " . item_joins() . " + " . item_joins(api_user()) . " STRAIGHT_JOIN `user` ON `user`.`uid` = `thread`.`uid` AND NOT `user`.`hidewall` AND `verb` = ? @@ -1849,7 +1856,7 @@ function api_statuses_public_timeline($type) $r = dba::p( "SELECT " . item_fieldlists() . " FROM `item` - " . item_joins() . " + " . item_joins(api_user()) . " STRAIGHT_JOIN `user` ON `user`.`uid` = `item`.`uid` AND NOT `user`.`hidewall` AND `verb` = ? @@ -1897,13 +1904,12 @@ api_register_func('api/statuses/public_timeline', 'api_statuses_public_timeline' function api_statuses_networkpublic_timeline($type) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } - $user_info = api_get_user($a); - $since_id = x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0; $max_id = x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0; @@ -1924,7 +1930,7 @@ function api_statuses_networkpublic_timeline($type) "SELECT " . item_fieldlists() . " FROM `thread` STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - " . item_joins() . " + " . item_joins(api_user()) . " WHERE `thread`.`uid` = 0 AND `verb` = ? AND NOT `thread`.`private` @@ -1967,13 +1973,12 @@ api_register_func('api/statuses/networkpublic_timeline', 'api_statuses_networkpu function api_statuses_show($type) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } - $user_info = api_get_user($a); - // params $id = intval($a->argv[3]); @@ -1997,6 +2002,19 @@ function api_statuses_show($type) $sql_extra .= " AND `item`.`id` = %d"; } + // try to fetch the item for the local user - or the public item, if there is no local one + $uri_item = dba::selectFirst('item', ['uri'], ['id' => $id]); + if (!DBM::is_result($uri_item)) { + throw new BadRequestException("There is no status with this id."); + } + + $item = dba::selectFirst('item', ['id'], ['uri' => $uri_item['uri'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); + if (!DBM::is_result($item)) { + throw new BadRequestException("There is no status with this id."); + } + + $id = $item['id']; + $r = q( "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, @@ -2006,7 +2024,7 @@ function api_statuses_show($type) INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` AND (NOT `contact`.`blocked` OR `contact`.`pending`) WHERE `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - AND `item`.`uid` = %d AND `item`.`verb` = '%s' + AND `item`.`uid` IN (0, %d) AND `item`.`verb` = '%s' $sql_extra", intval(api_user()), dbesc(ACTIVITY_POST), @@ -2041,13 +2059,12 @@ api_register_func('api/statuses/show', 'api_statuses_show', true); function api_conversation_show($type) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } - $user_info = api_get_user($a); - // params $id = intval($a->argv[3]); $count = (x($_REQUEST, 'count') ? $_REQUEST['count'] : 20); @@ -2071,22 +2088,25 @@ function api_conversation_show($type) logger('API: api_conversation_show: '.$id); - $r = q("SELECT `parent` FROM `item` WHERE `id` = %d", intval($id)); - if (DBM::is_result($r)) { - $id = $r[0]["parent"]; + // try to fetch the item for the local user - or the public item, if there is no local one + $item = dba::selectFirst('item', ['parent-uri'], ['id' => $id]); + if (!DBM::is_result($item)) { + throw new BadRequestException("There is no status with this id."); } + $parent = dba::selectFirst('item', ['id'], ['uri' => $item['parent-uri'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); + if (!DBM::is_result($parent)) { + throw new BadRequestException("There is no status with this id."); + } + + $id = $parent['id']; + $sql_extra = ''; if ($max_id > 0) { $sql_extra = ' AND `item`.`id` <= ' . intval($max_id); } - // Not sure why this query was so complicated. We should keep it here for a while, - // just to make sure that we really don't need it. - // FROM `item` INNER JOIN (SELECT `uri`,`parent` FROM `item` WHERE `id` = %d) AS `temp1` - // ON (`item`.`thr-parent` = `temp1`.`uri` AND `item`.`parent` = `temp1`.`parent`) - $r = q( "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, @@ -2097,7 +2117,7 @@ function api_conversation_show($type) AND (NOT `contact`.`blocked` OR `contact`.`pending`) WHERE `item`.`parent` = %d AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - AND `item`.`uid` = %d AND `item`.`verb` = '%s' + AND `item`.`uid` IN (0, %d) AND `item`.`verb` = '%s' AND `item`.`id`>%d $sql_extra ORDER BY `item`.`id` DESC LIMIT %d ,%d", intval($id), @@ -2236,7 +2256,7 @@ function api_statuses_destroy($type) $ret = api_statuses_show($type); - Item::deleteById($id); + Item::deleteForUser(['id' => $id], api_user()); return $ret; } @@ -2254,8 +2274,9 @@ api_register_func('api/statuses/destroy', 'api_statuses_destroy', true, API_METH function api_statuses_mentions($type) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } @@ -2265,9 +2286,7 @@ function api_statuses_mentions($type) unset($_REQUEST["screen_name"]); unset($_GET["screen_name"]); - $user_info = api_get_user($a); - // get last newtork messages - + // get last network messages // params $since_id = defaults($_REQUEST, 'since_id', 0); @@ -2346,13 +2365,12 @@ api_register_func('api/statuses/replies', 'api_statuses_mentions', true); function api_statuses_user_timeline($type) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } - $user_info = api_get_user($a); - logger( "api_statuses_user_timeline: api_user: ". api_user() . "\nuser_info: ".print_r($user_info, true) . @@ -2516,15 +2534,14 @@ function api_favorites($type) global $called_api; $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } $called_api = []; - $user_info = api_get_user($a); - // in friendica starred item are private // return favorites only for self logger('api_favorites: self:' . $user_info['self']); @@ -3265,12 +3282,14 @@ function api_help_test($type) api_register_func('api/help/test', 'api_help_test', false); /** + * Returns all lists the user subscribes to. * * @param string $type Return type (atom, rss, xml, json) * * @return array|string + * @see https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/get-lists-list */ -function api_lists($type) +function api_lists_list($type) { $ret = []; /// @TODO $ret is not filled here? @@ -3278,25 +3297,140 @@ function api_lists($type) } /// @TODO move to top of file or somewhere better -api_register_func('api/lists', 'api_lists', true); +api_register_func('api/lists/list', 'api_lists_list', true); +api_register_func('api/lists/subscriptions', 'api_lists_list', true); /** - * Returns all lists the user subscribes to. + * Returns all groups the user owns. * * @param string $type Return type (atom, rss, xml, json) * * @return array|string - * @see https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/get-lists-list + * @see https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/get-lists-ownerships */ -function api_lists_list($type) +function api_lists_ownerships($type) { - $ret = []; - /// @TODO $ret is not filled here? - return api_format_data('lists', $type, ["lists_list" => $ret]); + $a = get_app(); + + if (api_user() === false) { + throw new ForbiddenException(); + } + + // params + $user_info = api_get_user($a); + $uid = $user_info['uid']; + + $groups = dba::select('group', [], ['deleted' => 0, 'uid' => $uid]); + + // loop through all groups + $lists = []; + foreach ($groups as $group) { + if ($group['visible']) { + $mode = 'public'; + } else { + $mode = 'private'; + } + $lists[] = [ + 'name' => $group['name'], + 'id' => intval($group['id']), + 'id_str' => (string) $group['id'], + 'user' => $user_info, + 'mode' => $mode + ]; + } + return api_format_data("lists", $type, ['lists' => ['lists' => $lists]]); } /// @TODO move to top of file or somewhere better -api_register_func('api/lists/list', 'api_lists_list', true); +api_register_func('api/lists/ownerships', 'api_lists_ownerships', true); + +/** + * Returns recent statuses from users in the specified group. + * + * @param string $type Return type (atom, rss, xml, json) + * + * @return array|string + * @see https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/get-lists-ownerships + */ +function api_lists_statuses($type) +{ + $a = get_app(); + + $user_info = api_get_user($a); + if (api_user() === false || $user_info === false) { + throw new ForbiddenException(); + } + + unset($_REQUEST["user_id"]); + unset($_GET["user_id"]); + + unset($_REQUEST["screen_name"]); + unset($_GET["screen_name"]); + + if (empty($_REQUEST['list_id'])) { + throw new BadRequestException('list_id not specified'); + } + + // params + $count = (x($_REQUEST, 'count') ? $_REQUEST['count'] : 20); + $page = (x($_REQUEST, 'page') ? $_REQUEST['page'] - 1 : 0); + if ($page < 0) { + $page = 0; + } + $since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0); + $max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0); + $exclude_replies = (x($_REQUEST, 'exclude_replies') ? 1 : 0); + $conversation_id = (x($_REQUEST, 'conversation_id') ? $_REQUEST['conversation_id'] : 0); + + $start = $page * $count; + + $sql_extra = ''; + if ($max_id > 0) { + $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + } + if ($exclude_replies > 0) { + $sql_extra .= ' AND `item`.`parent` = `item`.`id`'; + } + if ($conversation_id > 0) { + $sql_extra .= ' AND `item`.`parent` = ' . intval($conversation_id); + } + + $statuses = dba::p( + "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, + `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, + `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, + `contact`.`id` AS `cid`, `group_member`.`gid` + FROM `item` + STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` + AND (NOT `contact`.`blocked` OR `contact`.`pending`) + STRAIGHT_JOIN `group_member` ON `group_member`.`contact-id` = `item`.`contact-id` + WHERE `item`.`uid` = ? AND `verb` = ? + AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` + $sql_extra + AND `item`.`id`>? + AND `group_member`.`gid` = ? + ORDER BY `item`.`id` DESC LIMIT ".intval($start)." ,".intval($count), + api_user(), + ACTIVITY_POST, + $since_id, + $_REQUEST['list_id'] + ); + + $items = api_format_items(dba::inArray($statuses), $user_info, false, $type); + + $data = ['status' => $items]; + switch ($type) { + case "atom": + case "rss": + $data = api_rss_extra($a, $data, $user_info); + break; + } + + return api_format_data("statuses", $type, $data); +} + +/// @TODO move to top of file or somewhere better +api_register_func('api/lists/statuses', 'api_lists_statuses', true); /** * Considers friends and followers lists to be private and won't return @@ -3696,7 +3830,7 @@ api_register_func('api/direct_messages/new', 'api_direct_messages_new', true, AP * @brief delete a direct_message from mail table through api * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array * @see https://developer.twitter.com/en/docs/direct-messages/sending-and-receiving/api-reference/delete-message */ function api_direct_messages_destroy($type) @@ -3782,8 +3916,9 @@ api_register_func('api/direct_messages/destroy', 'api_direct_messages_destroy', function api_direct_messages_box($type, $box, $verbose) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } @@ -3807,7 +3942,6 @@ function api_direct_messages_box($type, $box, $verbose) unset($_REQUEST["screen_name"]); unset($_GET["screen_name"]); - $user_info = api_get_user($a); $profile_url = $user_info["url"]; // pagination @@ -3858,7 +3992,9 @@ function api_direct_messages_box($type, $box, $verbose) $sender = $user_info; } - $ret[] = api_format_messages($item, $recipient, $sender); + if (isset($recipient) && isset($sender)) { + $ret[] = api_format_messages($item, $recipient, $sender); + } } @@ -3976,7 +4112,7 @@ api_register_func('api/oauth/access_token', 'api_oauth_access_token', false); * @brief delete a complete photoalbum with all containing photos from database through api * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array */ function api_fr_photoalbum_delete($type) { @@ -4012,7 +4148,7 @@ function api_fr_photoalbum_delete($type) if (!DBM::is_result($photo_item)) { throw new InternalServerErrorException("problem with deleting items occured"); } - Item::deleteById($photo_item[0]['id']); + Item::deleteForUser(['id' => $photo_item[0]['id']], api_user()); } // now let's delete all photos from the album @@ -4031,7 +4167,7 @@ function api_fr_photoalbum_delete($type) * @brief update the name of the album for all photos of an album * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array */ function api_fr_photoalbum_update($type) { @@ -4050,21 +4186,11 @@ function api_fr_photoalbum_update($type) throw new BadRequestException("no new albumname specified"); } // check if album is existing - $r = q( - "SELECT `id` FROM `photo` WHERE `uid` = %d AND `album` = '%s'", - intval(api_user()), - dbesc($album) - ); - if (!DBM::is_result($r)) { + if (!dba::exists('photo', ['uid' => api_user(), 'album' => $album])) { throw new BadRequestException("album not available"); } // now let's update all photos to the albumname - $result = q( - "UPDATE `photo` SET `album` = '%s' WHERE `uid` = %d AND `album` = '%s'", - dbesc($album_new), - intval(api_user()), - dbesc($album) - ); + $result = dba::update('photo', ['album' => $album_new], ['uid' => api_user(), 'album' => $album]); // return success of updating or error message if ($result) { @@ -4080,7 +4206,7 @@ function api_fr_photoalbum_update($type) * @brief list all photos of the authenticated user * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array */ function api_fr_photos_list($type) { @@ -4126,7 +4252,7 @@ function api_fr_photos_list($type) * @brief upload a new photo or change an existing photo * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array */ function api_fr_photo_create_update($type) { @@ -4274,7 +4400,7 @@ function api_fr_photo_create_update($type) * @brief delete a single photo from the database through api * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array */ function api_fr_photo_delete($type) { @@ -4315,7 +4441,7 @@ function api_fr_photo_delete($type) } // 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::deleteById($photo_item[0]['id']); + Item::deleteForUser(['id' => $photo_item[0]['id']], 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]); @@ -4357,7 +4483,7 @@ function api_fr_photo_detail($type) * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' * - * @return string + * @return string|array * @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) @@ -4366,7 +4492,7 @@ function api_account_update_profile_image($type) throw new ForbiddenException(); } // input params - $profileid = defaults($_REQUEST, 'profile_id', 0); + $profile_id = defaults($_REQUEST, 'profile_id', 0); // error if image data is missing if (!x($_FILES, 'image')) { @@ -4374,17 +4500,13 @@ function api_account_update_profile_image($type) } // check if specified profile id is valid - if ($profileid != 0) { - $r = q( - "SELECT `id` FROM `profile` WHERE `uid` = %d AND `id` = %d", - intval(api_user()), - intval($profileid) - ); + 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 (!DBM::is_result($r)) { + if (!DBM::is_result($profile)) { throw new BadRequestException("profile_id not available"); } - $is_default_profile = $r['profile']; + $is_default_profile = $profile['is-default']; } else { $is_default_profile = 1; } @@ -4409,40 +4531,20 @@ function api_account_update_profile_image($type) $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) { - q( - "UPDATE `photo` SET `profile` = 0 WHERE `profile` = 1 AND `resource-id` != '%s' AND `uid` = %d", - dbesc($data['photo']['id']), - intval(local_user()) - ); - - q( - "UPDATE `contact` SET `photo` = '%s', `thumb` = '%s', `micro` = '%s' WHERE `self` AND `uid` = %d", - dbesc(System::baseUrl() . '/photo/' . $data['photo']['id'] . '-4.' . $fileext), - dbesc(System::baseUrl() . '/photo/' . $data['photo']['id'] . '-5.' . $fileext), - dbesc(System::baseUrl() . '/photo/' . $data['photo']['id'] . '-6.' . $fileext), - intval(local_user()) - ); + $condition = ["`profile` AND `resource-id` != ? AND `uid` = ?", $data['photo']['id'], api_user()]; + dba::update('photo', ['profile' => false], $condition); } else { - q( - "UPDATE `profile` SET `photo` = '%s', `thumb` = '%s' WHERE `id` = %d AND `uid` = %d", - dbesc(System::baseUrl() . '/photo/' . $data['photo']['id'] . '-4.' . $filetype), - dbesc(System::baseUrl() . '/photo/' . $data['photo']['id'] . '-5.' . $filetype), - intval($_REQUEST['profile']), - intval(local_user()) - ); + $fields = ['photo' => System::baseUrl() . '/photo/' . $data['photo']['id'] . '-4.' . $filetype, + 'thumb' => System::baseUrl() . '/photo/' . $data['photo']['id'] . '-5.' . $filetype]; + dba::update('profile', $fields, ['id' => $_REQUEST['profile'], 'uid' => api_user()]); } - // we'll set the updated profile-photo timestamp even if it isn't the default profile, - // so that browsers will do a cache update unconditionally - - q( - "UPDATE `contact` SET `avatar-date` = '%s' WHERE `self` = 1 AND `uid` = %d", - dbesc(DateTimeFormat::utcNow()), - intval(local_user()) - ); + Contact::updateSelfFromUserID(api_user(), true); // Update global directory in background //$user = api_get_user(get_app()); @@ -4689,7 +4791,7 @@ function save_media_to_database($mediatype, $media, $type, $album, $allow_cid, $ logger("photo upload: new profile image upload ended", LOGGER_DEBUG); } - if ($r) { + if (isset($r) && $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($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $filetype, $visibility); @@ -4765,6 +4867,13 @@ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $f */ function prepare_photo_data($type, $scale, $photo_id) { + $a = get_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, "); @@ -4845,7 +4954,7 @@ function prepare_photo_data($type, $scale, $photo_id) ); // prepare output of comments - $commentData = api_format_items($r, api_get_user(get_app()), false, $type); + $commentData = api_format_items($r, $user_info, false, $type); $comments = []; if ($type == "xml") { $k = 0; @@ -4915,15 +5024,9 @@ function api_friendica_remoteauth() $sec = random_string(); - q( - "INSERT INTO `profile_check` ( `uid`, `cid`, `dfrn_id`, `sec`, `expire`) - VALUES( %d, %s, '%s', '%s', %d )", - intval(api_user()), - intval($cid), - dbesc($dfrn_id), - dbesc($sec), - intval(time() + 45) - ); + $fields = ['uid' => api_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id, + 'sec' => $sec, 'expire' => time() + 45]; + dba::insert('profile_check', $fields); logger($contact['name'] . ' ' . $sec, LOGGER_DEBUG); $dest = ($url ? '&destination_url=' . $url : ''); @@ -5220,7 +5323,7 @@ function api_clean_attachments($body) { $data = BBCode::getAttachmentData($body); - if (!$data) { + if (empty($data)) { return $body; } $body = ""; @@ -5346,6 +5449,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']); $users = []; @@ -5432,15 +5536,15 @@ function api_friendica_group_delete($type) } api_register_func('api/friendica/group_delete', 'api_friendica_group_delete', true, API_METHOD_DELETE); - /** - * Create the specified group with the posted array of contacts. + * Delete a group. * * @param string $type Return type (atom, rss, xml, json) * * @return array|string + * @see https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/post-lists-destroy */ -function api_friendica_group_create($type) +function api_lists_destroy($type) { $a = get_app(); @@ -5450,11 +5554,45 @@ function api_friendica_group_create($type) // params $user_info = api_get_user($a); - $name = (x($_REQUEST, 'name') ? $_REQUEST['name'] : ""); + $gid = (x($_REQUEST, 'list_id') ? $_REQUEST['list_id'] : 0); $uid = $user_info['uid']; - $json = json_decode($_POST['json'], true); - $users = $json['user']; + // error if no gid specified + if ($gid == 0) { + throw new BadRequestException('gid not specified'); + } + + // get data of the specified group id + $group = dba::selectFirst('group', [], ['uid' => $uid, 'id' => $gid]); + // error message if specified gid is not in database + if (!$group) { + throw new BadRequestException('gid not available'); + } + + if (Group::remove($gid)) { + $list = [ + 'name' => $group['name'], + 'id' => intval($gid), + 'id_str' => (string) $gid, + 'user' => $user_info + ]; + + return api_format_data("lists", $type, ['lists' => $list]); + } +} +api_register_func('api/lists/destroy', 'api_lists_destroy', true, API_METHOD_DELETE); + +/** + * Add a new group to the database. + * + * @param string $name Group name + * @param int $uid User ID + * @param array $users List of users to add to the group + * + * @return array + */ +function group_create($name, $uid, $users = []) +{ // error if no name specified if ($name == "") { throw new BadRequestException('group name not specified'); @@ -5510,12 +5648,73 @@ function api_friendica_group_create($type) } // return success message incl. missing users in array - $status = ($erroraddinguser ? "missing user" : ($reactivate_group ? "reactivated" : "ok")); - $success = ['success' => true, 'gid' => $gid, 'name' => $name, 'status' => $status, 'wrong users' => $errorusers]; + $status = ($erroraddinguser ? "missing user" : ((isset($reactivate_group) && $reactivate_group) ? "reactivated" : "ok")); + + return ['success' => true, 'gid' => $gid, 'name' => $name, 'status' => $status, 'wrong users' => $errorusers]; +} + +/** + * Create the specified group with the posted array of contacts. + * + * @param string $type Return type (atom, rss, xml, json) + * + * @return array|string + */ +function api_friendica_group_create($type) +{ + $a = get_app(); + + if (api_user() === false) { + throw new ForbiddenException(); + } + + // params + $user_info = api_get_user($a); + $name = (x($_REQUEST, 'name') ? $_REQUEST['name'] : ""); + $uid = $user_info['uid']; + $json = json_decode($_POST['json'], true); + $users = $json['user']; + + $success = group_create($name, $uid, $users); + return api_format_data("group_create", $type, ['result' => $success]); } api_register_func('api/friendica/group_create', 'api_friendica_group_create', true, API_METHOD_POST); +/** + * Create a new group. + * + * @param string $type Return type (atom, rss, xml, json) + * + * @return array|string + * @see https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/post-lists-create + */ +function api_lists_create($type) +{ + $a = get_app(); + + if (api_user() === false) { + throw new ForbiddenException(); + } + + // params + $user_info = api_get_user($a); + $name = (x($_REQUEST, 'name') ? $_REQUEST['name'] : ""); + $uid = $user_info['uid']; + + $success = group_create($name, $uid); + if ($success['success']) { + $grp = [ + 'name' => $success['name'], + 'id' => intval($success['gid']), + 'id_str' => (string) $success['gid'], + 'user' => $user_info + ]; + + return api_format_data("lists", $type, ['lists'=>$grp]); + } +} +api_register_func('api/lists/create', 'api_lists_create', true, API_METHOD_POST); /** * Update the specified group with the posted array of contacts. @@ -5557,7 +5756,7 @@ function api_friendica_group_update($type) foreach ($users as $user) { $found = ($user['cid'] == $cid ? true : false); } - if (!$found) { + if (!isset($found) || !$found) { Group::removeMemberByName($uid, $name, $cid); } } @@ -5590,6 +5789,54 @@ function api_friendica_group_update($type) api_register_func('api/friendica/group_update', 'api_friendica_group_update', true, API_METHOD_POST); +/** + * Update information about a group. + * + * @param string $type Return type (atom, rss, xml, json) + * + * @return array|string + * @see https://developer.twitter.com/en/docs/accounts-and-users/create-manage-lists/api-reference/post-lists-update + */ +function api_lists_update($type) +{ + $a = get_app(); + + if (api_user() === false) { + throw new ForbiddenException(); + } + + // params + $user_info = api_get_user($a); + $gid = (x($_REQUEST, 'list_id') ? $_REQUEST['list_id'] : 0); + $name = (x($_REQUEST, 'name') ? $_REQUEST['name'] : ""); + $uid = $user_info['uid']; + + // error if no gid specified + if ($gid == 0) { + throw new BadRequestException('gid not specified'); + } + + // get data of the specified group id + $group = dba::selectFirst('group', [], ['uid' => $uid, 'id' => $gid]); + // error message if specified gid is not in database + if (!$group) { + throw new BadRequestException('gid not available'); + } + + if (Group::update($gid, $name)) { + $list = [ + 'name' => $name, + 'id' => intval($gid), + 'id_str' => (string) $gid, + 'user' => $user_info + ]; + + return api_format_data("lists", $type, ['lists' => $list]); + } +} + +api_register_func('api/lists/update', 'api_lists_update', true, API_METHOD_POST); + /** * * @param string $type Return type (atom, rss, xml, json) @@ -5638,7 +5885,7 @@ api_register_func('api/friendica/activity/unattendmaybe', 'api_friendica_activit * @brief Returns notifications * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array */ function api_friendica_notification($type) { @@ -5672,13 +5919,14 @@ function api_friendica_notification($type) * @brief Set notification as seen and returns associated item (if possible) * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array */ function api_friendica_notification_seen($type) { $a = get_app(); + $user_info = api_get_user($a); - if (api_user() === false) { + if (api_user() === false || $user_info === false) { throw new ForbiddenException(); } if ($a->argc!==4) { @@ -5703,7 +5951,6 @@ function api_friendica_notification_seen($type) ); if ($r!==false) { // we found the item, return it to the user - $user_info = api_get_user($a); $ret = api_format_items($r, $user_info, false, $type); $data = ['status' => $ret]; return api_format_data("status", $type, $data); @@ -5721,7 +5968,7 @@ api_register_func('api/friendica/notification', 'api_friendica_notification', tr * @brief update a direct_message to seen state * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string (success result=ok, error result=error with error message) + * @return string|array (success result=ok, error result=error with error message) */ function api_friendica_direct_messages_setseen($type) { @@ -5741,25 +5988,14 @@ function api_friendica_direct_messages_setseen($type) return api_format_data("direct_messages_setseen", $type, ['$result' => $answer]); } - // get data of the specified message id - $r = q( - "SELECT `id` FROM `mail` WHERE `id` = %d AND `uid` = %d", - intval($id), - intval($uid) - ); - // error message if specified id is not in database - if (!DBM::is_result($r)) { + if (!dba::exists('mail', ['id' => $id, 'uid' => $uid])) { $answer = ['result' => 'error', 'message' => 'message id not in database']; return api_format_data("direct_messages_setseen", $type, ['$result' => $answer]); } // update seen indicator - $result = q( - "UPDATE `mail` SET `seen` = 1 WHERE `id` = %d AND `uid` = %d", - intval($id), - intval($uid) - ); + $result = dba::update('mail', ['seen' => true], ['id' => $id]); if ($result) { // return success @@ -5779,7 +6015,7 @@ api_register_func('api/friendica/direct_messages_setseen', 'api_friendica_direct * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' * @param string $box - * @return string (success: success=true if found and search_result contains found messages, + * @return string|array (success: success=true if found and search_result contains found messages, * success=false if nothing was found, search_result='nothing found', * error: result=error with error message) */ @@ -5827,7 +6063,9 @@ function api_friendica_direct_messages_search($type, $box = "") $sender = $user_info; } - $ret[] = api_format_messages($item, $recipient, $sender); + if (isset($recipient) && isset($sender)) { + $ret[] = api_format_messages($item, $recipient, $sender); + } } $success = ['success' => true, 'search_results' => $ret]; } @@ -5842,7 +6080,7 @@ api_register_func('api/friendica/direct_messages_search', 'api_friendica_direct_ * @brief return data of all the profiles a user has to the client * * @param string $type Known types are 'atom', 'rss', 'xml' and 'json' - * @return string + * @return string|array */ function api_friendica_profile_show($type) { @@ -5853,18 +6091,18 @@ function api_friendica_profile_show($type) } // input params - $profileid = (x($_REQUEST, 'profile_id') ? $_REQUEST['profile_id'] : 0); + $profile_id = (x($_REQUEST, 'profile_id') ? $_REQUEST['profile_id'] : 0); // retrieve general information about profiles for user $multi_profiles = Feature::isEnabled(api_user(), 'multi_profiles'); $directory = Config::get('system', 'directory'); // get data of the specified profile id or all profiles of the user if not specified - if ($profileid != 0) { + if ($profile_id != 0) { $r = q( "SELECT * FROM `profile` WHERE `uid` = %d AND `id` = %d", intval(api_user()), - intval($profileid) + intval($profile_id) ); // error message if specified gid is not in database @@ -5879,19 +6117,20 @@ function api_friendica_profile_show($type) } // loop through all returned profiles and retrieve data and users $k = 0; + $profiles = []; foreach ($r as $rr) { $profile = api_format_items_profiles($rr); // select all users from contact table, loop and prepare standard return for user data $users = []; - $r = q( + $nurls = q( "SELECT `id`, `nurl` FROM `contact` WHERE `uid`= %d AND `profile-id` = %d", intval(api_user()), intval($rr['profile_id']) ); - foreach ($r as $rr) { - $user = api_get_user($a, $rr['nurl']); + foreach ($nurls as $nurl) { + $user = api_get_user($a, $nurl['nurl']); ($type == "xml") ? $users[$k++ . ":user"] = $user : $users[] = $user; } $profile['users'] = $users;