Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

223 lines
6.1 KiB

  1. <?php
  2. /**
  3. * @copyright Copyright (C) 2020, Friendica
  4. *
  5. * @license GNU AGPL version 3 or any later version
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as
  9. * published by the Free Software Foundation, either version 3 of the
  10. * License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. *
  20. */
  21. namespace Friendica\Module\Api\Twitter;
  22. use Friendica\Database\DBA;
  23. use Friendica\DI;
  24. use Friendica\Model\Profile;
  25. use Friendica\Model\User;
  26. use Friendica\Module\BaseApi;
  27. use Friendica\Model\Contact;
  28. use Friendica\Network\HTTPException;
  29. use Friendica\Util\Strings;
  30. abstract class ContactEndpoint extends BaseApi
  31. {
  32. const DEFAULT_COUNT = 20;
  33. const MAX_COUNT = 200;
  34. public static function init(array $parameters = [])
  35. {
  36. parent::init($parameters);
  37. if (!self::login()) {
  38. throw new HTTPException\UnauthorizedException();
  39. }
  40. }
  41. /**
  42. * Computes the uid from the contact_id + screen_name parameters
  43. *
  44. * @param int|null $contact_id
  45. * @param string $screen_name
  46. * @return int
  47. * @throws HTTPException\NotFoundException
  48. */
  49. protected static function getUid(int $contact_id = null, string $screen_name = null)
  50. {
  51. $uid = self::$current_user_id;
  52. if ($contact_id || $screen_name) {
  53. // screen_name trumps user_id when both are provided
  54. if (!$screen_name) {
  55. $contact = Contact::getById($contact_id, ['nick', 'url']);
  56. // We don't have the followers of remote accounts so we check for locality
  57. if (empty($contact) || !Strings::startsWith($contact['url'], DI::baseUrl()->get())) {
  58. throw new HTTPException\NotFoundException(DI::l10n()->t('Contact not found'));
  59. }
  60. $screen_name = $contact['nick'];
  61. }
  62. $user = User::getByNickname($screen_name, ['uid']);
  63. if (empty($user)) {
  64. throw new HTTPException\NotFoundException(DI::l10n()->t('User not found'));
  65. }
  66. $uid = $user['uid'];
  67. }
  68. return $uid;
  69. }
  70. /**
  71. * This methods expands the contact ids into full user objects in an existing result set.
  72. *
  73. * @param mixed $rel A relationship constant or a list of them
  74. * @param int $uid The local user id we query the contacts from
  75. * @param int $cursor
  76. * @param int $count
  77. * @param bool $skip_status
  78. * @param bool $include_user_entities
  79. * @return array
  80. * @throws HTTPException\InternalServerErrorException
  81. * @throws HTTPException\NotFoundException
  82. * @throws \ImagickException
  83. */
  84. protected static function list($rel, int $uid, int $cursor = -1, int $count = self::DEFAULT_COUNT, bool $skip_status = false, bool $include_user_entities = true)
  85. {
  86. $return = self::ids($rel, $uid, $cursor, $count);
  87. $users = [];
  88. foreach ($return['ids'] as $contactId) {
  89. $users[] = DI::twitterUser()->createFromContactId($contactId, $uid, $skip_status, $include_user_entities);
  90. }
  91. unset($return['ids']);
  92. $return['users'] = $users;
  93. $return = [
  94. 'users' => $users,
  95. 'next_cursor' => $return['next_cursor'],
  96. 'next_cursor_str' => $return['next_cursor_str'],
  97. 'previous_cursor' => $return['previous_cursor'],
  98. 'previous_cursor_str' => $return['previous_cursor_str'],
  99. 'total_count' => $return['total_count'],
  100. ];
  101. return $return;
  102. }
  103. /**
  104. * @param mixed $rel A relationship constant or a list of them
  105. * @param int $uid The local user id we query the contacts from
  106. * @param int $cursor
  107. * @param int $count
  108. * @param bool $stringify_ids
  109. * @return array
  110. * @throws HTTPException\NotFoundException
  111. */
  112. protected static function ids($rel, int $uid, int $cursor = -1, int $count = self::DEFAULT_COUNT, bool $stringify_ids = false)
  113. {
  114. $hide_friends = false;
  115. if ($uid != self::$current_user_id) {
  116. $profile = Profile::getByUID($uid);
  117. if (empty($profile)) {
  118. throw new HTTPException\NotFoundException(DI::l10n()->t('Profile not found'));
  119. }
  120. $hide_friends = (bool)$profile['hide-friends'];
  121. }
  122. $ids = [];
  123. $next_cursor = 0;
  124. $previous_cursor = 0;
  125. $total_count = 0;
  126. if (!$hide_friends) {
  127. $condition = DBA::collapseCondition([
  128. 'rel' => $rel,
  129. 'uid' => $uid,
  130. 'self' => false,
  131. 'deleted' => false,
  132. 'hidden' => false,
  133. 'archive' => false,
  134. 'pending' => false
  135. ]);
  136. $total_count = DBA::count('contact', $condition);
  137. if ($cursor !== -1) {
  138. if ($cursor > 0) {
  139. $condition[0] .= " AND `id` > ?";
  140. $condition[] = $cursor;
  141. } else {
  142. $condition[0] .= " AND `id` < ?";
  143. $condition[] = -$cursor;
  144. }
  145. }
  146. $contacts = Contact::selectToArray(['id'], $condition, ['limit' => $count, 'order' => ['id']]);
  147. // Contains user-specific contact ids
  148. $ids = array_column($contacts, 'id');
  149. // Cursor is on the user-specific contact id since it's the sort field
  150. if (count($ids)) {
  151. $previous_cursor = -$ids[0];
  152. $next_cursor = $ids[count($ids) -1];
  153. }
  154. // No next page
  155. if ($total_count <= count($contacts) || count($contacts) < $count) {
  156. $next_cursor = 0;
  157. }
  158. // End of results
  159. if ($cursor < 0 && count($contacts) === 0) {
  160. $next_cursor = -1;
  161. }
  162. // No previous page
  163. if ($cursor === -1) {
  164. $previous_cursor = 0;
  165. }
  166. if ($cursor > 0 && count($contacts) === 0) {
  167. $previous_cursor = -$cursor;
  168. }
  169. if ($cursor < 0 && count($contacts) === 0) {
  170. $next_cursor = -1;
  171. }
  172. // Conversion to public contact ids
  173. array_walk($ids, function (&$contactId) use ($uid, $stringify_ids) {
  174. $cdata = Contact::getPublicAndUserContacID($contactId, $uid);
  175. if ($stringify_ids) {
  176. $contactId = (string)$cdata['public'];
  177. } else {
  178. $contactId = (int)$cdata['public'];
  179. }
  180. });
  181. }
  182. $return = [
  183. 'ids' => $ids,
  184. 'next_cursor' => $next_cursor,
  185. 'next_cursor_str' => (string)$next_cursor,
  186. 'previous_cursor' => $previous_cursor,
  187. 'previous_cursor_str' => (string)$previous_cursor,
  188. 'total_count' => $total_count,
  189. ];
  190. return $return;
  191. }
  192. }