mirror of
https://github.com/shlinkio/shlink.git
synced 2025-02-25 18:45:27 -06:00
Added option to automatically resolve url titles
This commit is contained in:
parent
356e68ca3e
commit
8b54098299
@ -19,6 +19,7 @@ return [
|
||||
'default_short_codes_length' => DEFAULT_SHORT_CODES_LENGTH,
|
||||
'redirect_status_code' => DEFAULT_REDIRECT_STATUS_CODE,
|
||||
'redirect_cache_lifetime' => DEFAULT_REDIRECT_CACHE_LIFETIME,
|
||||
'auto_resolve_titles' => false, // Deprecated value. Default to true with Shlink 3.0.0
|
||||
],
|
||||
|
||||
];
|
||||
|
@ -167,4 +167,17 @@ final class ShortUrlMeta
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function hasTitle(): bool
|
||||
{
|
||||
return $this->title !== null;
|
||||
}
|
||||
|
||||
public function withResolvedTitle(?string $title): self
|
||||
{
|
||||
$copy = clone $this;
|
||||
$copy->title = $title;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class UrlShortenerOptions extends AbstractOptions
|
||||
private bool $validateUrl = true;
|
||||
private int $redirectStatusCode = DEFAULT_REDIRECT_STATUS_CODE;
|
||||
private int $redirectCacheLifetime = DEFAULT_REDIRECT_CACHE_LIFETIME;
|
||||
private bool $autoResolveTitles = false; // Deprecated value. Default to true with Shlink 3.0.0
|
||||
|
||||
public function isUrlValidationEnabled(): bool
|
||||
{
|
||||
@ -55,4 +56,15 @@ class UrlShortenerOptions extends AbstractOptions
|
||||
? $redirectCacheLifetime
|
||||
: DEFAULT_REDIRECT_CACHE_LIFETIME;
|
||||
}
|
||||
|
||||
public function autoResolveTitles(): bool
|
||||
{
|
||||
return $this->autoResolveTitles;
|
||||
}
|
||||
|
||||
protected function setAutoResolveTitles(bool $autoResolveTitles): self
|
||||
{
|
||||
$this->autoResolveTitles = $autoResolveTitles;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
|
||||
use Shlinkio\Shlink\Core\Service\ShortUrl\ShortCodeHelperInterface;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface;
|
||||
use Shlinkio\Shlink\Core\Util\UrlValidatorInterface;
|
||||
use Throwable;
|
||||
|
||||
class UrlShortener implements UrlShortenerInterface
|
||||
{
|
||||
@ -37,7 +36,6 @@ class UrlShortener implements UrlShortenerInterface
|
||||
/**
|
||||
* @throws NonUniqueSlugException
|
||||
* @throws InvalidUrlException
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function shorten(ShortUrlMeta $meta): ShortUrl
|
||||
{
|
||||
@ -47,7 +45,13 @@ class UrlShortener implements UrlShortenerInterface
|
||||
return $existingShortUrl;
|
||||
}
|
||||
|
||||
$this->urlValidator->validateUrl($meta->getLongUrl(), $meta->doValidateUrl());
|
||||
if ($meta->hasTitle()) {
|
||||
$this->urlValidator->validateUrl($meta->getLongUrl(), $meta->doValidateUrl());
|
||||
} else {
|
||||
$meta = $meta->withResolvedTitle(
|
||||
$this->urlValidator->validateUrlWithTitle($meta->getLongUrl(), $meta->doValidateUrl()),
|
||||
);
|
||||
}
|
||||
|
||||
return $this->em->transactional(function () use ($meta) {
|
||||
$shortUrl = ShortUrl::fromMeta($meta, $this->relationResolver);
|
||||
|
@ -8,9 +8,12 @@ use Fig\Http\Message\RequestMethodInterface;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
|
||||
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
|
||||
|
||||
use function preg_match;
|
||||
|
||||
class UrlValidator implements UrlValidatorInterface, RequestMethodInterface
|
||||
{
|
||||
private const MAX_REDIRECTS = 15;
|
||||
@ -35,13 +38,36 @@ class UrlValidator implements UrlValidatorInterface, RequestMethodInterface
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validateUrlAndGetResponse($url, true);
|
||||
}
|
||||
|
||||
public function validateUrlWithTitle(string $url, ?bool $doValidate): ?string
|
||||
{
|
||||
$doValidate = $doValidate ?? $this->options->isUrlValidationEnabled();
|
||||
$response = $this->validateUrlAndGetResponse($url, $doValidate);
|
||||
|
||||
if ($response === null || ! $this->options->autoResolveTitles()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$body = $response->getBody()->__toString();
|
||||
preg_match('/<title>(.+)<\/title>/i', $body, $matches);
|
||||
return $matches[1] ?? null;
|
||||
}
|
||||
|
||||
private function validateUrlAndGetResponse(string $url, bool $throwOnError): ?ResponseInterface
|
||||
{
|
||||
try {
|
||||
$this->httpClient->request(self::METHOD_GET, $url, [
|
||||
return $this->httpClient->request(self::METHOD_GET, $url, [
|
||||
RequestOptions::ALLOW_REDIRECTS => ['max' => self::MAX_REDIRECTS],
|
||||
RequestOptions::IDN_CONVERSION => true,
|
||||
]);
|
||||
} catch (GuzzleException $e) {
|
||||
throw InvalidUrlException::fromUrl($url, $e);
|
||||
if ($throwOnError) {
|
||||
throw InvalidUrlException::fromUrl($url, $e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,9 @@ interface UrlValidatorInterface
|
||||
* @throws InvalidUrlException
|
||||
*/
|
||||
public function validateUrl(string $url, ?bool $doValidate): void;
|
||||
|
||||
/**
|
||||
* @throws InvalidUrlException
|
||||
*/
|
||||
public function validateUrlWithTitle(string $url, ?bool $doValidate): ?string;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class UrlShortenerTest extends TestCase
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->urlValidator = $this->prophesize(UrlValidatorInterface::class);
|
||||
$this->urlValidator->validateUrl('http://foobar.com/12345/hello?foo=bar', null)->will(
|
||||
$this->urlValidator->validateUrlWithTitle('http://foobar.com/12345/hello?foo=bar', null)->will(
|
||||
function (): void {
|
||||
},
|
||||
);
|
||||
@ -101,7 +101,7 @@ class UrlShortenerTest extends TestCase
|
||||
$findExisting->shouldHaveBeenCalledOnce();
|
||||
$getRepo->shouldHaveBeenCalledOnce();
|
||||
$this->em->persist(Argument::cetera())->shouldNotHaveBeenCalled();
|
||||
$this->urlValidator->validateUrl(Argument::cetera())->shouldNotHaveBeenCalled();
|
||||
$this->urlValidator->validateUrlWithTitle(Argument::cetera())->shouldNotHaveBeenCalled();
|
||||
self::assertSame($expected, $result);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user