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