Compare commits

...

2 commits

Author SHA1 Message Date
Hypolite Petovan 0136219ca3 WIP Server detail page 2022-04-25 08:45:09 -04:00
Hypolite Petovan 596dc79c78 [Composer] Add hashids/hashids dependency
- Add ext-gmp dependency
- Make bin/composer.phar executable
2022-04-25 08:45:09 -04:00
17 changed files with 436 additions and 63 deletions

0
bin/composer.phar Normal file → Executable file
View file

View file

@ -15,6 +15,7 @@
"php": ">=7.1.0",
"ext-curl": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-json": "*",
"ext-pdo": "*",
"asika/simple-console": "^1.0",
@ -25,6 +26,7 @@
"byjg/webrequest": "^1.0",
"gettext/gettext": "^4.6",
"gofabian/negotiation-middleware": "^0.1.3",
"hashids/hashids": "^2.0",
"laminas/laminas-escaper": "^2.6",
"masterminds/html5": "^2.3",
"monolog/monolog": "^1.17",

69
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a8bc1750aa6136e10ea28799a0974f6b",
"content-hash": "30ffd18b9855dbdef6e4bf6a3cd775da",
"packages": [
{
"name": "asika/simple-console",
@ -783,6 +783,72 @@
],
"time": "2017-02-22T18:45:01+00:00"
},
{
"name": "hashids/hashids",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/vinkla/hashids.git",
"reference": "7a945a5192d4a5c8888364970feece9bc26179df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vinkla/hashids/zipball/7a945a5192d4a5c8888364970feece9bc26179df",
"reference": "7a945a5192d4a5c8888364970feece9bc26179df",
"shasum": ""
},
"require": {
"php": "^5.6.4 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7 || ^6.3"
},
"suggest": {
"ext-bcmatch": "Required to use BC Math arbitrary precision mathematics (*).",
"ext-gmp": "Required to use GNU multiple precision mathematics (*)."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
}
},
"autoload": {
"psr-4": {
"Hashids\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ivan Akimov",
"email": "ivan@barreleye.com",
"homepage": "https://twitter.com/IvanAkimov"
},
{
"name": "Vincent Klaiber",
"email": "hello@vinkla.com",
"homepage": "https://vinkla.com"
}
],
"description": "Generate short, unique, non-sequential ids (like YouTube and Bitly) from numbers",
"homepage": "http://hashids.org/php",
"keywords": [
"bitly",
"decode",
"encode",
"hash",
"hashid",
"hashids",
"ids",
"obfuscate",
"youtube"
],
"time": "2017-10-28T11:24:20+00:00"
},
{
"name": "laminas/laminas-escaper",
"version": "2.6.1",
@ -4070,6 +4136,7 @@
"php": ">=7.1.0",
"ext-curl": "*",
"ext-gd": "*",
"ext-gmp": "*",
"ext-json": "*",
"ext-pdo": "*"
},

View file

