]> git.mxchange.org Git - friendica.git/blob - src/Protocol/Diaspora/Repository/DiasporaContact.php
Merge pull request #12379 from annando/id-for-image
[friendica.git] / src / Protocol / Diaspora / Repository / DiasporaContact.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2022, the Friendica project
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
22 namespace Friendica\Protocol\Diaspora\Repository;
23
24 use Friendica\BaseRepository;
25 use Friendica\Core\System;
26 use Friendica\Database\Database;
27 use Friendica\Database\Definition\DbaDefinition;
28 use Friendica\Model\APContact;
29 use Friendica\Model\Contact;
30 use Friendica\Model\Item;
31 use Friendica\Model\ItemURI;
32 use Friendica\Network\HTTPException;
33 use Friendica\Protocol\Diaspora\Entity;
34 use Friendica\Protocol\Diaspora\Factory;
35 use Friendica\Protocol\WebFingerUri;
36 use Friendica\Util\DateTimeFormat;
37 use Psr\Http\Message\UriInterface;
38 use Psr\Log\LoggerInterface;
39
40 class DiasporaContact extends BaseRepository
41 {
42         const ALWAYS_UPDATE                 = true;
43         const NEVER_UPDATE                  = false;
44         const UPDATE_IF_MISSING_OR_OUTDATED = null;
45
46         protected static $table_name = 'diaspora-contact-view';
47
48         /** @var Factory\DiasporaContact */
49         protected $factory;
50         /** @var DbaDefinition */
51         private $definition;
52
53         public function __construct(DbaDefinition $definition, Database $database, LoggerInterface $logger, Factory\DiasporaContact $factory)
54         {
55                 parent::__construct($database, $logger, $factory);
56
57                 $this->definition = $definition;
58         }
59
60         /**
61          * @param array $condition
62          * @param array $params
63          * @return Entity\DiasporaContact
64          * @throws HTTPException\NotFoundException
65          */
66         public function selectOne(array $condition, array $params = []): Entity\DiasporaContact
67         {
68                 return parent::_selectOne($condition, $params);
69         }
70
71         /**
72          * @param int $uriId
73          * @return Entity\DiasporaContact
74          * @throws HTTPException\NotFoundException
75          */
76         public function selectOneByUriId(int $uriId): Entity\DiasporaContact
77         {
78                 return $this->selectOne(['uri-id' => $uriId]);
79         }
80
81         /**
82          * @param UriInterface $uri
83          * @return Entity\DiasporaContact
84          * @throws HTTPException\NotFoundException
85          */
86         public function selectOneByUri(UriInterface $uri): Entity\DiasporaContact
87         {
88                 try {
89                         return $this->selectOne(['url' => (string) $uri]);
90                 } catch (HTTPException\NotFoundException $e) {
91                 }
92
93                 try {
94                         return $this->selectOne(['addr' => (string) $uri]);
95                 } catch (HTTPException\NotFoundException $e) {
96                 }
97
98                 return $this->selectOne(['alias' => (string) $uri]);
99         }
100
101         /**
102          * @param WebFingerUri $uri
103          * @return Entity\DiasporaContact
104          * @throws HTTPException\NotFoundException
105          */
106         public function selectOneByAddr(WebFingerUri $uri): Entity\DiasporaContact
107         {
108                 return $this->selectOne(['addr' => $uri->getAddr()]);
109         }
110
111         /**
112          * @param int $uriId
113          * @return bool
114          * @throws \Exception
115          */
116         public function existsByUriId(int $uriId): bool
117         {
118                 return $this->db->exists(self::$table_name, ['uri-id' => $uriId]);
119         }
120
121         public function save(Entity\DiasporaContact $DiasporaContact): Entity\DiasporaContact
122         {
123                 $uriId = $DiasporaContact->uriId ?? ItemURI::insert(['uri' => $DiasporaContact->url, 'guid' => $DiasporaContact->guid]);
124
125                 $fields = [
126                         'uri-id'            => $uriId,
127                         'addr'              => $DiasporaContact->addr,
128                         'alias'             => (string)$DiasporaContact->alias,
129                         'nick'              => $DiasporaContact->nick,
130                         'name'              => $DiasporaContact->name,
131                         'given-name'        => $DiasporaContact->givenName,
132                         'family-name'       => $DiasporaContact->familyName,
133                         'photo'             => (string)$DiasporaContact->photo,
134                         'photo-medium'      => (string)$DiasporaContact->photoMedium,
135                         'photo-small'       => (string)$DiasporaContact->photoSmall,
136                         'batch'             => (string)$DiasporaContact->batch,
137                         'notify'            => (string)$DiasporaContact->notify,
138                         'poll'              => (string)$DiasporaContact->poll,
139                         'subscribe'         => (string)$DiasporaContact->subscribe,
140                         'searchable'        => $DiasporaContact->searchable,
141                         'pubkey'            => $DiasporaContact->pubKey,
142                         'gsid'              => $DiasporaContact->gsid,
143                         'created'           => $DiasporaContact->created->format(DateTimeFormat::MYSQL),
144                         'updated'           => DateTimeFormat::utcNow(),
145                         'interacting_count' => $DiasporaContact->interacting_count,
146                         'interacted_count'  => $DiasporaContact->interacted_count,
147                         'post_count'        => $DiasporaContact->post_count,
148                 ];
149
150                 // Limit the length on incoming fields
151                 $fields = $this->definition->truncateFieldsForTable('diaspora-contact', $fields);
152
153                 $this->db->insert('diaspora-contact', $fields, Database::INSERT_UPDATE);
154
155                 return $this->selectOneByUriId($uriId);
156         }
157
158         /**
159          * Fetch a Diaspora profile from a given WebFinger address and updates it depending on the mode
160          *
161          * @param WebFingerUri $uri    Profile address
162          * @param boolean      $update true = always update, false = never update, null = update when not found or outdated
163          * @return Entity\DiasporaContact
164          * @throws HTTPException\NotFoundException
165          */
166         public function getByAddr(WebFingerUri $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
167         {
168                 if ($update !== self::ALWAYS_UPDATE) {
169                         try {
170                                 $dcontact = $this->selectOneByAddr($uri);
171                                 if ($update === self::NEVER_UPDATE) {
172                                         return $dcontact;
173                                 }
174                         } catch (HTTPException\NotFoundException $e) {
175                                 if ($update === self::NEVER_UPDATE) {
176                                         throw $e;
177                                 }
178
179                                 // This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
180                                 // but we still need the result of a probe to create the missing diaspora-contact record.
181                                 $update = self::ALWAYS_UPDATE;
182                         }
183                 }
184
185                 $contact = Contact::getByURL($uri, $update, ['uri-id']);
186                 if (empty($contact['uri-id'])) {
187                         throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
188                 }
189
190                 return self::selectOneByUriId($contact['uri-id']);
191         }
192
193         /**
194          * Fetch a Diaspora profile from a given profile URL and updates it depending on the mode
195          *
196          * @param UriInterface $uri    Profile URL
197          * @param boolean      $update true = always update, false = never update, null = update when not found or outdated
198          * @return Entity\DiasporaContact
199          * @throws HTTPException\NotFoundException
200          */
201         public function getByUrl(UriInterface $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
202         {
203                 if ($update !== self::ALWAYS_UPDATE) {
204                         try {
205                                 $dcontact = $this->selectOneByUriId(ItemURI::getIdByURI($uri));
206                                 if ($update === self::NEVER_UPDATE) {
207                                         return $dcontact;
208                                 }
209                         } catch (HTTPException\NotFoundException $e) {
210                                 if ($update === self::NEVER_UPDATE) {
211                                         throw $e;
212                                 }
213
214                                 // This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
215                                 // but we still need the result of a probe to create the missing diaspora-contact record.
216                                 $update = self::ALWAYS_UPDATE;
217                         }
218                 }
219
220                 $contact = Contact::getByURL($uri, $update, ['uri-id']);
221                 if (empty($contact['uri-id'])) {
222                         throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
223                 }
224
225                 return self::selectOneByUriId($contact['uri-id']);
226         }
227
228         /**
229          * Update or create a diaspora-contact entry via a probe array
230          *
231          * @param array $data Probe array
232          * @return Entity\DiasporaContact
233          * @throws \Exception
234          */
235         public function updateFromProbeArray(array $data): Entity\DiasporaContact
236         {
237                 if (empty($data['url'])) {
238                         throw new \InvalidArgumentException('Missing url key in Diaspora probe data array');
239                 }
240
241                 if (empty($data['guid'])) {
242                         throw new \InvalidArgumentException('Missing guid key in Diaspora probe data array');
243                 }
244
245                 if (empty($data['pubkey'])) {
246                         throw new \InvalidArgumentException('Missing pubkey key in Diaspora probe data array');
247                 }
248
249                 $uriId = ItemURI::insert(['uri' => $data['url'], 'guid' => $data['guid']]);
250
251                 $contact   = Contact::getByUriId($uriId, ['id', 'created']);
252                 $apcontact = APContact::getByURL($data['url'], false);
253                 if (!empty($apcontact)) {
254                         $interacting_count = $apcontact['followers_count'];
255                         $interacted_count  = $apcontact['following_count'];
256                         $post_count        = $apcontact['statuses_count'];
257                 } elseif (!empty($contact['id'])) {
258                         $last_interaction = DateTimeFormat::utc('now - 180 days');
259
260                         $interacting_count = $this->db->count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
261                         $interacted_count  = $this->db->count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
262                         $post_count        = $this->db->count('post', ['author-id' => $contact['id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]]);
263                 }
264
265                 $DiasporaContact = $this->factory->createfromProbeData(
266                         $data,
267                         $uriId,
268                         new \DateTime($contact['created'] ?? 'now', new \DateTimeZone('UTC')),
269                         $interacting_count ?? 0,
270                         $interacted_count ?? 0,
271                         $post_count ?? 0
272                 );
273
274                 $DiasporaContact = $this->save($DiasporaContact);
275
276                 $this->logger->info('Updated diaspora-contact', ['url' => (string) $DiasporaContact->url, 'callstack' => System::callstack(20)]);
277
278                 return $DiasporaContact;
279         }
280
281         /**
282          * get a url (scheme://domain.tld/u/user) from a given contact guid
283          *
284          * @param mixed $guid Hexadecimal string guid
285          *
286          * @return string the contact url or null
287          * @throws \Exception
288          */
289         public function getUrlByGuid(string $guid): ?string
290         {
291                 $diasporaContact = $this->db->selectFirst(self::$table_name, ['url'], ['guid' => $guid]);
292
293                 return $diasporaContact['url'] ?? null;
294         }
295 }