diff --git a/docs/Protocol.md b/docs/Protocol.md index 35d397b..dcefc7f 100644 --- a/docs/Protocol.md +++ b/docs/Protocol.md @@ -44,10 +44,11 @@ Example: "region": "New York", "country": "USA", "profile_url": "https://friendica.mrpetovan.com/profile/hypolite", - "dfrn_request": "https://friendica.mrpetovan.com/dfrn_request/hypolite", "photo": "https://friendica.mrpetovan.com/photo/27330388315ae4ed2b03e3c116980490-4.jpg?ts=1541567135", "tags": "videogame gaming boardgame politics philosophy development programming php", - "last_activity": "2018-45" + "last_activity": "2018-45", + "remote_follow": "https://friendica.mrpetovan.com/remote_follow/hypolite", + "subscribe": null }, ... ] diff --git a/src/classes/Controllers/Api/MatchSearch.php b/src/classes/Controllers/Api/MatchSearch.php index 28971ae..ba97311 100644 --- a/src/classes/Controllers/Api/MatchSearch.php +++ b/src/classes/Controllers/Api/MatchSearch.php @@ -11,10 +11,6 @@ use Psr\Http\Message\ServerRequestInterface; */ class MatchSearch { - /** - * @var \Atlas\Pdo\Connection - */ - private $atlas; /** * @var \Friendica\Directory\Models\Profile */ @@ -25,12 +21,10 @@ class MatchSearch private $l10n; public function __construct( - \Atlas\Pdo\Connection $atlas, \Friendica\Directory\Models\Profile $profileModel, \Gettext\TranslatorInterface $l10n ) { - $this->atlas = $atlas; $this->profileModel = $profileModel; $this->l10n = $l10n; } @@ -53,7 +47,13 @@ class MatchSearch $values = ['query' => $query]; - $profiles = $this->profileModel->getListForDisplay($pager->getItemsPerPage(), $pager->getStart(), $sql_where, $values); + $profiles = $this->profileModel->getListForDisplay( + null, + $pager->getItemsPerPage(), + $pager->getStart(), + $sql_where, + $values, + ); $results = []; foreach ($profiles as $profile) { diff --git a/src/classes/Controllers/Api/Search.php b/src/classes/Controllers/Api/Search.php index a27fa82..ea39dd7 100644 --- a/src/classes/Controllers/Api/Search.php +++ b/src/classes/Controllers/Api/Search.php @@ -11,10 +11,6 @@ use Psr\Http\Message\ServerRequestInterface; */ class Search { - /** - * @var \Atlas\Pdo\Connection - */ - private $atlas; /** * @var \Friendica\Directory\Models\Profile */ @@ -25,12 +21,10 @@ class Search private $l10n; public function __construct( - \Atlas\Pdo\Connection $atlas, \Friendica\Directory\Models\Profile $profileModel, \Gettext\TranslatorInterface $l10n ) { - $this->atlas = $atlas; $this->profileModel = $profileModel; $this->l10n = $l10n; } @@ -64,7 +58,13 @@ AND `account_type` = :account_type'; $values['account_type'] = $account_type; } - $profiles = $this->profileModel->getListForDisplay($pager->getItemsPerPage(), $pager->getStart(), $sql_where, $values); + $profiles = $this->profileModel->getListForDisplay( + null, + $pager->getItemsPerPage(), + $pager->getStart(), + $sql_where, + $values, + ); $count = $this->profileModel->getCountForDisplay($sql_where, $values); diff --git a/src/classes/Controllers/Console/Install.php b/src/classes/Controllers/Console/Install.php index 4de2e05..3e3fb3c 100644 --- a/src/classes/Controllers/Console/Install.php +++ b/src/classes/Controllers/Console/Install.php @@ -31,9 +31,9 @@ class Install extends \Asika\SimpleConsole\Console protected function getHelp() { $help = << [-h|--help|-?] [-v] + bin/console install [-h|--help|-?] [-v] Description Install directory diff --git a/src/classes/Controllers/Console/UpdateDb.php b/src/classes/Controllers/Console/UpdateDb.php index 0f2c32e..9ac7c76 100644 --- a/src/classes/Controllers/Console/UpdateDb.php +++ b/src/classes/Controllers/Console/UpdateDb.php @@ -37,12 +37,14 @@ class UpdateDb extends \Asika\SimpleConsole\Console $help = << [-h|--help|-?] [-v] + bin/console updatedb [] [-h|--help|-?] [-v] Description Update database schema Options + Optional target version number, default is the latest version. + Do not use this parameter if you're not sure what you're doing, it will result in data loss! -h|--help|-? Show help information -v Show more debug information. HELP; @@ -56,16 +58,38 @@ HELP; return 0; } - if (count($this->args) > 1) { + if (count($this->args) > 2) { throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments'); } - $this->out('Updating database schema to latest version...'); + $currentVersion = $this->migration->getCurrentVersion()['version']; - $this->migration->up(); + $this->out('Database schema currently in version ' . $currentVersion); - $this->out('Database schema migrated to version ' . $this->migration->getCurrentVersion()['version']); + if (count($this->args) == 1) { + $this->out('Updating database schema to latest version...'); + $this->migration->up(); + $this->out('Database schema migrated to version ' . $this->migration->getCurrentVersion()['version']); + return 0; + } + $target = $this->getArgument(1); + + if ($target > $currentVersion) { + $this->out('Updating database schema to version ' . $target); + $this->migration->up($target); + $this->out('Database schema migrated up to version ' . $this->migration->getCurrentVersion()['version']); + return 0; + } + + if ($target < $currentVersion) { + $this->out('Downgrading database schema to version ' . $target); + $this->migration->down($target); + $this->out('Database schema migrated down to version ' . $this->migration->getCurrentVersion()['version']); + return 0; + } + + $this->out('Target version equal to current version, exiting.'); return 0; } } diff --git a/src/classes/Controllers/Web/Directory.php b/src/classes/Controllers/Web/Directory.php index f4aaaec..84c5a1f 100644 --- a/src/classes/Controllers/Web/Directory.php +++ b/src/classes/Controllers/Web/Directory.php @@ -18,6 +18,10 @@ class Directory extends BaseController * @var \Atlas\Pdo\Connection */ private $atlas; + /** + * @var \Friendica\Directory\Models\Server + */ + private $serverModel; /** * @var \Friendica\Directory\Models\Profile */ @@ -37,6 +41,7 @@ class Directory extends BaseController public function __construct( \Atlas\Pdo\Connection $atlas, + \Friendica\Directory\Models\Server $serverModel, \Friendica\Directory\Models\Profile $profileModel, \Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs, \Friendica\Directory\Views\PhpRenderer $renderer, @@ -44,6 +49,7 @@ class Directory extends BaseController ) { $this->atlas = $atlas; + $this->serverModel = $serverModel; $this->profileModel = $profileModel; $this->accountTypeTabs = $accountTypeTabs; $this->renderer = $renderer; @@ -58,16 +64,22 @@ class Directory extends BaseController $pager = new Pager($this->l10n, $request, 20); - $condition = ''; + $sql_where = ''; $values = []; if (!empty($args['account_type'])) { - $condition = '`account_type` = :account_type'; + $sql_where = '`account_type` = :account_type'; $values = ['account_type' => $args['account_type']]; } - $profiles = $this->profileModel->getListForDisplay($pager->getItemsPerPage(), $pager->getStart(), $condition, $values); + $profiles = $this->profileModel->getListForDisplay( + $this->serverModel->getSubscribeUrlByProfile($request->getQueryParam('zrl', '')), + $pager->getItemsPerPage(), + $pager->getStart(), + $sql_where, + $values, + ); - $count = $this->profileModel->getCountForDisplay($condition, $values); + $count = $this->profileModel->getCountForDisplay($sql_where, $values); $vars = [ 'title' => $this->l10n->gettext('People'), @@ -82,7 +94,6 @@ class Directory extends BaseController $content = $this->renderer->fetch('directory.phtml', $vars); - // Render index view return ['content' => $content]; } } diff --git a/src/classes/Controllers/Web/Search.php b/src/classes/Controllers/Web/Search.php index d70925a..fc6f5ff 100644 --- a/src/classes/Controllers/Web/Search.php +++ b/src/classes/Controllers/Web/Search.php @@ -13,6 +13,10 @@ class Search extends BaseController * @var \Atlas\Pdo\Connection */ private $atlas; + /** + * @var \Friendica\Directory\Models\Server + */ + private $serverModel; /** * @var \Friendica\Directory\Models\Profile */ @@ -32,6 +36,7 @@ class Search extends BaseController public function __construct( \Atlas\Pdo\Connection $atlas, + \Friendica\Directory\Models\Server $serverModel, \Friendica\Directory\Models\Profile $profileModel, \Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs, \Friendica\Directory\Views\PhpRenderer $renderer, @@ -39,6 +44,7 @@ class Search extends BaseController ) { $this->atlas = $atlas; + $this->serverModel = $serverModel; $this->profileModel = $profileModel; $this->accountTypeTabs = $accountTypeTabs; $this->renderer = $renderer; @@ -89,7 +95,13 @@ AND `account_type` = :account_type'; $values['account_type'] = $account_type; } - $profiles = $this->profileModel->getListForDisplay($pager->getItemsPerPage(), $pager->getStart(), $sql_where, $values); + $profiles = $this->profileModel->getListForDisplay( + $this->serverModel->getSubscribeUrlByProfile($request->getQueryParam('zrl', '')), + $pager->getItemsPerPage(), + $pager->getStart(), + $sql_where, + $values, + ); $count = $this->profileModel->getCountForDisplay($sql_where, $values); @@ -106,7 +118,6 @@ AND `account_type` = :account_type'; $content = $this->renderer->fetch('search.phtml', $vars); - // Render index view return ['content' => $content, 'noNavSearch' => true]; } } diff --git a/src/classes/Models/Profile.php b/src/classes/Models/Profile.php index e8e5324..131a070 100644 --- a/src/classes/Models/Profile.php +++ b/src/classes/Models/Profile.php @@ -74,7 +74,7 @@ class Profile extends \Friendica\Directory\Model ]; } - public function getListForDisplay(int $limit = 30, int $start = 0, string $condition = '', array $values = []): array + public function getListForDisplay(string $subscribeUrl = null, int $limit = 30, int $start = 0, string $condition = '', array $values = []): array { if ($condition) { $condition = 'AND ' . $condition; @@ -86,8 +86,8 @@ class Profile extends \Friendica\Directory\Model ]); $stmt = 'SELECT p.`id`, p.`name`, p.`username`, p.`addr`, p.`account_type`, p.`language`, - p.`pdesc`, p.`locality`, p.`region`, p.`country`, p.`profile_url`, p.`dfrn_request`, - p.`photo`, p.`tags`, p.`last_activity` + p.`pdesc`, p.`locality`, p.`region`, p.`country`, p.`profile_url`, + p.`photo`, p.`tags`, p.`last_activity`, s.`version` FROM `profile` p JOIN `server` s ON s.`id` = p.`server_id` AND s.`available` AND NOT s.`hidden` WHERE p.`available` @@ -98,6 +98,12 @@ class Profile extends \Friendica\Directory\Model LIMIT :start, :limit'; $profiles = $this->atlas->fetchAll($stmt, $values); + array_walk($profiles, function (array &$profile) use ($subscribeUrl) { + $profile['remote_follow'] = version_compare($profile['version'], '2020.03', '>=') ? str_replace('/profile/', '/remote_follow/', $profile['profile_url']) : null; + $profile['subscribe'] = $subscribeUrl ? str_replace('{uri}', urlencode($profile['profile_url']), $subscribeUrl): null; + unset($profile['version']); + }); + return $profiles; } diff --git a/src/classes/Models/Server.php b/src/classes/Models/Server.php index 9f1ec5c..3e89603 100644 --- a/src/classes/Models/Server.php +++ b/src/classes/Models/Server.php @@ -41,4 +41,20 @@ class Server extends \Friendica\Directory\Model 'alias' => strtolower($server_alias) ]); } + + /** + * Returns the complete subscribe URL of the given profile URL if we have it for the related server + * + * @param string $profile_url + * @return mixed|null + */ + public function getSubscribeUrlByProfile(string $profile_url) + { + if (preg_match('#^(.+)/profile/#', $profile_url, $matches)) { + $server = $this->getByUrlAlias($matches[1]); + return $server['subscribe_url'] ?? null; + } + + return null; + } } diff --git a/src/classes/Pollers/Profile.php b/src/classes/Pollers/Profile.php index 70db56e..9dd546f 100644 --- a/src/classes/Pollers/Profile.php +++ b/src/classes/Pollers/Profile.php @@ -9,7 +9,6 @@ use Friendica\Directory\Utils\Network; */ class Profile { - const PROFILE_MISSING_REQUEST = 1; const PROFILE_MISSING_CONFIRM = 2; const PROFILE_MISSING_NOTIFY = 4; const PROFILE_MISSING_POLL = 8; @@ -177,9 +176,6 @@ class Profile // This is most likely a problem with the site configuration. Ignore. if ($error = self::validateParams($params)) { $this->logger->warning('Poll aborted, parameters invalid.', ['params' => $params]); - if ($error & Profile::PROFILE_MISSING_REQUEST) { - $this->logger->notice('dfrn-request parameter is empty.'); - } if ($error & Profile::PROFILE_MISSING_CONFIRM) { $this->logger->notice('dfrn-confirm parameter is empty.'); } @@ -232,7 +228,6 @@ class Profile 'region' => $params['region'] ?? '', 'country' => $params['country-name'] ?? '', 'profile_url' => $profile_uri, - 'dfrn_request' => $params['dfrn-request'] ?? null, 'photo' => $params['photo'], 'tags' => implode(' ', $tags), 'addr' => $addr, @@ -254,7 +249,6 @@ class Profile `region` = :region, `country` = :country, `profile_url` = :profile_url, - `dfrn_request` = :dfrn_request, `photo` = :photo, `tags` = :tags, `addr` = :addr, @@ -274,7 +268,6 @@ class Profile `region` = :region, `country` = :country, `profile_url` = :profile_url, - `dfrn_request` = :dfrn_request, `photo` = :photo, `tags` = :tags, `addr` = :addr, @@ -361,9 +354,6 @@ class Profile private static function validateParams(array $params): int { $errors = 0; - if (empty($params['dfrn-request'])) { - $errors &= self::PROFILE_MISSING_REQUEST; - } if (empty($params['dfrn-confirm'])) { $errors &= self::PROFILE_MISSING_CONFIRM; } diff --git a/src/classes/Pollers/Server.php b/src/classes/Pollers/Server.php index e6c160c..96ef29b 100644 --- a/src/classes/Pollers/Server.php +++ b/src/classes/Pollers/Server.php @@ -147,6 +147,10 @@ class Server $addons = $probe_result['data']['plugins']; } + if ($probe_result['data']['admin']['profile']) { + $subscribe = $this->getSubscribeUrl($probe_result['data']['url'], $probe_result['data']['admin']['profile']); + } + $this->atlas->perform( 'UPDATE `server` SET `available` = 1, @@ -161,6 +165,7 @@ class Server `admin_name` = :admin_name, `admin_profile` = :admin_profile, `noscrape_url` = :noscrape_url, + `subscribe_url` = :subscribe_url, `ssl_state` = :ssl_state WHERE `id` = :server_id', [ @@ -175,6 +180,7 @@ class Server 'admin_name' => $probe_result['data']['admin']['name'], 'admin_profile' => $probe_result['data']['admin']['profile'], 'noscrape_url' => $probe_result['data']['no_scrape_url'] ?? null, + 'subscribe_url' => $subscribe ?? null, 'ssl_state' => $probe_result['ssl_state'] ] ); @@ -452,4 +458,38 @@ class Server } } } + + public function getSubscribeUrl($base_url, $profile) + { + $xrdRequest = new WebRequest($base_url . '/xrd'); + $xrdRequest->addRequestHeader('Accept', 'application/jrd+json'); + $xrdJsonData = $xrdRequest->get(['uri' => $profile]); + + $this->logger->debug('WebRequest: ' . $xrdRequest->getLastFetchedUrl() . ' Status: ' . $xrdRequest->getLastStatus()); + + if ($xrdRequest->getLastStatus() != 200) { + $this->logger->info('Unsuccessful XRD request: ' . $xrdRequest->getLastFetchedUrl()); + return null; + } + + try { + $xrdData = json_decode($xrdJsonData); + } catch (\Throwable $e) { + $this->logger->notice('Invalid JSON string for XRD URL: ' . $xrdRequest->getLastFetchedUrl()); + return null; + } + + if (!isset($xrdData->links)) { + $this->logger->notice('Invalid JSON structure for XRD URL: ' . $xrdRequest->getLastFetchedUrl()); + return null; + } + + foreach ($xrdData->links as $link) { + if ($link->rel == 'http://ostatus.org/schema/1.0/subscribe') { + return $link->template ?? null; + } + } + + return null; + } } diff --git a/src/classes/Routes/Http/MatchSearch.php b/src/classes/Routes/Http/MatchSearch.php index 7590b71..1c70ba6 100644 --- a/src/classes/Routes/Http/MatchSearch.php +++ b/src/classes/Routes/Http/MatchSearch.php @@ -10,7 +10,6 @@ class MatchSearch extends BaseRoute public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response { return (new \Friendica\Directory\Controllers\Api\MatchSearch( - $this->container->atlas, $this->container->get(\Friendica\Directory\Models\Profile::class), $this->container->l10n ))->render($request, $response, $args); diff --git a/src/classes/Routes/Http/Search.php b/src/classes/Routes/Http/Search.php index b9db49d..ba4a533 100644 --- a/src/classes/Routes/Http/Search.php +++ b/src/classes/Routes/Http/Search.php @@ -10,7 +10,6 @@ class Search extends BaseRoute public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): \Slim\Http\Response { return (new \Friendica\Directory\Controllers\Api\Search( - $this->container->atlas, $this->container->get(\Friendica\Directory\Models\Profile::class), $this->container->l10n ))->render($request, $response, $args); diff --git a/src/classes/Routes/Web/Directory.php b/src/classes/Routes/Web/Directory.php index 5d70edc..f5228b2 100644 --- a/src/classes/Routes/Web/Directory.php +++ b/src/classes/Routes/Web/Directory.php @@ -13,6 +13,7 @@ class Directory extends BaseRoute $this->controller = new \Friendica\Directory\Controllers\Web\Directory( $this->container->atlas, + $this->container->get(\Friendica\Directory\Models\Server::class), $this->container->get(\Friendica\Directory\Models\Profile::class), $this->container->get(\Friendica\Directory\Views\Widget\AccountTypeTabs::class), $this->container->renderer, diff --git a/src/classes/Routes/Web/Search.php b/src/classes/Routes/Web/Search.php index d758053..324c5ba 100644 --- a/src/classes/Routes/Web/Search.php +++ b/src/classes/Routes/Web/Search.php @@ -13,6 +13,7 @@ class Search extends BaseRoute $this->controller = new \Friendica\Directory\Controllers\Web\Search( $this->container->atlas, + $this->container->get(\Friendica\Directory\Models\Server::class), $this->container->get(\Friendica\Directory\Models\Profile::class), $this->container->get(\Friendica\Directory\Views\Widget\AccountTypeTabs::class), $this->container->renderer, diff --git a/src/sql/migrations/down/0008.sql b/src/sql/migrations/down/0008.sql new file mode 100644 index 0000000..f69fc74 --- /dev/null +++ b/src/sql/migrations/down/0008.sql @@ -0,0 +1,3 @@ +BEGIN; +ALTER TABLE `server` DROP `subscribe_url`; +COMMIT; \ No newline at end of file diff --git a/src/sql/migrations/down/0009.sql b/src/sql/migrations/down/0009.sql new file mode 100644 index 0000000..fd6ce4a --- /dev/null +++ b/src/sql/migrations/down/0009.sql @@ -0,0 +1,3 @@ +BEGIN; +ALTER TABLE `profile` ADD `dfrn_request` VARCHAR(250) DEFAULT NULL AFTER `profile_url`; +COMMIT; \ No newline at end of file diff --git a/src/sql/migrations/up/0009.sql b/src/sql/migrations/up/0009.sql new file mode 100644 index 0000000..d0cc568 --- /dev/null +++ b/src/sql/migrations/up/0009.sql @@ -0,0 +1,3 @@ +BEGIN; +ALTER TABLE `server` ADD `subscribe_url` VARCHAR(250) NULL AFTER `noscrape_url`; +COMMIT; diff --git a/src/sql/migrations/up/0010.sql b/src/sql/migrations/up/0010.sql new file mode 100644 index 0000000..3875c11 --- /dev/null +++ b/src/sql/migrations/up/0010.sql @@ -0,0 +1,3 @@ +BEGIN; +ALTER TABLE `profile` DROP `dfrn_request`; +COMMIT; \ No newline at end of file diff --git a/src/templates/sub/profile.phtml b/src/templates/sub/profile.phtml index 5a651b3..68c67c0 100644 --- a/src/templates/sub/profile.phtml +++ b/src/templates/sub/profile.phtml @@ -26,12 +26,22 @@ if (!empty($profile['country'])) {

escapeHtml($profile['pdesc']) ?>

- +
- +
- + - - + +
- +
- + escapeHtml($tag) ?> - +
- +