@ -9,10 +9,6 @@ use \Friendica\Directory\Content\Pager;
*/
class Search extends BaseController
{
/**
* @var \Atlas\Pdo\Connection
*/
private $atlas;
/**
* @var \Friendica\Directory\Models\Profile
*/
@ -31,14 +27,12 @@ class Search extends BaseController
private $l10n;
public function __construct(
\Atlas\Pdo\Connection $atlas,
\Friendica\Directory\Models\Profile $profileModel,
\Friendica\Directory\Views\Widget\AccountTypeTabs $accountTypeTabs,
\Friendica\Directory\Views\PhpRenderer $renderer,
\Gettext\TranslatorInterface $l10n
)
{
$this->atlas = $atlas;
$this->profileModel = $profileModel;
$this->accountTypeTabs = $accountTypeTabs;
$this->renderer = $renderer;

View file

@ -0,0 +1,63 @@
<?php
namespace Friendica\Directory\Controllers\Web;
use Slim\Exception\NotFoundException;
class Server extends BaseController
{
/** @var \Friendica\Directory\Models\Server */
private $serverModel;
/** @var \Friendica\Directory\Models\SiteScrape */
private $siteScrapeModel;
/** @var \Friendica\Directory\Views\PhpRenderer */
private $renderer;
/** @var \Gettext\TranslatorInterface */
private $l10n;
/** @var \Psr\SimpleCache\CacheInterface */
private $simplecache;
public function __construct(\Friendica\Directory\Models\Server $serverModel, \Friendica\Directory\Models\SiteScrape $siteScrapeModel, \Friendica\Directory\Views\PhpRenderer $renderer, \Gettext\TranslatorInterface $l10n, \Psr\SimpleCache\CacheInterface $simplecache)
{
$this->serverModel = $serverModel;
$this->siteScrapeModel = $siteScrapeModel;
$this->renderer = $renderer;
$this->l10n = $l10n;
$this->simplecache = $simplecache;
}
function render(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args): array
{
$server = $this->serverModel->getByHashId($args['hashid']);
if (empty($server) || !$server['available'] || $server['hidden']) {
throw new NotFoundException($request, $response);
}
$stable_version = $this->simplecache->get('stable_version');
if (!$stable_version) {
$stable_version = trim(file_get_contents('https://git.friendi.ca/friendica/friendica/raw/branch/stable/VERSION'));
$this->simplecache->set('stable_version', $stable_version);
}
$dev_version = $this->simplecache->get('dev_version');
if (!$dev_version) {
$dev_version = trim(file_get_contents('https://git.friendi.ca/friendica/friendica/raw/branch/develop/VERSION'));
$this->simplecache->set('dev_version', $dev_version);
}
$rc_version = str_replace('-dev', '-rc', $dev_version);
$vars = [
'server' => $server,
'stable_version' => $stable_version,
'dev_version' => $dev_version,
'rc_version' => $rc_version,
];
$content = $this->renderer->fetch('server.phtml', $vars);
// Render index view
return ['content' => $content];
}
}

View file

@ -29,9 +29,13 @@ class Servers extends BaseController
* @var \Psr\SimpleCache\CacheInterface
*/
private $simplecache;
/**
* @var \Friendica\Directory\Models\Server
*/
private $serverModel;
public function __construct(
\Atlas\Pdo\Connection $atlas,
\Atlas\Pdo\Connection $atlas, \Friendica\Directory\Models\Server $serverModel,
\Friendica\Directory\Views\PhpRenderer $renderer,
\Gettext\TranslatorInterface $l10n,
\Psr\SimpleCache\CacheInterface $simplecache
@ -41,6 +45,7 @@ class Servers extends BaseController
$this->renderer = $renderer;
$this->l10n = $l10n;
$this->simplecache = $simplecache;
$this->serverModel = $serverModel;
}
public function render(Request $request, Response $response, array $args): array
@ -63,43 +68,9 @@ class Servers extends BaseController
$pager = new Pager($this->l10n, $request, 20);
$sql_where = '';
$values = [];
$servers = $this->serverModel->listPageForDirectory($pager->getStart(), $pager->getItemsPerPage(), $args['language'] ?? '');
if ($args['language']) {
$sql_where .= '
AND LEFT(`language`, 2) = LEFT(:language, 2)';
$values['language'] = $args['language'];
}
$stmt = 'SELECT *
FROM `server` s
WHERE `reg_policy` != "REGISTER_CLOSED"
AND `available`
AND NOT `hidden`
' . $sql_where . '
ORDER BY `health_score` DESC, `ssl_state` DESC, `info` != "" DESC, `last_seen` DESC
LIMIT :start, :limit';
$listValues = array_merge($values, [
'start' => [$pager->getStart(), PDO::PARAM_INT],
'limit' => [$pager->getItemsPerPage(), PDO::PARAM_INT]
]);
$servers = $this->atlas->fetchAll($stmt, $listValues);
foreach ($servers as $key => $server) {
$servers[$key]['user_count'] = $this->atlas->fetchValue(
'SELECT COUNT(*) FROM `profile` WHERE `available` AND NOT `hidden` AND `server_id` = :server_id',
['server_id' => [$server['id'], PDO::PARAM_INT]]
);
}
$stmt = 'SELECT COUNT(*)
FROM `server` s
WHERE `reg_policy` != "REGISTER_CLOSED"
AND `available`
AND NOT `hidden`
' . $sql_where;
$count = $this->atlas->fetchValue($stmt, $values);
$count = $this->serverModel->countForDirectory($args['language'] ?? '');
$vars = [
'title' => $this->l10n->gettext('Public Servers'),

View file

@ -2,11 +2,105 @@
namespace Friendica\Directory\Models;
use Hashids\Hashids;
use PDO;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Server extends \Friendica\Directory\Model
{
/** @var Hashids */
private $hashids;
public function __construct(\Atlas\Pdo\Connection $atlas, Hashids $hashids)
{
parent::__construct($atlas);
$this->hashids = $hashids;
}
/**
* @param int $start
* @param int $limit
* @param string|null $language
* @return array
*/
public function listPageForDirectory(int $start = 0, int $limit = 50, string $language = null): array
{
$sql_where = '';
$values = [];
if ($language) {
$sql_where .= '
AND LEFT(`language`, 2) = LEFT(:language, 2)';
$values['language'] = $language;
}
$stmt = 'SELECT *
FROM `server` s
WHERE `reg_policy` != "REGISTER_CLOSED"
AND `available`
AND NOT `hidden`
' . $sql_where . '
ORDER BY `health_score` DESC, `ssl_state` DESC, `info` != "" DESC, `last_seen` DESC
LIMIT :start, :limit';
$listValues = array_merge($values, [
'start' => [$start, PDO::PARAM_INT],
'limit' => [$limit, PDO::PARAM_INT]
]);
$servers = $this->atlas->fetchAll($stmt, $listValues);
array_walk($servers, function (&$server) {
$server['hashid'] = $this->hashids->encode($server['id']);
$server['user_count'] = $this->atlas->fetchValue(
'SELECT COUNT(*) FROM `profile` WHERE `available` AND NOT `hidden` AND `server_id` = :server_id',
['server_id' => [$server['id'], PDO::PARAM_INT]]
);
});
return $servers;
}
/**
* @param null $language
* @return int
*/
public function countForDirectory($language = null): int
{
$sql_where = '';
$values = [];
if ($language) {
$sql_where .= '
AND LEFT(`language`, 2) = LEFT(:language, 2)';
$values['language'] = $language;
}
$stmt = 'SELECT COUNT(*)
FROM `server` s
WHERE `reg_policy` != "REGISTER_CLOSED"
AND `available`
AND NOT `hidden`
' . $sql_where;
return $this->atlas->fetchValue($stmt, $values);
}
/**
* @param string $hashid
* @return array
*/
public function getByHashId(string $hashid): array
{
$serverId = $this->hashids->decode($hashid)[0];
$server = $this->atlas->fetchOne('SELECT * FROM `server` WHERE `id` = :id',
['id' => $serverId]
);
return $server;
}
/**
* @param string $server_url
* @return array|null
@ -23,6 +117,7 @@ class Server extends \Friendica\Directory\Model
}
/**
* @param int $server_id
* @param string $server_url
*/
public function addAliasToServer(int $server_id, string $server_url): void

View file

@ -0,0 +1,29 @@
<?php
namespace Friendica\Directory\Models;
use Hashids\Hashids;
use PDO;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class SiteScrape extends \Friendica\Directory\Model
{
/**
* @param int $serverId
* @return array
*/
public function listByServerIdy(int $serverId): array
{
$stmt = 'SELECT *
FROM `site_scrape`
WHERE `server_id` = :serverId';
$values = [
'serverId' => $serverId,
];
$siteScrapes = $this->atlas->fetchAll($stmt, $values);
return $siteScrapes;
}
}

View file

@ -12,7 +12,6 @@ class Search extends BaseRoute
parent::__construct($container);
$this->controller = new \Friendica\Directory\Controllers\Web\Search(
$this->container->atlas,
$this->container->get(\Friendica\Directory\Models\Profile::class),
$this->container->get(\Friendica\Directory\Views\Widget\AccountTypeTabs::class),
$this->container->renderer,

View file

@ -0,0 +1,22 @@
<?php
namespace Friendica\Directory\Routes\Web;
/**
* @author Hypolite Petovan <hypolite@mrpetovan.com>
*/
class Server extends BaseRoute
{
public function __construct(\Slim\Container $container)
{
parent::__construct($container);
$this->controller = new \Friendica\Directory\Controllers\Web\Server(
$this->container->get(\Friendica\Directory\Models\Server::class),
$this->container->get(\Friendica\Directory\Models\SiteScrape::class),
$this->container->renderer,
$this->container->l10n,
$this->container->simplecache,
);
}
}

View file

@ -13,6 +13,7 @@ class Servers extends BaseRoute
$this->controller = new \Friendica\Directory\Controllers\Web\Servers(
$this->container->atlas,
$this->container->get(\Friendica\Directory\Models\Server::class),
$this->container->renderer,
$this->container->l10n,
$this->container->simplecache

View file

@ -27,7 +27,7 @@ use Slim\Router;
class PhpRenderer extends \Slim\Views\PhpRenderer
{
/**
* @var \Zend\Escaper\Escaper
* @var \Laminas\Escaper\Escaper
*/
private $escaper;
/**
@ -40,7 +40,7 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
private $router;
public function __construct(
\Zend\Escaper\Escaper $escaper,
\Laminas\Escaper\Escaper $escaper,
\Gettext\TranslatorInterface $l10n,
Router $router,
string $templatePath = "",
@ -188,12 +188,16 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
* @param array $queryParams
* @return string
*/
function r(string $name, array $data = [], array $queryParams = [])
function r(string $name, array $data = [], array $queryParams = []): string
{
if ($this->getAttribute('zrl')) {
$queryParams['zrl'] = $this->getAttribute('zrl');
}
array_walk($data, function(&$value) {
$value = urlencode($value);
});
return $this->router->pathFor($name, $data, $queryParams);
}
@ -201,8 +205,9 @@ class PhpRenderer extends \Slim\Views\PhpRenderer
* Add sitewide ZRL support for external URLs
*
* @param string $url
* @return string
*/
function u(string $url)
function u(string $url): string
{
if ($this->getAttribute('zrl')) {
$uri = new \ByJG\Util\Uri($url);

View file

@ -100,9 +100,14 @@ $container[\Friendica\Directory\Models\ProfilePollQueue::class] = function (Cont
};
$container[\Friendica\Directory\Models\Server::class] = function (ContainerInterface $c): Friendica\Directory\Models\Server {
return new Friendica\Directory\Models\Server($c->get('atlas'));
return new Friendica\Directory\Models\Server($c->get('atlas'), $c->get('hashids'));
};
$container[\Friendica\Directory\Models\SiteScrape::class] = function (ContainerInterface $c): Friendica\Directory\Models\SiteScrape {
return new Friendica\Directory\Models\SiteScrape($c->get('atlas'));
};
$container[\Friendica\Directory\Pollers\Directory::class] = function (ContainerInterface $c): Friendica\Directory\Pollers\Directory {
$settings = $c->get('settings')['poller'];
return new Friendica\Directory\Pollers\Directory(
@ -143,3 +148,7 @@ $container[\Friendica\Directory\Views\Widget\AccountTypeTabs::class] = function
$c->get('router')
);
};
$container['hashids'] = function (ContainerInterface $c): \Hashids\Hashids {
return new \Hashids\Hashids($_SERVER['HTTP_HOST'], 5);
};

View file

@ -1,5 +1,7 @@
<?php
use Friendica\Directory\Routes;
use Slim\Http\Request;
use Slim\Http\Response;
@ -9,29 +11,29 @@ use Slim\Http\Response;
* @var $app \Slim\App
*/
$app->get('/servers/surprise', \Friendica\Directory\Routes\Http\Surprise::class);
$app->get('/servers[/{language}]', \Friendica\Directory\Routes\Web\Servers::class)->setName('servers');
$app->get('/servers/surprise', Routes\Http\Surprise::class);
$app->get('/servers[/{language}]', Routes\Web\Servers::class)->setName('servers');
$app->get('/search[/{account_type}]', function (Request $request, Response $response, $args) {
if ($request->getAttribute('negotiation')->getMediaType() == 'application/json') {
$route = new \Friendica\Directory\Routes\Http\Search($this);
$route = new Routes\Http\Search($this);
} else {
$route = new \Friendica\Directory\Routes\Web\Search($this);
$route = new Routes\Web\Search($this);
}
return $route($request, $response, $args);
})->setName('search');
$app->post('/msearch', \Friendica\Directory\Routes\Http\MatchSearch::class);
$app->post('/msearch', Routes\Http\MatchSearch::class);
$app->get('/stats', \Friendica\Directory\Routes\Web\Statistics::class)->setName('stats');
$app->get('/stats', Routes\Web\Statistics::class)->setName('stats');
$app->get('/submit', \Friendica\Directory\Routes\Http\Submit::class);
$app->get('/submit', Routes\Http\Submit::class);
$app->get('/photo/{profile_id:[0-9]+}.jpg', \Friendica\Directory\Routes\Http\Photo::class)->setName('photo');
$app->get('/photo/{profile_id:[0-9]+}.jpg', Routes\Http\Photo::class)->setName('photo');
$app->get('/sync/pull/all', \Friendica\Directory\Routes\Http\SyncPull::class);
$app->get('/sync/pull/since/{since}', \Friendica\Directory\Routes\Http\SyncPull::class);
$app->get('/sync/pull/all', Routes\Http\SyncPull::class);
$app->get('/sync/pull/since/{since}', Routes\Http\SyncPull::class);
$app->get('/VERSION', function (Request $request, Response $response) {
$response->getBody()->write(file_get_contents(__DIR__ . '/../VERSION'));
@ -42,10 +44,12 @@ $app->get('/VERSION', function (Request $request, Response $response) {
foreach(glob(__DIR__ . '/../config/pages/*.html') as $page) {
$app->get('/' . strtolower(basename($page, '.html')), function (Request $request, Response $response, $args) use ($page) {
$route = new \Friendica\Directory\Routes\Web\Pages($this, $page);
$route = new Routes\Web\Pages($this, $page);
return $route($request, $response, $args);
});
}
$app->get('/[{account_type}]', \Friendica\Directory\Routes\Web\Directory::class)->setName('directory');
$app->get('/[{account_type}]', Routes\Web\Directory::class)->setName('directory');
$app->get('/server/{hashid}', Routes\Web\Server::class)->setName('server');

View file

112
src/templates/server.phtml Normal file
View file

@ -0,0 +1,112 @@
<?php
if ($server['health_score'] <= 0) {
$badge_class = 'badge-dark';
} elseif ($server['health_score'] <= 50) {
$badge_class = 'badge-danger';
} elseif ($server['health_score'] <= 80) {
$badge_class = 'badge-warning';
} else {
$badge_class = 'badge-success';
}
if ($server['version'] == $stable_version) {
$version_badge = '<span class="badge badge-success"><i class="fa fa-smile"></i> ' . $this->__('Stable Version') . '</span>';
} elseif ($server['version'] == $dev_version || $server['version'] == $rc_version) {
$version_badge = '<span class="badge badge-secondary"><i class="fa fa-poo"></i> ' . $this->__('Develop Version') . '</span>';
} else {
$version_badge = '<span class="badge badge-warning"><i class="fa fa-frown"></i> ' . $this->__('Outdated Version') . '</span>';
}
$base_url = $server['base_url'];
$base_url_display = substr($base_url, strpos($base_url, '/') + 2);
?>
<div class="row">
<h1 class="col-lg-10"><?php echo $this->e($server['name']) ?></h1>
<div class="col-lg-2">
<a href="<?php echo $this->escapeHtmlAttr($this->u($base_url)); ?>" class="btn btn-primary"><i class="fa fa-external-link-alt"></i>
<?php echo $this->__('Visit Server')?>
</a>
</div>
</div>
<h2 class="card-subtitle mb-2 text-muted">
<?php if ($server['ssl_state']): ?>
<span class="badge badge-success"><i class="fa fa-lock"></i> HTTPS</span>
<?php else: ?>
<span class="badge badge-secondary"><i class="fa fa-lock-open"></i> HTTP</span>
<?php endif; ?>
<a href="<?php echo $this->escapeHtmlAttr($this->u($base_url)); ?>"><?php echo $this->e($base_url_display); ?></a>
</h2>
<dl>
<div class="row">
<dt class="col-sm-2"><i class="fa fa-heartbeat"></i> <?php echo $this->__('Health Score')?></dt>
<dd class="col-sm-10">
<span class="badge <?php echo $badge_class ?>">
<?php echo $server['health_score'] ?>
</span>
</dd>
</div>
<?php if ($server['language']):?>
<div class="row">
<dt class="col-sm-2"><i class="fa fa-language"></i> <?php echo $this->__('Default Language')?></dt>
<dd class="col-sm-10">
<a href="<?php echo $this->r('servers', ['language' => $server['language']])?>">
<?php echo $this->e(Friendica\Directory\Utils\L10n::localeToLanguageString($server['language'])) ?>
</a>
</dd>
</div>
<?php endif;?>
<div class="row">
<dt class="col-sm-2"><i class="fa fa-user"></i> <?php echo $this->__('Known Users')?></dt>
<dd class="col-sm-10">
<?php if ($server['user_count']):?>
<?php echo $this->n__('%s User', '%s Users', $server['user_count'])?>
<?php else: ?>
<?php echo $this->__('None');?>
<?php endif; ?>
</dd>
</div>
<div class="row">
<dt class="col-sm-2"><?php echo $this->__('Registration Policy')?></dt>
<dd class="col-sm-10">
<?php if ($server['reg_policy'] == 'REGISTER_APPROVE'):?>
<span class="badge badge-primary">
<i class="fa fa-dungeon"></i> <?php echo $this->__('By Approval')?>
</span>
<?php else:?>
<span class="badge badge-success">
<i class="fa fa-door-open"></i> <?php echo $this->__('Open')?>
</span>
<?php endif;?>
</dd>
</div>
<div class="row">
<dt class="col-sm-2"><?php echo $this->__('Friendica Version')?></dt>
<dd class="col-sm-10">
<?php echo $server['version']; ?> <?php echo $version_badge; ?>
</dd>
</div>
<?php if ($server['admin_profile'] && $server['admin_name']): ?>
<div class="row">
<dt class="col-sm-2"><?php echo $this->__('Administrator')?></dt>
<dd class="col-sm-10">
<a href="<?php echo $this->escapeHtmlAttr($this->u($server['admin_profile'])); ?>" class="badge badge-primary">
<i class="fa fa-star"></i> <?php echo $this->e($server['admin_name']); ?>
</a>
</dd>
</div>
<?php endif; ?>
<div class="row">
<dt class="col-sm-2"><?php echo $this->__('Description')?></dt>
<dd class="col-sm-10">
<?php if ($server['info']) : ?>
<?php echo $this->e($server['info']); ?>
<?php else: ?>
<span class="text-muted"><?php echo $this->__('No description provided')?></span>
<?php endif; ?>
</dd>
</div>
</dl>

View file

@ -26,7 +26,7 @@ $base_url_display = substr($base_url, strpos($base_url, '/') + 2);
<div class="card mr-2 mb-2 bg-light" id="server-card-<?php echo $server['id'] ?>">
<div class="card-body">
<h5 class="card-title">
<?php echo $this->e($server['name']); ?>
<a href="<?php echo $this->r('server', ['hashid' => $server['hashid']])?>"><?php echo $this->e($server['name']); ?></a>
</h5>
<h6 class="card-subtitle mb-2 text-muted">
<?php if ($server['ssl_state']): ?>