Fetch followers/followings of contacts

This commit is contained in:
Michael 2020-07-26 07:34:33 +00:00
parent eaf159fc20
commit 18617f6c48
10 changed files with 219 additions and 208 deletions

View File

@ -1,6 +1,6 @@
-- ------------------------------------------
-- Friendica 2020.09-dev (Red Hot Poker)
-- DB_UPDATE_VERSION 1357
-- DB_UPDATE_VERSION 1358
-- ------------------------------------------
@ -102,6 +102,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
`avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`term-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last post',
`last-discovery` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last follower discovery',
`priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`blocked` boolean NOT NULL DEFAULT '1' COMMENT 'Node-wide block status',
`block_reason` text COMMENT 'Node-wide block reason',
@ -342,8 +343,12 @@ CREATE TABLE IF NOT EXISTS `contact-relation` (
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact the related contact had interacted with',
`relation-cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'related contact who had interacted with the contact',
`last-interaction` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last interaction',
`follow-updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last update of the contact relationship',
`follows` boolean NOT NULL DEFAULT '0' COMMENT '',
PRIMARY KEY(`cid`,`relation-cid`),
INDEX `relation-cid` (`relation-cid`)
INDEX `relation-cid` (`relation-cid`),
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`relation-cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Contact relations';
--
@ -517,17 +522,6 @@ CREATE TABLE IF NOT EXISTS `gcontact` (
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='global contacts';
--
-- TABLE gfollower
--
CREATE TABLE IF NOT EXISTS `gfollower` (
`gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'global contact',
`follower-gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'global contact of the follower',
`deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates that the connection has been deleted',
PRIMARY KEY(`gcid`,`follower-gcid`),
INDEX `follower-gcid` (`follower-gcid`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Followers of global contacts';
--
-- TABLE glink
--

View File

@ -1418,7 +1418,6 @@ class Contact
'poll' => $data['poll'] ?? '',
'name' => $data['name'] ?? '',
'nick' => $data['nick'] ?? '',
'photo' => $data['photo'] ?? '',
'keywords' => $data['keywords'] ?? '',
'location' => $data['location'] ?? '',
'about' => $data['about'] ?? '',
@ -1474,14 +1473,6 @@ class Contact
} else {
// Else do a direct update
self::updateFromProbe($contact_id, '', false);
// Update the gcontact entry
if ($uid == 0) {
GContact::updateFromPublicContactID($contact_id);
if (($data['network'] == Protocol::ACTIVITYPUB) && in_array(DI::config()->get('system', 'gcontact_discovery'), [GContact::DISCOVERY_DIRECT, GContact::DISCOVERY_RECURSIVE])) {
GContact::discoverFollowers($data['url']);
}
}
}
} else {
$fields = ['url', 'nurl', 'addr', 'alias', 'name', 'nick', 'keywords', 'location', 'about', 'avatar-date', 'baseurl', 'gsid'];
@ -1818,9 +1809,11 @@ class Contact
$uid = $contact['uid'];
// Only update the cached photo links of public contacts when they already are cached
if (($uid == 0) && !$force && empty($contact['photo']) && empty($contact['thumb']) && empty($contact['micro'])) {
if (($uid == 0) && !$force && empty($contact['thumb']) && empty($contact['micro'])) {
if ($contact['avatar'] != $avatar) {
DBA::update('contact', ['avatar' => $avatar], ['id' => $cid]);
Logger::info('Only update the avatar', ['id' => $cid, 'avatar' => $avatar, 'contact' => $contact]);
}
return;
}
@ -1830,28 +1823,27 @@ class Contact
$contact['micro'] ?? '',
];
$update = ($contact['avatar'] != $avatar) || $force;
if (!$update) {
foreach ($data as $image_uri) {
$image_rid = Photo::ridFromURI($image_uri);
if ($image_rid && !Photo::exists(['resource-id' => $image_rid, 'uid' => $uid])) {
Logger::info('Regenerating avatar', ['contact uid' => $uid, 'cid' => $cid, 'missing photo' => $image_rid, 'avatar' => $contact['avatar']]);
$force = true;
$update = true;
}
}
}
if (($contact["avatar"] != $avatar) || $force) {
if ($update) {
$photos = Photo::importProfilePhoto($avatar, $uid, $cid, true);
if ($photos) {
$fields = ['avatar' => $avatar, 'photo' => $photos[0], 'thumb' => $photos[1], 'micro' => $photos[2], 'avatar-date' => DateTimeFormat::utcNow()];
DBA::update('contact', $fields, ['id' => $cid]);
// Update the public contact (contact id = 0)
if ($uid != 0) {
$pcontact = DBA::selectFirst('contact', ['id'], ['nurl' => $contact['nurl'], 'uid' => 0]);
if (DBA::isResult($pcontact)) {
DBA::update('contact', $fields, ['id' => $pcontact['id']]);
}
}
} elseif (empty($contact['avatar'])) {
// Ensure that the avatar field is set
DBA::update('contact', ['avatar' => $avatar], ['id' => $cid]);
Logger::info('Failed profile import', ['id' => $cid, 'force' => $force, 'avatar' => $avatar, 'contact' => $contact]);
}
}
}
@ -2035,6 +2027,13 @@ class Contact
$new_pubkey = $ret['pubkey'];
// Update the gcontact entry
if ($uid == 0) {
GContact::updateFromPublicContactID($id);
}
ContactRelation::discoverByUrl($ret['url']);
$update = false;
// make sure to not overwrite existing values with blank entries except some technical fields
@ -2508,7 +2507,6 @@ class Contact
'nurl' => Strings::normaliseLink($url),
'name' => $name,
'nick' => $nick,
'photo' => $photo,
'network' => $network,
'rel' => self::FOLLOWER,
'blocked' => 0,

View File

@ -0,0 +1,163 @@
<?php
/**
* @copyright Copyright (C) 2020, Friendica
*
* @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\Model;
use Friendica\Core\Logger;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
class ContactRelation
{
/**
* No discovery of followers/followings
*/
const DISCOVERY_NONE = 0;
/**
* Discover followers/followings of local contacts
*/
const DISCOVERY_LOCAL = 1;
/**
* Discover followers/followings of local contacts and contacts that visibly interacted on the system
*/
const DISCOVERY_INTERACTOR = 2;
/**
* Discover followers/followings of all contacts
*/
const DISCOVERY_ALL = 3;
public static function store(int $target, int $actor, string $interaction_date)
{
if ($actor == $target) {
return;
}
DBA::update('contact-relation', ['last-interaction' => $interaction_date], ['cid' => $target, 'relation-cid' => $actor], true);
}
/**
* Fetches the followers of a given profile and adds them
*
* @param string $url URL of a profile
* @return void
*/
public static function discoverByUrl(string $url)
{
$contact_discovery = DI::config()->get('system', 'contact_discovery');
if ($contact_discovery == self::DISCOVERY_NONE) {
return;
}
$contact = Contact::getByURL($url);
if (empty($contact)) {
return;
}
if ($contact['last-discovery'] > DateTimeFormat::utc('now - 1 month')) {
Logger::info('Last discovery was less then a month before.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['last-discovery']]);
return;
}
if ($contact_discovery != self::DISCOVERY_ALL) {
$local = DBA::exists('contact', ["`nurl` = ? AND `uid` != ?", Strings::normaliseLink($url), 0]);
if (($contact_discovery == self::DISCOVERY_LOCAL) && !$local) {
Logger::info('No discovery - This contact is not followed/following locally.', ['id' => $contact['id'], 'url' => $url]);
return;
}
if ($contact_discovery == self::DISCOVERY_INTERACTOR) {
$interactor = DBA::exists('contact-relation', ["`relation-cid` = ? AND `last-interaction` > ?", $contact['id'], DBA::NULL_DATETIME]);
if (!$local && !$interactor) {
Logger::info('No discovery - This contact is not interacting locally.', ['id' => $contact['id'], 'url' => $url]);
return;
}
}
} elseif ($contact['created'] > DateTimeFormat::utc('now - 1 day')) {
Logger::info('Newly created contacs are not discovered to avoid DDoS attacks.', ['id' => $contact['id'], 'url' => $url, 'discovery' => $contact['created']]);
return;
}
$apcontact = APContact::getByURL($url);
if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) {
$followers = ActivityPub::fetchItems($apcontact['followers']);
} else {
$followers = [];
}
if (!empty($apcontact['following']) && is_string($apcontact['following'])) {
$followings = ActivityPub::fetchItems($apcontact['following']);
} else {
$followings = [];
}
if (empty($followers) && empty($followings)) {
return;
}
$target = $contact['id'];
if (!empty($followers)) {
// Clear the follower list, since it will be recreated in the next step
DBA::update('contact-relation', ['follows' => false], ['cid' => $target]);
}
$contacts = [];
foreach (array_merge($followers, $followings) as $contact) {
if (is_string($contact)) {
$contacts[] = $contact;
} elseif (!empty($contact['url']) && is_string($contact['url'])) {
$contacts[] = $contact['url'];
}
}
$contacts = array_unique($contacts);
Logger::info('Discover contacts', ['id' => $target, 'url' => $url, 'contacts' => count($contacts)]);
foreach ($contacts as $contact) {
$actor = Contact::getIdForURL($contact);
if (!empty($actor)) {
$fields = [];
if (in_array($contact, $followers)) {
$fields = ['cid' => $target, 'relation-cid' => $actor];
} elseif (in_array($contact, $followings)) {
$fields = ['cid' => $actor, 'relation-cid' => $target];
} else {
continue;
}
DBA::update('contact-relation', ['follows' => true, 'follow-updated' => DateTimeFormat::utcNow()], $fields, true);
}
}
if (!empty($followers)) {
// Delete all followers that aren't followers anymore (and aren't interacting)
DBA::delete('contact-relation', ['cid' => $target, 'follows' => false, 'last-interaction' => DBA::NULL_DATETIME]);
}
DBA::update('contact', ['last-discovery' => DateTimeFormat::utcNow()], ['id' => $target]);
Logger::info('Contacts discovery finished, "last-discovery" set', ['id' => $target, 'url' => $url]);
return;
}
}

View File

@ -43,19 +43,6 @@ use Friendica\Util\Strings;
*/
class GContact
{
/**
* No discovery of followers/followings
*/
const DISCOVERY_NONE = 0;
/**
* Only discover followers/followings from direct contacts
*/
const DISCOVERY_DIRECT = 1;
/**
* Recursive discovery of followers/followings
*/
const DISCOVERY_RECURSIVE = 2;
/**
* Search global contact table by nick or name
*
@ -1288,129 +1275,6 @@ class GContact
}
}
/**
* Fetches the followers of a given profile and adds them
*
* @param string $url URL of a profile
* @return void
*/
public static function discoverFollowers(string $url)
{
$gcontact = DBA::selectFirst('gcontact', ['id', 'last_discovery'], ['nurl' => Strings::normaliseLink(($url))]);
if (!DBA::isResult($gcontact)) {
return;
}
if ($gcontact['last_discovery'] > DateTimeFormat::utc('now - 1 month')) {
Logger::info('Last discovery was less then a month before.', ['url' => $url, 'discovery' => $gcontact['last_discovery']]);
return;
}
$gcid = $gcontact['id'];
$apcontact = APContact::getByURL($url);
if (!empty($apcontact['followers']) && is_string($apcontact['followers'])) {
$followers = ActivityPub::fetchItems($apcontact['followers']);
} else {
$followers = [];
}
if (!empty($apcontact['following']) && is_string($apcontact['following'])) {
$followings = ActivityPub::fetchItems($apcontact['following']);
} else {
$followings = [];
}
if (!empty($followers) || !empty($followings)) {
if (!empty($followers)) {
// Clear the follower list, since it will be recreated in the next step
DBA::update('gfollower', ['deleted' => true], ['gcid' => $gcid]);
}
$contacts = [];
foreach (array_merge($followers, $followings) as $contact) {
if (is_string($contact)) {
$contacts[] = $contact;
} elseif (!empty($contact['url']) && is_string($contact['url'])) {
$contacts[] = $contact['url'];
}
}
$contacts = array_unique($contacts);
Logger::info('Discover AP contacts', ['url' => $url, 'contacts' => count($contacts)]);
foreach ($contacts as $contact) {
$gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(($contact))]);
if (DBA::isResult($gcontact)) {
$fields = [];
if (in_array($contact, $followers)) {
$fields = ['gcid' => $gcid, 'follower-gcid' => $gcontact['id']];
} elseif (in_array($contact, $followings)) {
$fields = ['gcid' => $gcontact['id'], 'follower-gcid' => $gcid];
}
if (!empty($fields)) {
Logger::info('Set relation between contacts', $fields);
DBA::update('gfollower', ['deleted' => false], $fields, true);
continue;
}
}
if (!Network::isUrlBlocked($contact)) {
Logger::info('Discover new AP contact', ['url' => $contact]);
Worker::add(PRIORITY_LOW, 'UpdateGContact', $contact, 'nodiscover');
} else {
Logger::info('No discovery, the URL is blocked.', ['url' => $contact]);
}
}
if (!empty($followers)) {
// Delete all followers that aren't undeleted
DBA::delete('gfollower', ['gcid' => $gcid, 'deleted' => true]);
}
DBA::update('gcontact', ['last_discovery' => DateTimeFormat::utcNow()], ['id' => $gcid]);
Logger::info('AP contacts discovery finished, last discovery set', ['url' => $url]);
return;
}
$data = Probe::uri($url);
if (empty($data['poco'])) {
return;
}
$curlResult = DI::httpRequest()->get($data['poco']);
if (!$curlResult->isSuccess()) {
return;
}
$poco = json_decode($curlResult->getBody(), true);
if (empty($poco['entry'])) {
return;
}
Logger::info('PoCo Discovery started', ['url' => $url, 'contacts' => count($poco['entry'])]);
foreach ($poco['entry'] as $entries) {
if (!empty($entries['urls'])) {
foreach ($entries['urls'] as $entry) {
if ($entry['type'] == 'profile') {
if (DBA::exists('gcontact', ['nurl' => Strings::normaliseLink(($entry['value']))])) {
continue;
}
if (!Network::isUrlBlocked($entry['value'])) {
Logger::info('Discover new PoCo contact', ['url' => $entry['value']]);
Worker::add(PRIORITY_LOW, 'UpdateGContact', $entry['value'], 'nodiscover');
} else {
Logger::info('No discovery, the URL is blocked.', ['url' => $entry['value']]);
}
}
}
}
}
DBA::update('gcontact', ['last_discovery' => DateTimeFormat::utcNow()], ['id' => $gcid]);
Logger::info('PoCo Discovery finished', ['url' => $url]);
}
/**
* Returns a random, global contact of the current node
*

View File

@ -1554,9 +1554,7 @@ class Item
}
// Update the contact relations
if ($item['author-id'] != $parent['author-id']) {
DBA::update('contact-relation', ['last-interaction' => $item['created']], ['cid' => $parent['author-id'], 'relation-cid' => $item['author-id']], true);
}
ContactRelation::store($parent['author-id'], $item['author-id'], $item['created']);
}
return $item;

View File

@ -28,7 +28,7 @@ use Friendica\Core\Theme;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\GContact;
use Friendica\Model\ContactRelation;
use Friendica\Module\BaseAdmin;
use Friendica\Module\Register;
use Friendica\Protocol\PortableContact;
@ -178,8 +178,8 @@ class Site extends BaseAdmin
$min_memory = (!empty($_POST['min_memory']) ? intval(trim($_POST['min_memory'])) : 0);
$optimize_max_tablesize = (!empty($_POST['optimize_max_tablesize']) ? intval(trim($_POST['optimize_max_tablesize'])) : 100);
$optimize_fragmentation = (!empty($_POST['optimize_fragmentation']) ? intval(trim($_POST['optimize_fragmentation'])) : 30);
$contact_discovery = (!empty($_POST['contact_discovery']) ? intval(trim($_POST['contact_discovery'])) : ContactRelation::DISCOVERY_NONE);
$poco_completion = (!empty($_POST['poco_completion']) ? intval(trim($_POST['poco_completion'])) : false);
$gcontact_discovery = (!empty($_POST['gcontact_discovery']) ? intval(trim($_POST['gcontact_discovery'])) : GContact::DISCOVERY_NONE);
$poco_requery_days = (!empty($_POST['poco_requery_days']) ? intval(trim($_POST['poco_requery_days'])) : 7);
$poco_discovery = (!empty($_POST['poco_discovery']) ? intval(trim($_POST['poco_discovery'])) : PortableContact::DISABLED);
$poco_discovery_since = (!empty($_POST['poco_discovery_since']) ? intval(trim($_POST['poco_discovery_since'])) : 30);
@ -308,7 +308,7 @@ class Site extends BaseAdmin
DI::config()->set('system', 'optimize_max_tablesize', $optimize_max_tablesize);
DI::config()->set('system', 'optimize_fragmentation', $optimize_fragmentation);
DI::config()->set('system', 'poco_completion' , $poco_completion);
DI::config()->set('system', 'gcontact_discovery' , $gcontact_discovery);
DI::config()->set('system', 'contact_discovery' , $contact_discovery);
DI::config()->set('system', 'poco_requery_days' , $poco_requery_days);
DI::config()->set('system', 'poco_discovery' , $poco_discovery);
DI::config()->set('system', 'poco_discovery_since' , $poco_discovery_since);
@ -551,9 +551,11 @@ class Site extends BaseAdmin
];
$discovery_choices = [
GContact::DISCOVERY_NONE => DI::l10n()->t('none'),
GContact::DISCOVERY_DIRECT => DI::l10n()->t('Direct contacts'),
GContact::DISCOVERY_RECURSIVE => DI::l10n()->t('Contacts of contacts')
ContactRelation::DISCOVERY_NONE => DI::l10n()->t('none'),
ContactRelation::DISCOVERY_LOCAL => DI::l10n()->t('Local contacts'),
ContactRelation::DISCOVERY_INTERACTOR => DI::l10n()->t('Interactors'),
// "All" is deactivated until we are sure not to put too much stress on the fediverse with this
// ContactRelation::DISCOVERY_ALL => DI::l10n()->t('All'),
];
$diaspora_able = (DI::baseUrl()->getUrlPath() == '');
@ -677,8 +679,13 @@ class Site extends BaseAdmin
'$optimize_max_tablesize' => ['optimize_max_tablesize', DI::l10n()->t('Maximum table size for optimization'), $optimize_max_tablesize, DI::l10n()->t('Maximum table size (in MB) for the automatic optimization. Enter -1 to disable it.')],
'$optimize_fragmentation' => ['optimize_fragmentation', DI::l10n()->t('Minimum level of fragmentation'), DI::config()->get('system', 'optimize_fragmentation', 30), DI::l10n()->t('Minimum fragmenation level to start the automatic optimization - default value is 30%.')],
'$contact_discovery' => ['contact_discovery', DI::l10n()->t('Discover followers/followings from contacts'), DI::config()->get('system', 'contact_discovery'), DI::l10n()->t('If enabled, contacts are checked for their followers and following contacts.') . '<ul>' .
'<li>' . DI::l10n()->t('None - deactivated') . '</li>' .
'<li>' . DI::l10n()->t('Local contacts - contacts of our local contacts are discovered for their followers/followings.') . '</li>' .
'<li>' . DI::l10n()->t('Interactors - contacts of our local contacts and contacts who interacted on locally visible postings are discovered for their followers/followings.') . '</li></ul>',
$discovery_choices],
'$poco_completion' => ['poco_completion', DI::l10n()->t('Periodical check of global contacts'), DI::config()->get('system', 'poco_completion'), DI::l10n()->t('If enabled, the global contacts are checked periodically for missing or outdated data and the vitality of the contacts and servers.')],
'$gcontact_discovery' => ['gcontact_discovery', DI::l10n()->t('Discover followers/followings from global contacts'), DI::config()->get('system', 'gcontact_discovery'), DI::l10n()->t('If enabled, the global contacts are checked for new contacts among their followers and following contacts. This option will create huge masses of jobs, so it should only be activated on powerful machines.'), $discovery_choices],
'$poco_requery_days' => ['poco_requery_days', DI::l10n()->t('Days between requery'), DI::config()->get('system', 'poco_requery_days'), DI::l10n()->t('Number of days after which a server is requeried for his contacts.')],
'$poco_discovery' => ['poco_discovery', DI::l10n()->t('Discover contacts from other servers'), DI::config()->get('system', 'poco_discovery'), DI::l10n()->t('Periodically query other servers for contacts. You can choose between "Users": the users on the remote system, "Global Contacts": active contacts that are known on the system. The fallback is meant for Redmatrix servers and older friendica servers, where global contacts weren\'t available. The fallback increases the server load, so the recommended setting is "Users, Global Contacts".'), $poco_discovery_choices],
'$poco_discovery_since' => ['poco_discovery_since', DI::l10n()->t('Timeframe for fetching global contacts'), DI::config()->get('system', 'poco_discovery_since'), DI::l10n()->t('When the discovery is activated, this value defines the timeframe for the activity of the global contacts that are fetched from other servers.'), $poco_discovery_since_choices],

View File

@ -40,9 +40,5 @@ class UpdateGContact
$success = GContact::updateFromProbe($url, $force);
Logger::info('Updated from probe', ['url' => $url, 'force' => $force, 'success' => $success]);
if ($success && !$nodiscover && (DI::config()->get('system', 'gcontact_discovery') == GContact::DISCOVERY_RECURSIVE)) {
GContact::discoverFollowers($url);
}
}
}

View File

@ -54,7 +54,7 @@
use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1357);
define('DB_UPDATE_VERSION', 1358);
}
return [
@ -158,6 +158,7 @@ return [
"avatar-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"term-date" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"last-item" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "date of the last post"],
"last-discovery" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "date of the last follower discovery"],
"priority" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"blocked" => ["type" => "boolean", "not null" => "1", "default" => "1", "comment" => "Node-wide block status"],
"block_reason" => ["type" => "text", "comment" => "Node-wide block reason"],
@ -407,9 +408,11 @@ return [
"contact-relation" => [
"comment" => "Contact relations",
"fields" => [
"cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["contact" => "id"], "primary" => "1", "comment" => "contact the related contact had interacted with"],
"relation-cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["contact" => "id"], "primary" => "1", "comment" => "related contact who had interacted with the contact"],
"cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "primary" => "1", "comment" => "contact the related contact had interacted with"],
"relation-cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "primary" => "1", "comment" => "related contact who had interacted with the contact"],
"last-interaction" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last interaction"],
"follow-updated" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date of the last update of the contact relationship"],
"follows" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
],
"indexes" => [
"PRIMARY" => ["cid", "relation-cid"],
@ -593,18 +596,6 @@ return [
"gsid" => ["gsid"]
]
],
"gfollower" => [
"comment" => "Followers of global contacts",
"fields" => [
"gcid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "relation" => ["gcontact" => "id"], "comment" => "global contact"],
"follower-gcid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "relation" => ["gcontact" => "id"], "comment" => "global contact of the follower"],
"deleted" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "1 indicates that the connection has been deleted"],
],
"indexes" => [
"PRIMARY" => ["gcid", "follower-gcid"],
"follower-gcid" => ["follower-gcid"],
]
],
"glink" => [
"comment" => "'friends of friends' linkages derived from poco",
"fields" => [

View File

@ -97,8 +97,8 @@
<div class="submit"><input type="submit" name="page_site" value="{{$submit}}"/></div>
<h2>{{$portable_contacts}}</h2>
{{include file="field_select.tpl" field=$contact_discovery}}
{{include file="field_checkbox.tpl" field=$poco_completion}}
{{include file="field_select.tpl" field=$gcontact_discovery}}
{{include file="field_input.tpl" field=$poco_requery_days}}
{{include file="field_select.tpl" field=$poco_discovery}}
{{include file="field_select.tpl" field=$poco_discovery_since}}

View File

@ -218,8 +218,8 @@
</div>
<div id="admin-settings-contacts-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="admin-settings-cocontactsrporate">
<div class="panel-body">
{{include file="field_select.tpl" field=$contact_discovery}}
{{include file="field_checkbox.tpl" field=$poco_completion}}
{{include file="field_select.tpl" field=$gcontact_discovery}}
{{include file="field_input.tpl" field=$poco_requery_days}}
{{include file="field_select.tpl" field=$poco_discovery}}
{{include file="field_select.tpl" field=$poco_discovery_since}}