283 lines
9.5 KiB
PHP
283 lines
9.5 KiB
PHP
<?php
|
|
/**
|
|
* @copyright Copyright (C) 2010-2022, the Friendica project
|
|
*
|
|
* @license GNU AGPL version 3 or any later version
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
namespace Friendica\Protocol\Diaspora\Repository;
|
|
|
|
use Friendica\BaseRepository;
|
|
use Friendica\Core\System;
|
|
use Friendica\Database\Database;
|
|
use Friendica\Database\Definition\DbaDefinition;
|
|
use Friendica\Model\APContact;
|
|
use Friendica\Model\Contact;
|
|
use Friendica\Model\Item;
|
|
use Friendica\Model\ItemURI;
|
|
use Friendica\Network\HTTPException;
|
|
use Friendica\Protocol\Diaspora\Entity;
|
|
use Friendica\Protocol\Diaspora\Factory;
|
|
use Friendica\Protocol\WebFingerUri;
|
|
use Friendica\Util\DateTimeFormat;
|
|
use Psr\Http\Message\UriInterface;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class DiasporaContact extends BaseRepository
|
|
{
|
|
const ALWAYS_UPDATE = true;
|
|
const NEVER_UPDATE = false;
|
|
const UPDATE_IF_MISSING_OR_OUTDATED = null;
|
|
|
|
protected static $table_name = 'diaspora-contact-view';
|
|
|
|
/** @var Factory\DiasporaContact */
|
|
protected $factory;
|
|
/** @var DbaDefinition */
|
|
private $definition;
|
|
|
|
public function __construct(DbaDefinition $definition, Database $database, LoggerInterface $logger, Factory\DiasporaContact $factory)
|
|
{
|
|
parent::__construct($database, $logger, $factory);
|
|
|
|
$this->definition = $definition;
|
|
}
|
|
|
|
/**
|
|
* @param array $condition
|
|
* @param array $params
|
|
* @return Entity\DiasporaContact
|
|
* @throws HTTPException\NotFoundException
|
|
*/
|
|
public function selectOne(array $condition, array $params = []): Entity\DiasporaContact
|
|
{
|
|
return parent::_selectOne($condition, $params);
|
|
}
|
|
|
|
/**
|
|
* @param int $uriId
|
|
* @return Entity\DiasporaContact
|
|
* @throws HTTPException\NotFoundException
|
|
*/
|
|
public function selectOneByUriId(int $uriId): Entity\DiasporaContact
|
|
{
|
|
return $this->selectOne(['uri-id' => $uriId]);
|
|
}
|
|
|
|
/**
|
|
* @param UriInterface $uri
|
|
* @return Entity\DiasporaContact
|
|
* @throws HTTPException\NotFoundException
|
|
*/
|
|
public function selectOneByUri(UriInterface $uri): Entity\DiasporaContact
|
|
{
|
|
try {
|
|
return $this->selectOne(['url' => (string) $uri]);
|
|
} catch (HTTPException\NotFoundException $e) {
|
|
}
|
|
|
|
try {
|
|
return $this->selectOne(['addr' => (string) $uri]);
|
|
} catch (HTTPException\NotFoundException $e) {
|
|
}
|
|
|
|
return $this->selectOne(['alias' => (string) $uri]);
|
|
}
|
|
|
|
/**
|
|
* @param WebFingerUri $uri
|
|
* @return Entity\DiasporaContact
|
|
* @throws HTTPException\NotFoundException
|
|
*/
|
|
public function selectOneByAddr(WebFingerUri $uri): Entity\DiasporaContact
|
|
{
|
|
return $this->selectOne(['addr' => $uri->getAddr()]);
|
|
}
|
|
|
|
/**
|
|
* @param int $uriId
|
|
* @return bool
|
|
* @throws \Exception
|
|
*/
|
|
public function existsByUriId(int $uriId): bool
|
|
{
|
|
return $this->db->exists(self::$table_name, ['uri-id' => $uriId]);
|
|
}
|
|
|
|
public function save(Entity\DiasporaContact $DiasporaContact): Entity\DiasporaContact
|
|
{
|
|
$uriId = $DiasporaContact->uriId ?? ItemURI::insert(['uri' => $DiasporaContact->url, 'guid' => $DiasporaContact->guid]);
|
|
|
|
$fields = [
|
|
'uri-id' => $uriId,
|
|
'addr' => $DiasporaContact->addr,
|
|
'alias' => (string)$DiasporaContact->alias,
|
|
'nick' => $DiasporaContact->nick,
|
|
'name' => $DiasporaContact->name,
|
|
'given-name' => $DiasporaContact->givenName,
|
|
'family-name' => $DiasporaContact->familyName,
|
|
'photo' => (string)$DiasporaContact->photo,
|
|
'photo-medium' => (string)$DiasporaContact->photoMedium,
|
|
'photo-small' => (string)$DiasporaContact->photoSmall,
|
|
'batch' => (string)$DiasporaContact->batch,
|
|
'notify' => (string)$DiasporaContact->notify,
|
|
'poll' => (string)$DiasporaContact->poll,
|
|
'subscribe' => (string)$DiasporaContact->subscribe,
|
|
'searchable' => $DiasporaContact->searchable,
|
|
'pubkey' => $DiasporaContact->pubKey,
|
|
'gsid' => $DiasporaContact->gsid,
|
|
'created' => $DiasporaContact->created->format(DateTimeFormat::MYSQL),
|
|
'updated' => DateTimeFormat::utcNow(),
|
|
'interacting_count' => $DiasporaContact->interacting_count,
|
|
'interacted_count' => $DiasporaContact->interacted_count,
|
|
'post_count' => $DiasporaContact->post_count,
|
|
];
|
|
|
|
// Limit the length on incoming fields
|
|
$fields = $this->definition->truncateFieldsForTable('diaspora-contact', $fields);
|
|
|
|
$this->db->insert('diaspora-contact', $fields, Database::INSERT_UPDATE);
|
|
|
|
return $this->selectOneByUriId($uriId);
|
|
}
|
|
|
|
/**
|
|
* Fetch a Diaspora profile from a given WebFinger address and updates it depending on the mode
|
|
*
|
|
* @param WebFingerUri $uri Profile address
|
|
* @param boolean $update true = always update, false = never update, null = update when not found or outdated
|
|
* @return Entity\DiasporaContact
|
|
* @throws HTTPException\NotFoundException
|
|
*/
|
|
public function getByAddr(WebFingerUri $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
|
|
{
|
|
if ($update !== self::ALWAYS_UPDATE) {
|
|
try {
|
|
$dcontact = $this->selectOneByAddr($uri);
|
|
if ($update === self::NEVER_UPDATE) {
|
|
return $dcontact;
|
|
}
|
|
} catch (HTTPException\NotFoundException $e) {
|
|
if ($update === self::NEVER_UPDATE) {
|
|
throw $e;
|
|
}
|
|
|
|
// This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
|
|
// but we still need the result of a probe to create the missing diaspora-contact record.
|
|
$update = self::ALWAYS_UPDATE;
|
|
}
|
|
}
|
|
|
|
$contact = Contact::getByURL($uri, $update, ['uri-id']);
|
|
if (empty($contact['uri-id'])) {
|
|
throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
|
|
}
|
|
|
|
return self::selectOneByUriId($contact['uri-id']);
|
|
}
|
|
|
|
/**
|
|
* Fetch a Diaspora profile from a given profile URL and updates it depending on the mode
|
|
*
|
|
* @param UriInterface $uri Profile URL
|
|
* @param boolean $update true = always update, false = never update, null = update when not found or outdated
|
|
* @return Entity\DiasporaContact
|
|
* @throws HTTPException\NotFoundException
|
|
*/
|
|
public function getByUrl(UriInterface $uri, ?bool $update = self::UPDATE_IF_MISSING_OR_OUTDATED): Entity\DiasporaContact
|
|
{
|
|
if ($update !== self::ALWAYS_UPDATE) {
|
|
try {
|
|
$dcontact = $this->selectOneByUriId(ItemURI::getIdByURI($uri));
|
|
if ($update === self::NEVER_UPDATE) {
|
|
return $dcontact;
|
|
}
|
|
} catch (HTTPException\NotFoundException $e) {
|
|
if ($update === self::NEVER_UPDATE) {
|
|
throw $e;
|
|
}
|
|
|
|
// This is necessary for Contact::getByURL in case the base contact record doesn't need probing,
|
|
// but we still need the result of a probe to create the missing diaspora-contact record.
|
|
$update = self::ALWAYS_UPDATE;
|
|
}
|
|
}
|
|
|
|
$contact = Contact::getByURL($uri, $update, ['uri-id']);
|
|
if (empty($contact['uri-id'])) {
|
|
throw new HTTPException\NotFoundException('Diaspora profile with URI ' . $uri . ' not found');
|
|
}
|
|
|
|
return self::selectOneByUriId($contact['uri-id']);
|
|
}
|
|
|
|
/**
|
|
* Update or create a diaspora-contact entry via a probe array
|
|
*
|
|
* @param array $data Probe array
|
|
* @return Entity\DiasporaContact
|
|
* @throws \Exception
|
|
*/
|
|
public function updateFromProbeArray(array $data): Entity\DiasporaContact
|
|
{
|
|
$uriId = ItemURI::insert(['uri' => $data['url'], 'guid' => $data['guid']]);
|
|
|
|
$contact = Contact::getByUriId($uriId, ['id', 'created']);
|
|
$apcontact = APContact::getByURL($data['url'], false);
|
|
if (!empty($apcontact)) {
|
|
$interacting_count = $apcontact['followers_count'];
|
|
$interacted_count = $apcontact['following_count'];
|
|
$post_count = $apcontact['statuses_count'];
|
|
} elseif (!empty($contact['id'])) {
|
|
$last_interaction = DateTimeFormat::utc('now - 180 days');
|
|
|
|
$interacting_count = $this->db->count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
|
|
$interacted_count = $this->db->count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
|
|
$post_count = $this->db->count('post', ['author-id' => $contact['id'], 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT]]);
|
|
}
|
|
|
|
$DiasporaContact = $this->factory->createfromProbeData(
|
|
$data,
|
|
$uriId,
|
|
new \DateTime($contact['created'] ?? 'now', new \DateTimeZone('UTC')),
|
|
$interacting_count ?? 0,
|
|
$interacted_count ?? 0,
|
|
$post_count ?? 0
|
|
);
|
|
|
|
$DiasporaContact = $this->save($DiasporaContact);
|
|
|
|
$this->logger->info('Updated diaspora-contact', ['url' => (string) $DiasporaContact->url, 'callstack' => System::callstack(20)]);
|
|
|
|
return $DiasporaContact;
|
|
}
|
|
|
|
/**
|
|
* get a url (scheme://domain.tld/u/user) from a given contact guid
|
|
*
|
|
* @param mixed $guid Hexadecimal string guid
|
|
*
|
|
* @return string the contact url or null
|
|
* @throws \Exception
|
|
*/
|
|
public function getUrlByGuid(string $guid): ?string
|
|
{
|
|
$diasporaContact = $this->db->selectFirst(self::$table_name, ['url'], ['guid' => $guid]);
|
|
|
|
return $diasporaContact['url'] ?? null;
|
|
}
|
|
}
|