3 * @copyright Copyright (C) 2020, Friendica
5 * @license GNU AGPL version 3 or any later version
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.
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.
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/>.
22 namespace Friendica\Model;
24 use Friendica\BaseModel;
25 use Friendica\Core\Protocol;
26 use Friendica\Database\Database;
27 use Friendica\Network\HTTPException;
28 use Friendica\Protocol\ActivityPub;
29 use Friendica\Protocol\Diaspora;
30 use Friendica\Repository;
31 use Friendica\Util\DateTimeFormat;
32 use Psr\Log\LoggerInterface;
37 * @property int contact-id
38 * @property bool knowyou
39 * @property bool duplex
40 * @property string note
41 * @property string hash
42 * @property string datetime
43 * @property bool blocked
44 * @property bool ignore
46 class Introduction extends BaseModel
48 /** @var Repository\Introduction */
51 public function __construct(Database $dba, LoggerInterface $logger, Repository\Introduction $intro, array $data = [])
53 parent::__construct($dba, $logger, $data);
55 $this->intro = $intro;
59 * Confirms a follow request and sends a notice to the remote contact.
61 * @param bool $duplex Is it a follow back?
62 * @param bool|null $hidden Should this contact be hidden? null = no change
64 * @throws HTTPException\InternalServerErrorException
65 * @throws HTTPException\NotFoundException
66 * @throws \ImagickException
68 public function confirm(bool $duplex = false, bool $hidden = null)
70 $this->logger->info('Confirming follower', ['cid' => $this->{'contact-id'}]);
72 $contact = Contact::selectFirst([], ['id' => $this->{'contact-id'}, 'uid' => $this->uid]);
75 throw new HTTPException\NotFoundException('Contact record not found.');
78 $newRelation = $contact['rel'];
79 $writable = $contact['writable'];
81 if (!empty($contact['protocol'])) {
82 $protocol = $contact['protocol'];
84 $protocol = $contact['network'];
87 if ($protocol == Protocol::ACTIVITYPUB) {
88 ActivityPub\Transmitter::sendContactAccept($contact['url'], $contact['hub-verify'], $contact['uid']);
91 if (in_array($protocol, [Protocol::DIASPORA, Protocol::ACTIVITYPUB])) {
93 $newRelation = Contact::FRIEND;
95 $newRelation = Contact::FOLLOWER;
98 if ($newRelation != Contact::FOLLOWER) {
104 'name-date' => DateTimeFormat::utcNow(),
105 'uri-date' => DateTimeFormat::utcNow(),
108 'protocol' => $protocol,
109 'writable' => $writable,
110 'hidden' => $hidden ?? $contact['hidden'],
111 'rel' => $newRelation,
113 $this->dba->update('contact', $fields, ['id' => $contact['id']]);
115 array_merge($contact, $fields);
117 if ($newRelation == Contact::FRIEND) {
118 if ($protocol == Protocol::DIASPORA) {
119 $ret = Diaspora::sendShare(User::getById($contact['uid']), $contact);
120 $this->logger->info('share returns', ['return' => $ret]);
121 } elseif ($protocol == Protocol::ACTIVITYPUB) {
122 ActivityPub\Transmitter::sendActivity('Follow', $contact['url'], $contact['uid']);
126 return $this->intro->delete($this);
130 * Silently ignores the introduction, hides it from notifications and prevents the remote contact from submitting
131 * additional follow requests.
136 public function ignore()
138 $this->ignore = true;
140 return $this->intro->update($this);
144 * Discards the introduction and sends a rejection message to AP contacts.
147 * @throws HTTPException\InternalServerErrorException
148 * @throws HTTPException\NotFoundException
149 * @throws \ImagickException
151 public function discard()
153 // If it is a friend suggestion, the contact is not a new friend but an existing friend
154 // that should not be deleted.
156 // When the contact entry had been created just for that intro, we want to get rid of it now
157 $condition = ['id' => $this->{'contact-id'}, 'uid' => $this->uid,
158 'self' => false, 'pending' => true, 'rel' => [0, Contact::FOLLOWER]];
159 if ($this->dba->exists('contact', $condition)) {
160 Contact::remove($this->{'contact-id'});
162 $this->dba->update('contact', ['pending' => false], ['id' => $this->{'contact-id'}]);
166 $contact = Contact::selectFirst([], ['id' => $this->{'contact-id'}, 'uid' => $this->uid]);
169 throw new HTTPException\NotFoundException('Contact record not found.');
172 if (!empty($contact['protocol'])) {
173 $protocol = $contact['protocol'];
175 $protocol = $contact['network'];
178 if ($protocol == Protocol::ACTIVITYPUB) {
179 ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $contact['uid']);
182 return $this->intro->delete($this);