[TOOLS] Run CS-fixer on all files
This commit is contained in:
parent
5e42723624
commit
ec28f23025
@ -30,12 +30,12 @@ use App\Core\GSFile;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Log;
|
||||
use App\Core\Router\Router;
|
||||
use Component\Attachment\Entity\AttachmentThumbnail;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\ClientException;
|
||||
use App\Util\Exception\NoSuchFileException;
|
||||
use App\Util\Exception\NotFoundException;
|
||||
use App\Util\Exception\ServerException;
|
||||
use Component\Attachment\Entity\AttachmentThumbnail;
|
||||
use Symfony\Component\HttpFoundation\HeaderUtils;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
@ -39,7 +41,7 @@ class ActorToAttachment extends Entity
|
||||
// @codeCoverageIgnoreStart
|
||||
private int $attachment_id;
|
||||
private int $actor_id;
|
||||
private \DateTimeInterface $modified;
|
||||
private DateTimeInterface $modified;
|
||||
|
||||
public function setAttachmentId(int $attachment_id): self
|
||||
{
|
||||
@ -77,10 +79,6 @@ class ActorToAttachment extends Entity
|
||||
// @codeCoverageIgnoreEnd
|
||||
// }}} Autocode
|
||||
|
||||
/**
|
||||
* @param int $attachment_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhereAttachmentId(int $attachment_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
@ -92,11 +90,6 @@ class ActorToAttachment extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $actor_id
|
||||
* @param int $attachment_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhere(int $attachment_id, int $actor_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
@ -109,10 +102,6 @@ class ActorToAttachment extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $actor_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhereActorId(int $actor_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -26,10 +28,10 @@ use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use App\Core\Event;
|
||||
use App\Core\GSFile;
|
||||
use App\Entity\Note;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Log;
|
||||
use App\Core\Router\Router;
|
||||
use App\Entity\Note;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\ClientException;
|
||||
use App\Util\Exception\DuplicateFoundException;
|
||||
@ -77,17 +79,11 @@ class Attachment extends Entity
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLives(): int
|
||||
{
|
||||
return $this->lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lives
|
||||
*/
|
||||
public function setLives(int $lives): void
|
||||
{
|
||||
$this->lives = $lives;
|
||||
@ -176,39 +172,31 @@ class Attachment extends Entity
|
||||
public function getMimetypeMajor(): ?string
|
||||
{
|
||||
$mime = $this->getMimetype();
|
||||
return is_null($mime) ? $mime : GSFile::mimetypeMajor($mime);
|
||||
return \is_null($mime) ? $mime : GSFile::mimetypeMajor($mime);
|
||||
}
|
||||
|
||||
public function getMimetypeMinor(): ?string
|
||||
{
|
||||
$mime = $this->getMimetype();
|
||||
return is_null($mime) ? $mime : GSFile::mimetypeMinor($mime);
|
||||
return \is_null($mime) ? $mime : GSFile::mimetypeMinor($mime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function livesIncrementAndGet(): int
|
||||
{
|
||||
++$this->lives;
|
||||
return $this->lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function livesDecrementAndGet(): int
|
||||
{
|
||||
--$this->lives;
|
||||
return $this->lives;
|
||||
}
|
||||
|
||||
const FILEHASH_ALGO = 'sha256';
|
||||
public const FILEHASH_ALGO = 'sha256';
|
||||
|
||||
/**
|
||||
* Delete a file if safe, removes dependencies, cleanups and flushes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function kill(): bool
|
||||
{
|
||||
@ -223,7 +211,7 @@ class Attachment extends Entity
|
||||
*/
|
||||
public function deleteStorage(): bool
|
||||
{
|
||||
if (!is_null($filepath = $this->getPath())) {
|
||||
if (!\is_null($filepath = $this->getPath())) {
|
||||
if (file_exists($filepath)) {
|
||||
if (@unlink($filepath) === false) {
|
||||
// @codeCoverageIgnoreStart
|
||||
@ -248,6 +236,7 @@ class Attachment extends Entity
|
||||
|
||||
/**
|
||||
* Attachment delete always removes dependencies, cleanups and flushes
|
||||
*
|
||||
* @see kill() It's more likely that you want to use that rather than call delete directly
|
||||
*/
|
||||
protected function delete(): bool
|
||||
@ -261,7 +250,7 @@ class Attachment extends Entity
|
||||
|
||||
// Collect files starting with the one associated with this attachment
|
||||
$files = [];
|
||||
if (!is_null($filepath = $this->getPath())) {
|
||||
if (!\is_null($filepath = $this->getPath())) {
|
||||
$files[] = $filepath;
|
||||
}
|
||||
|
||||
@ -306,25 +295,21 @@ class Attachment extends Entity
|
||||
/**
|
||||
* TODO: Maybe this isn't the best way of handling titles
|
||||
*
|
||||
* @param null|Note $note
|
||||
*
|
||||
* @throws DuplicateFoundException
|
||||
* @throws NotFoundException
|
||||
* @throws ServerException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBestTitle(?Note $note = null): string
|
||||
{
|
||||
// If we have a note, then the best title is the title itself
|
||||
if (!is_null(($note))) {
|
||||
if (!\is_null(($note))) {
|
||||
$title = Cache::get('attachment-title-' . $this->getId() . '-' . $note->getId(), function () use ($note) {
|
||||
try {
|
||||
$attachment_to_note = DB::findOneBy('attachment_to_note', [
|
||||
'attachment_id' => $this->getId(),
|
||||
'note_id' => $note->getId(),
|
||||
]);
|
||||
if (!is_null($attachment_to_note->getTitle())) {
|
||||
if (!\is_null($attachment_to_note->getTitle())) {
|
||||
return $attachment_to_note->getTitle();
|
||||
}
|
||||
} catch (NotFoundException) {
|
||||
@ -332,14 +317,14 @@ class Attachment extends Entity
|
||||
Event::handle('AttachmentGetBestTitle', [$this, $note, &$title]);
|
||||
return $title;
|
||||
}
|
||||
return null;
|
||||
|
||||
});
|
||||
if ($title != null) {
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
// Else
|
||||
if (!is_null($filename = $this->getFilename())) {
|
||||
if (!\is_null($filename = $this->getFilename())) {
|
||||
// A filename would do just as well
|
||||
return $filename;
|
||||
} else {
|
||||
@ -359,7 +344,7 @@ class Attachment extends Entity
|
||||
public function getPath()
|
||||
{
|
||||
$filename = $this->getFilename();
|
||||
return is_null($filename) ? null : Common::config('attachments', 'dir') . DIRECTORY_SEPARATOR . $filename;
|
||||
return \is_null($filename) ? null : Common::config('attachments', 'dir') . \DIRECTORY_SEPARATOR . $filename;
|
||||
}
|
||||
|
||||
public function getUrl(int $type = Router::ABSOLUTE_URL): string
|
||||
@ -368,9 +353,6 @@ class Attachment extends Entity
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $size
|
||||
* @param bool $crop
|
||||
*
|
||||
* @throws ClientException
|
||||
* @throws NotFoundException
|
||||
* @throws ServerException
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
@ -39,7 +41,7 @@ class AttachmentToLink extends Entity
|
||||
// @codeCoverageIgnoreStart
|
||||
private int $attachment_id;
|
||||
private int $link_id;
|
||||
private \DateTimeInterface $modified;
|
||||
private DateTimeInterface $modified;
|
||||
|
||||
public function setAttachmentId(int $attachment_id): self
|
||||
{
|
||||
@ -77,10 +79,6 @@ class AttachmentToLink extends Entity
|
||||
// @codeCoverageIgnoreEnd
|
||||
// }}} Autocode
|
||||
|
||||
/**
|
||||
* @param int $attachment_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhereAttachmentId(int $attachment_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
@ -92,11 +90,6 @@ class AttachmentToLink extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $link_id
|
||||
* @param int $attachment_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhere(int $link_id, int $attachment_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
@ -109,10 +102,6 @@ class AttachmentToLink extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $link_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhereLinkId(int $link_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
@ -19,7 +21,6 @@
|
||||
|
||||
namespace Component\Attachment\Entity;
|
||||
|
||||
use App\Core\Cache;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use DateTimeInterface;
|
||||
@ -45,7 +46,7 @@ class AttachmentToNote extends Entity
|
||||
private int $attachment_id;
|
||||
private int $note_id;
|
||||
private ?string $title;
|
||||
private \DateTimeInterface $modified;
|
||||
private DateTimeInterface $modified;
|
||||
|
||||
public function setAttachmentId(int $attachment_id): self
|
||||
{
|
||||
@ -94,11 +95,6 @@ class AttachmentToNote extends Entity
|
||||
// @codeCoverageIgnoreEnd
|
||||
// }}} Autocode
|
||||
|
||||
/**
|
||||
* @param int $note_id
|
||||
* @param int $attachment_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhere(int $note_id, int $attachment_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
@ -111,10 +107,6 @@ class AttachmentToNote extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $note_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhereNoteId(int $note_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
@ -126,10 +118,6 @@ class AttachmentToNote extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attachment_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhereAttachmentId(int $attachment_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
|
@ -27,8 +27,8 @@ use App\Core\Cache;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use App\Core\Router\Router;
|
||||
use Component\Attachment\Entity\Attachment;
|
||||
use App\Util\Common;
|
||||
use Component\Attachment\Entity\Attachment;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
/**
|
||||
* @author James Walker <james@status.net>
|
||||
* @author Craig Andrews <candrews@integralblue.com>
|
||||
@ -8,13 +10,9 @@
|
||||
|
||||
namespace Component\FreeNetwork\Controller;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Core\Event;
|
||||
use Component\FreeNetwork\Util\Discovery;
|
||||
use Component\FreeNetwork\Util\XrdController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use XML_XRD;
|
||||
|
||||
class HostMeta extends XrdController
|
||||
{
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
// GNU social is free software: you can redistribute it and/or modify
|
||||
@ -30,7 +32,6 @@ use App\Util\Common;
|
||||
use Component\FreeNetwork\Util\Discovery;
|
||||
use Component\FreeNetwork\Util\XrdController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class OwnerXrd extends XrdController
|
||||
{
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
/*
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2010, StatusNet, Inc.
|
||||
@ -69,7 +71,7 @@ class Webfinger extends XrdController
|
||||
$this->xrd->subject = $this->resource;
|
||||
|
||||
foreach ($this->target->getAliases() as $alias) {
|
||||
if ($alias != $this->xrd->subject && !in_array($alias, $this->xrd->aliases)) {
|
||||
if ($alias != $this->xrd->subject && !\in_array($alias, $this->xrd->aliases)) {
|
||||
$this->xrd->aliases[] = $alias;
|
||||
}
|
||||
}
|
||||
|
@ -32,17 +32,11 @@ declare(strict_types = 1);
|
||||
|
||||
namespace Component\FreeNetwork\Entity;
|
||||
|
||||
use App\Core\Cache;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Log;
|
||||
use App\Entity\Actor;
|
||||
use Component\FreeNetwork\Util\Discovery;
|
||||
use DateTimeInterface;
|
||||
use Exception;
|
||||
use Plugin\ActivityPub\Util\DiscoveryHints;
|
||||
use Plugin\ActivityPub\Util\Explorer;
|
||||
|
||||
/**
|
||||
* Table Definition for free_network_actor_protocol
|
||||
@ -119,9 +113,9 @@ class FreeNetworkActorProtocol extends Entity
|
||||
|
||||
public static function protocolSucceeded(string $protocol, int|Actor $actor_id, string $addr): void
|
||||
{
|
||||
$actor_id = is_int($actor_id) ? $actor_id : $actor_id->getId();
|
||||
$actor_id = \is_int($actor_id) ? $actor_id : $actor_id->getId();
|
||||
$attributed_protocol = self::getByPK(['actor_id' => $actor_id]);
|
||||
if (is_null($attributed_protocol)) {
|
||||
if (\is_null($attributed_protocol)) {
|
||||
$attributed_protocol = self::create([
|
||||
'actor_id' => $actor_id,
|
||||
'protocol' => $protocol,
|
||||
@ -130,14 +124,14 @@ class FreeNetworkActorProtocol extends Entity
|
||||
} else {
|
||||
$attributed_protocol->setProtocol($protocol);
|
||||
}
|
||||
DB::wrapInTransaction(fn() => DB::persist($attributed_protocol));
|
||||
DB::wrapInTransaction(fn () => DB::persist($attributed_protocol));
|
||||
}
|
||||
|
||||
public static function canIActor(string $protocol, int|Actor $actor_id): bool
|
||||
{
|
||||
$actor_id = is_int($actor_id) ? $actor_id : $actor_id->getId();
|
||||
$actor_id = \is_int($actor_id) ? $actor_id : $actor_id->getId();
|
||||
$attributed_protocol = self::getByPK(['actor_id' => $actor_id])?->getProtocol();
|
||||
if (is_null($attributed_protocol)) {
|
||||
if (\is_null($attributed_protocol)) {
|
||||
// If it is not attributed, you can go ahead.
|
||||
return true;
|
||||
} else {
|
||||
@ -149,9 +143,9 @@ class FreeNetworkActorProtocol extends Entity
|
||||
public static function canIAddr(string $protocol, string $target): bool
|
||||
{
|
||||
// Normalize $addr, i.e. add 'acct:' if missing
|
||||
$addr = Discovery::normalize($target);
|
||||
$addr = Discovery::normalize($target);
|
||||
$attributed_protocol = self::getByPK(['addr' => $addr])?->getProtocol();
|
||||
if (is_null($attributed_protocol)) {
|
||||
if (\is_null($attributed_protocol)) {
|
||||
// If it is not attributed, you can go ahead.
|
||||
return true;
|
||||
} else {
|
||||
@ -167,7 +161,7 @@ class FreeNetworkActorProtocol extends Entity
|
||||
'fields' => [
|
||||
'actor_id' => ['type' => 'int', 'not null' => true],
|
||||
'protocol' => ['type' => 'varchar', 'length' => 32, 'description' => 'the protocol plugin that should handle federation of this actor'],
|
||||
'addr' => ['type' => 'text', 'not null' => true, 'description' => 'webfinger acct'],
|
||||
'addr' => ['type' => 'text', 'not null' => true, 'description' => 'webfinger acct'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
],
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
/**
|
||||
* StatusNet, the distributed open-source microblogging tool
|
||||
*
|
||||
@ -50,7 +52,7 @@ use Throwable;
|
||||
*/
|
||||
class WebfingerReconstructionException extends ServerException
|
||||
{
|
||||
public function __construct(string $message = '', int $code = 500, Throwable $previous = null)
|
||||
public function __construct(string $message = '', int $code = 500, ?Throwable $previous = null)
|
||||
{
|
||||
// We could log an entry here with the search parameters
|
||||
parent::__construct(_m('WebFinger URI generation failed.'));
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
/**
|
||||
* StatusNet - the distributed open-source microblogging tool
|
||||
* Copyright (C) 2010, StatusNet, Inc.
|
||||
@ -69,7 +71,7 @@ class LinkHeader
|
||||
$this->type = null;
|
||||
|
||||
// remove uri-reference from header
|
||||
$str = substr($str, strlen($uri_reference[0]));
|
||||
$str = mb_substr($str, \mb_strlen($uri_reference[0]));
|
||||
|
||||
// parse link-params
|
||||
$params = explode(';', $str);
|
||||
@ -78,7 +80,7 @@ class LinkHeader
|
||||
if (empty($param)) {
|
||||
continue;
|
||||
}
|
||||
list($param_name, $param_value) = explode('=', $param, 2);
|
||||
[$param_name, $param_value] = explode('=', $param, 2);
|
||||
|
||||
$param_name = trim($param_name);
|
||||
$param_value = preg_replace('(^"|"$)', '', trim($param_value));
|
||||
@ -110,18 +112,18 @@ class LinkHeader
|
||||
$headers = $response->getHeader('Link');
|
||||
if ($headers) {
|
||||
// Can get an array or string, so try to simplify the path
|
||||
if (!is_array($headers)) {
|
||||
if (!\is_array($headers)) {
|
||||
$headers = [$headers];
|
||||
}
|
||||
|
||||
foreach ($headers as $header) {
|
||||
$lh = new self($header);
|
||||
|
||||
if ((is_null($rel) || $lh->rel == $rel) && (is_null($type) || $lh->type == $type)) {
|
||||
if ((\is_null($rel) || $lh->rel == $rel) && (\is_null($type) || $lh->type == $type)) {
|
||||
return $lh->href;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Component\FreeNetwork\Util\LrddMethod;
|
||||
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -39,29 +41,27 @@ class LrddMethodHostMeta extends LRDDMethod
|
||||
/**
|
||||
* For RFC6415 and HTTP URIs, fetch the host-meta file
|
||||
* and look for LRDD templates
|
||||
*
|
||||
* @param mixed $uri
|
||||
*/
|
||||
public function discover($uri)
|
||||
{
|
||||
// This is allowed for RFC6415 but not the 'WebFinger' RFC7033.
|
||||
$try_schemes = ['https', 'http'];
|
||||
|
||||
$scheme = mb_strtolower(parse_url($uri, PHP_URL_SCHEME));
|
||||
$scheme = mb_strtolower(parse_url($uri, \PHP_URL_SCHEME));
|
||||
switch ($scheme) {
|
||||
case 'acct':
|
||||
// We can't use parse_url data for this, since the 'host'
|
||||
// entry is only set if the scheme has '://' after it.
|
||||
$parts = explode('@', parse_url($uri, PHP_URL_PATH), 2);
|
||||
$parts = explode('@', parse_url($uri, \PHP_URL_PATH), 2);
|
||||
|
||||
if (!Discovery::isAcct($uri) || count($parts) != 2) {
|
||||
if (!Discovery::isAcct($uri) || \count($parts) != 2) {
|
||||
throw new Exception('Bad resource URI: ' . $uri);
|
||||
}
|
||||
[, $domain] = $parts;
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
$domain = mb_strtolower(parse_url($uri, PHP_URL_HOST));
|
||||
$domain = mb_strtolower(parse_url($uri, \PHP_URL_HOST));
|
||||
$try_schemes = [$scheme];
|
||||
break;
|
||||
default:
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Component\FreeNetwork\Util\LrddMethod;
|
||||
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -38,10 +40,7 @@ class LrddMethodLinkHtml extends LRDDMethod
|
||||
* For HTTP IDs, fetch the URL and look for <link> elements
|
||||
* in the HTML response.
|
||||
*
|
||||
* @param mixed $uri
|
||||
*
|
||||
* @todo fail out of WebFinger URIs faster
|
||||
*
|
||||
*/
|
||||
public function discover($uri)
|
||||
{
|
||||
@ -65,7 +64,7 @@ class LrddMethodLinkHtml extends LRDDMethod
|
||||
|
||||
preg_match('/<head(\s[^>]*)?>(.*?)<\/head>/is', $html, $head_matches);
|
||||
|
||||
if (count($head_matches) != 3) {
|
||||
if (\count($head_matches) != 3) {
|
||||
return [];
|
||||
}
|
||||
[, , $head_html] = $head_matches;
|
||||
@ -78,23 +77,23 @@ class LrddMethodLinkHtml extends LRDDMethod
|
||||
$link_type = null;
|
||||
|
||||
preg_match('/\srel=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $rel_matches);
|
||||
if (count($rel_matches) > 3) {
|
||||
if (\count($rel_matches) > 3) {
|
||||
$link_rel = $rel_matches[3];
|
||||
} elseif (count($rel_matches) > 1) {
|
||||
} elseif (\count($rel_matches) > 1) {
|
||||
$link_rel = $rel_matches[1];
|
||||
}
|
||||
|
||||
preg_match('/\shref=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $href_matches);
|
||||
if (count($href_matches) > 3) {
|
||||
if (\count($href_matches) > 3) {
|
||||
$link_uri = $href_matches[3];
|
||||
} elseif (count($href_matches) > 1) {
|
||||
} elseif (\count($href_matches) > 1) {
|
||||
$link_uri = $href_matches[1];
|
||||
}
|
||||
|
||||
preg_match('/\stype=(("|\')([^\\2]*?)\\2|[^"\'\s]+)/i', $link_html, $type_matches);
|
||||
if (count($type_matches) > 3) {
|
||||
if (\count($type_matches) > 3) {
|
||||
$link_type = $type_matches[3];
|
||||
} elseif (count($type_matches) > 1) {
|
||||
} elseif (\count($type_matches) > 1) {
|
||||
$link_type = $type_matches[1];
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
// GNU social is free software: you can redistribute it and/or modify
|
||||
@ -36,19 +38,17 @@ class LrddMethodWebfinger extends LRDDMethod
|
||||
/**
|
||||
* Simply returns the WebFinger URL over HTTPS at the uri's domain:
|
||||
* https://{domain}/.well-known/webfinger?resource={uri}
|
||||
*
|
||||
* @param mixed $uri
|
||||
*/
|
||||
public function discover($uri)
|
||||
{
|
||||
$parts = explode('@', parse_url($uri, PHP_URL_PATH), 2);
|
||||
$parts = explode('@', parse_url($uri, \PHP_URL_PATH), 2);
|
||||
|
||||
if (!Discovery::isAcct($uri) || count($parts) != 2) {
|
||||
if (!Discovery::isAcct($uri) || \count($parts) != 2) {
|
||||
throw new Exception('Bad resource URI: ' . $uri);
|
||||
}
|
||||
[, $domain] = $parts;
|
||||
if (!filter_var($domain, FILTER_VALIDATE_IP)
|
||||
&& !filter_var(gethostbyname($domain), FILTER_VALIDATE_IP)) {
|
||||
if (!filter_var($domain, \FILTER_VALIDATE_IP)
|
||||
&& !filter_var(gethostbyname($domain), \FILTER_VALIDATE_IP)) {
|
||||
throw new Exception('Bad resource host.');
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ class LrddMethodWebfinger extends LRDDMethod
|
||||
Discovery::LRDD_REL,
|
||||
'https://' . $domain . '/.well-known/webfinger?resource={uri}',
|
||||
Discovery::JRD_MIMETYPE,
|
||||
true // isTemplate
|
||||
true, // isTemplate
|
||||
);
|
||||
|
||||
return [$link];
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Component\FreeNetwork\Util;
|
||||
|
||||
use App\Core\Entity;
|
||||
@ -40,8 +42,6 @@ abstract class WebfingerResource
|
||||
|
||||
/**
|
||||
* List of alternative IDs of a certain Actor
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAliases(): array
|
||||
{
|
||||
@ -53,7 +53,7 @@ abstract class WebfingerResource
|
||||
// you've run HTTPS all the time!
|
||||
if (Common::config('fix', 'legacy_http')) {
|
||||
foreach ($aliases as $alias => $id) {
|
||||
if (!strtolower(parse_url($alias, PHP_URL_SCHEME)) === 'https') {
|
||||
if (!mb_strtolower(parse_url($alias, \PHP_URL_SCHEME)) === 'https') {
|
||||
continue;
|
||||
}
|
||||
$aliases[preg_replace('/^https:/i', 'http:', $alias, 1)] = $id;
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Component\FreeNetwork\Util\WebfingerResource;
|
||||
|
||||
use App\Core\Event;
|
||||
@ -24,9 +26,9 @@ use XML_XRD_Element_Link;
|
||||
*/
|
||||
class WebfingerResourceActor extends WebFingerResource
|
||||
{
|
||||
const PROFILEPAGE = 'http://webfinger.net/rel/profile-page';
|
||||
public const PROFILEPAGE = 'http://webfinger.net/rel/profile-page';
|
||||
|
||||
public function __construct(Actor $object = null)
|
||||
public function __construct(?Actor $object = null)
|
||||
{
|
||||
// The type argument above verifies that it's our class
|
||||
parent::__construct($object);
|
||||
@ -49,8 +51,9 @@ class WebfingerResourceActor extends WebFingerResource
|
||||
/**
|
||||
* Reconstruct WebFinger acct: from object
|
||||
*
|
||||
* @return array|false|mixed|string|string[]|null
|
||||
* @throws WebfingerReconstructionException
|
||||
*
|
||||
* @return null|array|false|mixed|string|string[]
|
||||
*/
|
||||
public function reconstructAcct()
|
||||
{
|
||||
@ -58,7 +61,7 @@ class WebfingerResourceActor extends WebFingerResource
|
||||
|
||||
if (Event::handle('StartWebFingerReconstruction', [$this->object, &$acct])) {
|
||||
// TODO: getUri may not always give us the correct host on remote users?
|
||||
$host = parse_url($this->object->getUri(Router::ABSOLUTE_URL), PHP_URL_HOST);
|
||||
$host = parse_url($this->object->getUri(Router::ABSOLUTE_URL), \PHP_URL_HOST);
|
||||
if (empty($this->object->getNickname()) || empty($host)) {
|
||||
throw new WebFingerReconstructionException(print_r($this->object, true));
|
||||
}
|
||||
@ -75,8 +78,11 @@ class WebfingerResourceActor extends WebFingerResource
|
||||
if (Event::handle('StartWebFingerProfileLinks', [$xrd, $this->object])) {
|
||||
|
||||
// Profile page, can give more metadata from Link header or HTML parsing
|
||||
$xrd->links[] = new XML_XRD_Element_Link(self::PROFILEPAGE,
|
||||
$this->object->getUrl(Router::ABSOLUTE_URL), 'text/html');
|
||||
$xrd->links[] = new XML_XRD_Element_Link(
|
||||
self::PROFILEPAGE,
|
||||
$this->object->getUrl(Router::ABSOLUTE_URL),
|
||||
'text/html',
|
||||
);
|
||||
|
||||
// // XFN
|
||||
// $xrd->links[] = new XML_XRD_Element_Link('http://gmpg.org/xfn/11',
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Component\FreeNetwork\Util\WebfingerResource;
|
||||
|
||||
use App\Core\Event;
|
||||
@ -22,7 +24,7 @@ use XML_XRD_Element_Link;
|
||||
*/
|
||||
class WebfingerResourceNote extends WebfingerResource
|
||||
{
|
||||
public function __construct(Note $object = null)
|
||||
public function __construct(?Note $object = null)
|
||||
{
|
||||
// The type argument above verifies that it's our class
|
||||
parent::__construct($object);
|
||||
@ -30,29 +32,37 @@ class WebfingerResourceNote extends WebfingerResource
|
||||
|
||||
/**
|
||||
* Update given XRD with self's data
|
||||
*
|
||||
* @param XML_XRD $xrd
|
||||
*/
|
||||
public function updateXRD(XML_XRD $xrd)
|
||||
{
|
||||
if (Event::handle('StartWebFingerNoticeLinks', [$xrd, $this->object])) {
|
||||
if ($this->object->isLocal()) {
|
||||
$xrd->links[] = new XML_XRD_Element_Link('alternate',
|
||||
common_local_url('ApiStatusesShow',
|
||||
$xrd->links[] = new XML_XRD_Element_Link(
|
||||
'alternate',
|
||||
common_local_url(
|
||||
'ApiStatusesShow',
|
||||
['id' => $this->object->id,
|
||||
'format' => 'atom', ]),
|
||||
'application/atom+xml');
|
||||
'format' => 'atom', ],
|
||||
),
|
||||
'application/atom+xml',
|
||||
);
|
||||
|
||||
$xrd->links[] = new XML_XRD_Element_Link('alternate',
|
||||
common_local_url('ApiStatusesShow',
|
||||
$xrd->links[] = new XML_XRD_Element_Link(
|
||||
'alternate',
|
||||
common_local_url(
|
||||
'ApiStatusesShow',
|
||||
['id' => $this->object->id,
|
||||
'format' => 'json', ]),
|
||||
'application/json');
|
||||
'format' => 'json', ],
|
||||
),
|
||||
'application/json',
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
$xrd->links[] = new XML_XRD_Element_Link('alternate',
|
||||
$xrd->links[] = new XML_XRD_Element_Link(
|
||||
'alternate',
|
||||
$this->object->getUrl(),
|
||||
'text/html');
|
||||
'text/html',
|
||||
);
|
||||
} catch (InvalidUrlException $e) {
|
||||
// don't do a fallback in webfinger
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Component\FreeNetwork\Util;
|
||||
|
||||
use App\Core\Controller;
|
||||
@ -35,4 +37,4 @@ abstract class XrdController extends Controller
|
||||
$this->setXRD();
|
||||
return ['xrd' => $this->xrd, 'default_mimetype' => $this->default_mimetype];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,13 +28,13 @@ use App\Core\Controller;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Form;
|
||||
use function App\Core\I18n\_m;
|
||||
use Component\Language\Entity\ActorLanguage;
|
||||
use Component\Language\Entity\Language as LangEntity;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\NoLoggedInUser;
|
||||
use App\Util\Exception\RedirectException;
|
||||
use App\Util\Exception\ServerException;
|
||||
use App\Util\Form\FormFields;
|
||||
use Component\Language\Entity\ActorLanguage;
|
||||
use Component\Language\Entity\Language as LangEntity;
|
||||
use Functional as F;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
|
||||
@ -26,13 +26,12 @@ namespace Component\Language\Entity;
|
||||
use App\Core\Cache;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Entity\Actor;
|
||||
use App\Entity\Note;
|
||||
use App\Util\Common;
|
||||
use DateTimeInterface;
|
||||
use Functional as F;
|
||||
use function App\Core\I18n\_m;
|
||||
use function is_null;
|
||||
|
||||
/**
|
||||
* Entity for languages
|
||||
@ -115,8 +114,8 @@ class Language extends Entity
|
||||
{
|
||||
return Cache::getHashMapKey(
|
||||
map_key: 'languages-id',
|
||||
key: (string)$id,
|
||||
calculate_map: fn() => F\reindex(DB::dql('select l from language l'), fn(self $l) => (string)$l->getId()),
|
||||
key: (string) $id,
|
||||
calculate_map: fn () => F\reindex(DB::dql('select l from language l'), fn (self $l) => (string) $l->getId()),
|
||||
);
|
||||
}
|
||||
|
||||
@ -125,7 +124,7 @@ class Language extends Entity
|
||||
return Cache::getHashMapKey(
|
||||
'languages',
|
||||
$locale,
|
||||
calculate_map: fn() => F\reindex(DB::dql('select l from language l'), fn(self $l) => $l->getLocale()),
|
||||
calculate_map: fn () => F\reindex(DB::dql('select l from language l'), fn (self $l) => $l->getLocale()),
|
||||
);
|
||||
}
|
||||
|
||||
@ -138,10 +137,10 @@ class Language extends Entity
|
||||
{
|
||||
$langs = Cache::getHashMap(
|
||||
'languages',
|
||||
fn() => F\reindex(DB::dql('select l from language l'), fn(self $l) => $l->getLocale()),
|
||||
fn () => F\reindex(DB::dql('select l from language l'), fn (self $l) => $l->getLocale()),
|
||||
);
|
||||
|
||||
return array_merge(...F\map(array_values($langs), fn($l) => $l->toChoiceFormat()));
|
||||
return array_merge(...F\map(array_values($langs), fn ($l) => $l->toChoiceFormat()));
|
||||
}
|
||||
|
||||
public function toChoiceFormat(): array
|
||||
@ -156,18 +155,18 @@ class Language extends Entity
|
||||
public static function getSortedLanguageChoices(?Actor $actor, ?Actor $context_actor, ?bool $use_short_display): array
|
||||
{
|
||||
$language_choices = self::getLanguageChoices();
|
||||
if (is_null($actor)) {
|
||||
if (\is_null($actor)) {
|
||||
return [$language_choices, []];
|
||||
}
|
||||
$preferred_language_choices = $actor->getPreferredLanguageChoices($context_actor);
|
||||
ksort($language_choices);
|
||||
if ($use_short_display ?? Common::config('posting', 'use_short_language_display')) {
|
||||
$key = array_key_first($preferred_language_choices);
|
||||
$key = array_key_first($preferred_language_choices);
|
||||
$language = $preferred_language_choices[$key];
|
||||
unset($preferred_language_choices[$key], $language_choices[$key]);
|
||||
$short_display = $language->getShortDisplay();
|
||||
$short_display = $language->getShortDisplay();
|
||||
$preferred_language_choices[$short_display] = ($locale = $language->getLocale());
|
||||
$language_choices[$short_display] = $locale;
|
||||
$language_choices[$short_display] = $locale;
|
||||
}
|
||||
return [$language_choices, $preferred_language_choices];
|
||||
}
|
||||
@ -175,14 +174,14 @@ class Language extends Entity
|
||||
public static function schemaDef(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'language',
|
||||
'name' => 'language',
|
||||
'description' => 'all known languages',
|
||||
'fields' => [
|
||||
'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
|
||||
'locale' => ['type' => 'varchar', 'length' => 64, 'description' => 'The locale identifier for the language of a note. 2-leter-iso-language-code_4-leter-script-code_2-leter-iso-country-code, but kept longer in case we get a different format'],
|
||||
'long_display' => ['type' => 'varchar', 'length' => 64, 'description' => 'The long display string for the language, in english (translated later)'],
|
||||
'fields' => [
|
||||
'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
|
||||
'locale' => ['type' => 'varchar', 'length' => 64, 'description' => 'The locale identifier for the language of a note. 2-leter-iso-language-code_4-leter-script-code_2-leter-iso-country-code, but kept longer in case we get a different format'],
|
||||
'long_display' => ['type' => 'varchar', 'length' => 64, 'description' => 'The long display string for the language, in english (translated later)'],
|
||||
'short_display' => ['type' => 'varchar', 'length' => 12, 'description' => 'The short display string for the language (used for the first option)'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
],
|
||||
'primary key' => ['id'],
|
||||
'unique keys' => [
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
@ -40,7 +42,7 @@ class NoteToLink extends Entity
|
||||
// @codeCoverageIgnoreStart
|
||||
private int $link_id;
|
||||
private int $note_id;
|
||||
private \DateTimeInterface $modified;
|
||||
private DateTimeInterface $modified;
|
||||
|
||||
public function setLinkId(int $link_id): self
|
||||
{
|
||||
@ -93,10 +95,6 @@ class NoteToLink extends Entity
|
||||
return parent::create($args, $obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $note_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhereNoteId(int $note_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
@ -108,11 +106,6 @@ class NoteToLink extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $link_id
|
||||
* @param int $note_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhere(int $link_id, int $note_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
@ -125,10 +118,6 @@ class NoteToLink extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $link_id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function removeWhereLinkId(int $link_id): mixed
|
||||
{
|
||||
return DB::dql(
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
@ -46,8 +48,8 @@ class Notification extends Entity
|
||||
private int $activity_id;
|
||||
private int $target_id;
|
||||
private ?string $reason;
|
||||
private \DateTimeInterface $created;
|
||||
private \DateTimeInterface $modified;
|
||||
private DateTimeInterface $created;
|
||||
private DateTimeInterface $modified;
|
||||
|
||||
public function setActivityId(int $activity_id): self
|
||||
{
|
||||
@ -107,9 +109,6 @@ class Notification extends Entity
|
||||
// @codeCoverageIgnoreEnd
|
||||
// }}} Autocode
|
||||
|
||||
/**
|
||||
* @return Actor
|
||||
*/
|
||||
public function getTarget(): Actor
|
||||
{
|
||||
return Actor::getById($this->getTargetId());
|
||||
@ -122,18 +121,14 @@ class Notification extends Entity
|
||||
*/
|
||||
public static function getNotificationTargetIdsByActivity(int|Activity $activity_id): array
|
||||
{
|
||||
$notifications = DB::findBy('notification', ['activity_id' => is_int($activity_id) ? $activity_id : $activity_id->getId()]);
|
||||
$targets = [];
|
||||
$notifications = DB::findBy('notification', ['activity_id' => \is_int($activity_id) ? $activity_id : $activity_id->getId()]);
|
||||
$targets = [];
|
||||
foreach ($notifications as $notification) {
|
||||
$targets[] = $notification->getTargetId();
|
||||
}
|
||||
return $targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|Activity $activity_id
|
||||
* @return array
|
||||
*/
|
||||
public function getNotificationTargetsByActivity(int|Activity $activity_id): array
|
||||
{
|
||||
return DB::findBy('actor', ['id' => $this->getNotificationTargetIdsByActivity($activity_id)]);
|
||||
@ -154,7 +149,7 @@ class Notification extends Entity
|
||||
'primary key' => ['activity_id', 'target_id'],
|
||||
'indexes' => [
|
||||
'attention_activity_id_idx' => ['activity_id'],
|
||||
'attention_target_id_idx' => ['target_id'],
|
||||
'attention_target_id_idx' => ['target_id'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
@ -48,8 +50,8 @@ class UserNotificationPrefs extends Entity
|
||||
private bool $dm = true;
|
||||
private bool $post_on_status_change = false;
|
||||
private ?bool $enable_posting;
|
||||
private \DateTimeInterface $created;
|
||||
private \DateTimeInterface $modified;
|
||||
private DateTimeInterface $created;
|
||||
private DateTimeInterface $modified;
|
||||
|
||||
public function setUserId(int $user_id): self
|
||||
{
|
||||
|
@ -37,7 +37,6 @@ use App\Core\Security;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Actor;
|
||||
use App\Entity\GroupInbox;
|
||||
use Component\Language\Entity\Language;
|
||||
use App\Entity\Note;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\ClientException;
|
||||
@ -49,6 +48,7 @@ use App\Util\Formatting;
|
||||
use Component\Attachment\Entity\ActorToAttachment;
|
||||
use Component\Attachment\Entity\AttachmentToNote;
|
||||
use Component\Conversation\Conversation;
|
||||
use Component\Language\Entity\Language;
|
||||
use Functional as F;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||
|
@ -26,7 +26,6 @@ namespace Component\Tag;
|
||||
use App\Core\Cache;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Event;
|
||||
use Component\Language\Entity\Language;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Modules\Component;
|
||||
use App\Core\Router\Router;
|
||||
@ -38,6 +37,7 @@ use App\Util\Common;
|
||||
use App\Util\Exception\ClientException;
|
||||
use App\Util\Formatting;
|
||||
use App\Util\HTML;
|
||||
use Component\Language\Entity\Language;
|
||||
use Component\Tag\Controller as C;
|
||||
use Doctrine\Common\Collections\ExpressionBuilder;
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -106,15 +107,15 @@ class ActivitypubActivity extends Entity
|
||||
public static function schemaDef(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'activitypub_activity',
|
||||
'name' => 'activitypub_activity',
|
||||
'fields' => [
|
||||
'activity_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Activity.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'activity_id to give attention'],
|
||||
'activity_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Activity.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'activity_id to give attention'],
|
||||
'activity_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Activity\'s URI'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
],
|
||||
'primary key' => ['activity_uri'],
|
||||
'indexes' => [
|
||||
'indexes' => [
|
||||
'activity_activity_uri_idx' => ['activity_uri'],
|
||||
],
|
||||
];
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -33,6 +34,7 @@ namespace Plugin\ActivityPub\Entity;
|
||||
|
||||
use App\Core\Cache;
|
||||
use App\Core\Entity;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Log;
|
||||
use App\Entity\Actor;
|
||||
use Component\FreeNetwork\Util\Discovery;
|
||||
@ -41,9 +43,6 @@ use Exception;
|
||||
use Plugin\ActivityPub\Util\DiscoveryHints;
|
||||
use Plugin\ActivityPub\Util\Explorer;
|
||||
use XML_XRD;
|
||||
use function App\Core\I18n\_m;
|
||||
use function array_key_exists;
|
||||
use function is_null;
|
||||
|
||||
/**
|
||||
* Table Definition for activitypub_actor
|
||||
@ -158,10 +157,10 @@ class ActivitypubActor extends Entity
|
||||
$addr = Discovery::normalize($addr);
|
||||
|
||||
// Try the cache
|
||||
$uri = Cache::get(sprintf('ActivitypubActor-webfinger-%s', urlencode($addr)), fn() => false);
|
||||
$uri = Cache::get(sprintf('ActivitypubActor-webfinger-%s', urlencode($addr)), fn () => false);
|
||||
|
||||
if ($uri !== false) {
|
||||
if (is_null($uri)) {
|
||||
if (\is_null($uri)) {
|
||||
// TRANS: Exception.
|
||||
throw new Exception(_m('Not a valid WebFinger address (via cache).'));
|
||||
}
|
||||
@ -197,7 +196,7 @@ class ActivitypubActor extends Entity
|
||||
DiscoveryHints::fromXRD($xrd),
|
||||
);
|
||||
|
||||
if (array_key_exists('activitypub', $hints)) {
|
||||
if (\array_key_exists('activitypub', $hints)) {
|
||||
$uri = $hints['activitypub'];
|
||||
try {
|
||||
LOG::info("Discovery on acct:{$addr} with URI:{$uri}");
|
||||
@ -240,9 +239,7 @@ class ActivitypubActor extends Entity
|
||||
|
||||
/**
|
||||
* @param ActivitypubActor $ap_actor
|
||||
* @param Actor $actor
|
||||
* @param ActivitypubRsa $activitypub_rsa
|
||||
* @param string $res
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function update_profile(self &$ap_actor, Actor &$actor, ActivitypubRsa &$activitypub_rsa, string $res): void
|
||||
@ -253,17 +250,17 @@ class ActivitypubActor extends Entity
|
||||
public static function schemaDef(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'activitypub_actor',
|
||||
'name' => 'activitypub_actor',
|
||||
'fields' => [
|
||||
'uri' => ['type' => 'text', 'not null' => true],
|
||||
'actor_id' => ['type' => 'int', 'not null' => true],
|
||||
'inbox_uri' => ['type' => 'text', 'not null' => true],
|
||||
'uri' => ['type' => 'text', 'not null' => true],
|
||||
'actor_id' => ['type' => 'int', 'not null' => true],
|
||||
'inbox_uri' => ['type' => 'text', 'not null' => true],
|
||||
'inbox_shared_uri' => ['type' => 'text'],
|
||||
'url' => ['type' => 'text'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
'url' => ['type' => 'text'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
],
|
||||
'primary key' => ['actor_id'],
|
||||
'primary key' => ['actor_id'],
|
||||
'foreign keys' => [
|
||||
'activitypub_actor_actor_id_fkey' => ['actor', ['actor_id' => 'id']],
|
||||
],
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -117,16 +118,16 @@ class ActivitypubObject extends Entity
|
||||
public static function schemaDef(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'activitypub_object',
|
||||
'name' => 'activitypub_object',
|
||||
'fields' => [
|
||||
'object_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Object\'s URI'],
|
||||
'object_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Object\'s URI'],
|
||||
'object_type' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'the name of the table this object refers to'],
|
||||
'object_id' => ['type' => 'int', 'not null' => true, 'description' => 'id in the referenced table'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
'object_id' => ['type' => 'int', 'not null' => true, 'description' => 'id in the referenced table'],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
],
|
||||
'primary key' => ['object_uri'],
|
||||
'indexes' => [
|
||||
'indexes' => [
|
||||
'activity_object_uri_idx' => ['object_uri'],
|
||||
],
|
||||
];
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -119,15 +120,15 @@ class ActivitypubRsa extends Entity
|
||||
public static function schemaDef(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'activitypub_rsa',
|
||||
'name' => 'activitypub_rsa',
|
||||
'fields' => [
|
||||
'actor_id' => ['type' => 'int', 'not null' => true],
|
||||
'actor_id' => ['type' => 'int', 'not null' => true],
|
||||
'private_key' => ['type' => 'text'],
|
||||
'public_key' => ['type' => 'text', 'not null' => true],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
'public_key' => ['type' => 'text', 'not null' => true],
|
||||
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
|
||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||
],
|
||||
'primary key' => ['actor_id'],
|
||||
'primary key' => ['actor_id'],
|
||||
'foreign keys' => [
|
||||
'activitypub_rsa_actor_id_fkey' => ['actor', ['actor_id' => 'id']],
|
||||
],
|
||||
@ -137,27 +138,28 @@ class ActivitypubRsa extends Entity
|
||||
/**
|
||||
* Guarantees RSA keys for a given actor.
|
||||
*
|
||||
* @param Actor $gsactor
|
||||
* @param bool $fetch =true Should attempt to fetch keys from a remote profile?
|
||||
* @return ActivitypubRsa The keys (private key is null for remote actors)
|
||||
*
|
||||
* @throws ServerException It should never occur, but if so, we break everything!
|
||||
*
|
||||
* @return ActivitypubRsa The keys (private key is null for remote actors)
|
||||
*/
|
||||
public static function getByActor(Actor $gsactor, bool $fetch = true): self
|
||||
{
|
||||
$apRSA = self::getByPK(['actor_id' => ($actor_id = $gsactor->getId())]);
|
||||
if (is_null($apRSA)) {
|
||||
if (\is_null($apRSA)) {
|
||||
// Nonexistent key pair for this profile
|
||||
if ($gsactor->getIsLocal()) {
|
||||
self::generateKeys($private_key, $public_key);
|
||||
$apRSA = self::create([
|
||||
'actor_id' => $actor_id,
|
||||
'actor_id' => $actor_id,
|
||||
'private_key' => $private_key,
|
||||
'public_key' => $public_key,
|
||||
'public_key' => $public_key,
|
||||
]);
|
||||
DB::wrapInTransaction(fn() => DB::persist($apRSA));
|
||||
DB::wrapInTransaction(fn () => DB::persist($apRSA));
|
||||
} else {
|
||||
// ASSERT: This should never happen, but try to recover!
|
||||
Log::error("Activitypub_rsa: An impossible thing has happened... Please let the devs know.");
|
||||
Log::error('Activitypub_rsa: An impossible thing has happened... Please let the devs know.');
|
||||
if ($fetch) {
|
||||
//$res = Activitypub_explorer::get_remote_user_activity($profile->getUri());
|
||||
//Activitypub_rsa::update_public_key($profile, $res['publicKey']['publicKeyPem']);
|
||||
@ -173,16 +175,17 @@ class ActivitypubRsa extends Entity
|
||||
/**
|
||||
* Generates a pair of RSA keys.
|
||||
*
|
||||
* @param string|null $private_key out
|
||||
* @param string|null $public_key out
|
||||
* @param null|string $private_key out
|
||||
* @param null|string $public_key out
|
||||
*
|
||||
* @author PHP Manual Contributed Notes <dirt@awoms.com>
|
||||
*/
|
||||
private static function generateKeys(?string &$private_key, ?string &$public_key): void
|
||||
{
|
||||
$config = [
|
||||
'digest_alg' => 'sha512',
|
||||
'digest_alg' => 'sha512',
|
||||
'private_key_bits' => 2048,
|
||||
'private_key_type' => OPENSSL_KEYTYPE_RSA,
|
||||
'private_key_type' => \OPENSSL_KEYTYPE_RSA,
|
||||
];
|
||||
|
||||
// Create the private and public key
|
||||
@ -192,8 +195,8 @@ class ActivitypubRsa extends Entity
|
||||
openssl_pkey_export($res, $private_key);
|
||||
|
||||
// Extract the public key from $res to $pubKey
|
||||
$pubKey = openssl_pkey_get_details($res);
|
||||
$public_key = $pubKey["key"];
|
||||
$pubKey = openssl_pkey_get_details($res);
|
||||
$public_key = $pubKey['key'];
|
||||
unset($pubKey);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -51,7 +52,6 @@ use XML_XRD;
|
||||
/**
|
||||
* DiscoveryHints implementation for GNU social
|
||||
*
|
||||
*
|
||||
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
@ -59,9 +59,6 @@ class DiscoveryHints
|
||||
{
|
||||
/**
|
||||
* Search the WebFinger XRD after an ActivityPub URI
|
||||
*
|
||||
* @param XML_XRD $xrd
|
||||
* @return array
|
||||
*/
|
||||
public static function fromXRD(XML_XRD $xrd): array
|
||||
{
|
||||
@ -79,4 +76,4 @@ class DiscoveryHints
|
||||
|
||||
return $hints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -35,15 +36,13 @@ use App\Core\HTTPClient;
|
||||
use App\Core\Log;
|
||||
use App\Util\Exception\NoSuchActorException;
|
||||
use Exception;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
use Plugin\ActivityPub\ActivityPub;
|
||||
use Plugin\ActivityPub\Entity\ActivitypubActor;
|
||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use function in_array;
|
||||
use function is_null;
|
||||
use const JSON_UNESCAPED_SLASHES;
|
||||
|
||||
/**
|
||||
* ActivityPub's own Explorer
|
||||
@ -60,10 +59,8 @@ class Explorer
|
||||
/**
|
||||
* Shortcut function to get a single profile from its URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
*
|
||||
* @return ActivitypubActor
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws NoSuchActorException
|
||||
* @throws RedirectionExceptionInterface
|
||||
@ -86,8 +83,8 @@ class Explorer
|
||||
* This function cleans the $this->discovered_actor_profiles array
|
||||
* so that there is no erroneous data
|
||||
*
|
||||
* @param string $url User's url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
* @param string $url User's url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
*
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws NoSuchActorException
|
||||
@ -99,7 +96,7 @@ class Explorer
|
||||
*/
|
||||
public function lookup(string $url, bool $grab_online = true)
|
||||
{
|
||||
if (in_array($url, ActivityPub::PUBLIC_TO)) {
|
||||
if (\in_array($url, ActivityPub::PUBLIC_TO)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -114,8 +111,8 @@ class Explorer
|
||||
* This is a recursive function that will accumulate the results on
|
||||
* $discovered_actor_profiles array
|
||||
*
|
||||
* @param string $url User's url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
* @param string $url User's url
|
||||
* @param bool $grab_online whether to try online grabbing, defaults to true
|
||||
*
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws NoSuchActorException
|
||||
@ -187,13 +184,13 @@ class Explorer
|
||||
{
|
||||
Log::debug('ActivityPub Explorer: Trying to grab a remote actor for ' . $url);
|
||||
$response = HTTPClient::get($url, ['headers' => ACTIVITYPUB::HTTP_CLIENT_HEADERS]);
|
||||
$res = json_decode($response->getContent(), true);
|
||||
$res = json_decode($response->getContent(), true);
|
||||
if ($response->getStatusCode() == 410) { // If it was deleted
|
||||
return true; // Nothing to add.
|
||||
} elseif (!HTTPClient::statusCodeIsOkay($response)) { // If it is unavailable
|
||||
return false; // Try to add at another time.
|
||||
}
|
||||
if (is_null($res)) {
|
||||
if (\is_null($res)) {
|
||||
Log::debug('ActivityPub Explorer: Invalid response returned from given Actor URL: ' . $res);
|
||||
return true; // Nothing to add.
|
||||
}
|
||||
@ -210,7 +207,7 @@ class Explorer
|
||||
Log::debug(
|
||||
'ActivityPub Explorer: Invalid potential remote actor while grabbing remotely: ' . $url
|
||||
. '. He returned the following: ' . json_encode($res, JSON_UNESCAPED_SLASHES)
|
||||
. ' and the following exception: ' . $e->getMessage()
|
||||
. ' and the following exception: ' . $e->getMessage(),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@ -229,14 +226,12 @@ class Explorer
|
||||
public static function get_aprofile_by_url(string $v): ActivitypubActor|bool
|
||||
{
|
||||
$aprofile = ActivitypubActor::getByPK(['uri' => $v]);
|
||||
return is_null($aprofile) ? false : ActivitypubActor::getByPK(['uri' => $v]);
|
||||
return \is_null($aprofile) ? false : ActivitypubActor::getByPK(['uri' => $v]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the Explorer to transverse a collection of persons.
|
||||
*
|
||||
* @param string $url
|
||||
* @return bool
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws NoSuchActorException
|
||||
* @throws RedirectionExceptionInterface
|
||||
@ -246,7 +241,7 @@ class Explorer
|
||||
private function travel_collection(string $url): bool
|
||||
{
|
||||
$response = HTTPClient::get($url, ['headers' => ACTIVITYPUB::HTTP_CLIENT_HEADERS]);
|
||||
$res = json_decode($response->getContent(), true);
|
||||
$res = json_decode($response->getContent(), true);
|
||||
|
||||
if (!isset($res['orderedItems'])) {
|
||||
return false;
|
||||
@ -258,7 +253,7 @@ class Explorer
|
||||
}
|
||||
}
|
||||
// Go through entire collection
|
||||
if (!is_null($res['next'])) {
|
||||
if (!\is_null($res['next'])) {
|
||||
$this->travel_collection($res['next']);
|
||||
}
|
||||
|
||||
@ -272,12 +267,12 @@ class Explorer
|
||||
* @param string $url User's url
|
||||
*
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws Exception
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string|null If it is able to fetch, false if it's gone
|
||||
* @return null|string If it is able to fetch, false if it's gone
|
||||
* // Exceptions when network issues or unsupported Activity format
|
||||
*/
|
||||
public static function get_remote_user_activity(string $url): string|null
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -17,9 +17,11 @@ declare(strict_types=1);
|
||||
*
|
||||
* @category Network
|
||||
* @package Nautilus
|
||||
*
|
||||
* @author Aaron Parecki <aaron@parecki.com>
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
|
||||
* @link https://github.com/aaronpk/Nautilus/blob/master/app/ActivityPub/HTTPSignature.php
|
||||
*
|
||||
* @see https://github.com/aaronpk/Nautilus/blob/master/app/ActivityPub/HTTPSignature.php
|
||||
*/
|
||||
|
||||
namespace Plugin\ActivityPub\Util;
|
||||
@ -34,12 +36,14 @@ class HTTPSignature
|
||||
/**
|
||||
* Sign a message with an Actor
|
||||
*
|
||||
* @param Actor $user Actor signing
|
||||
* @param string $url Inbox url
|
||||
* @param string|bool $body Data to sign (optional)
|
||||
* @param array $addlHeaders Additional headers (optional)
|
||||
* @return array Headers to be used in request
|
||||
* @param Actor $user Actor signing
|
||||
* @param string $url Inbox url
|
||||
* @param bool|string $body Data to sign (optional)
|
||||
* @param array $addlHeaders Additional headers (optional)
|
||||
*
|
||||
* @throws Exception Attempted to sign something that belongs to an Actor we don't own
|
||||
*
|
||||
* @return array Headers to be used in request
|
||||
*/
|
||||
public static function sign(Actor $user, string $url, string|bool $body = false, array $addlHeaders = []): array
|
||||
{
|
||||
@ -47,15 +51,15 @@ class HTTPSignature
|
||||
if ($body) {
|
||||
$digest = self::_digest($body);
|
||||
}
|
||||
$headers = self::_headersToSign($url, $digest);
|
||||
$headers = array_merge($headers, $addlHeaders);
|
||||
$stringToSign = self::_headersToSigningString($headers);
|
||||
$signedHeaders = implode(' ', array_map('strtolower', array_keys($headers)));
|
||||
$headers = self::_headersToSign($url, $digest);
|
||||
$headers = array_merge($headers, $addlHeaders);
|
||||
$stringToSign = self::_headersToSigningString($headers);
|
||||
$signedHeaders = implode(' ', array_map('strtolower', array_keys($headers)));
|
||||
$actor_private_key = ActivitypubRsa::getByActor($user)->getPrivateKey();
|
||||
// Intentionally unhandled exception, we want this to explode if that happens as it would be a bug
|
||||
$key = openssl_pkey_get_private($actor_private_key);
|
||||
openssl_sign($stringToSign, $signature, $key, OPENSSL_ALGO_SHA256);
|
||||
$signature = base64_encode($signature);
|
||||
openssl_sign($stringToSign, $signature, $key, \OPENSSL_ALGO_SHA256);
|
||||
$signature = base64_encode($signature);
|
||||
$signatureHeader = 'keyId="' . $user->getUri() . '#public-key' . '",headers="' . $signedHeaders . '",algorithm="rsa-sha256",signature="' . $signature . '"';
|
||||
unset($headers['(request-target)']);
|
||||
$headers['Signature'] = $signatureHeader;
|
||||
@ -65,20 +69,16 @@ class HTTPSignature
|
||||
|
||||
/**
|
||||
* @param array|string $body array or json string $body
|
||||
* @return string
|
||||
*/
|
||||
private static function _digest(array|string $body): string
|
||||
{
|
||||
if (is_array($body)) {
|
||||
if (\is_array($body)) {
|
||||
$body = json_encode($body);
|
||||
}
|
||||
return base64_encode(hash('sha256', $body, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param string|bool $digest
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function _headersToSign(string $url, string|bool $digest = false): array
|
||||
@ -86,12 +86,12 @@ class HTTPSignature
|
||||
$date = new DateTime('UTC');
|
||||
|
||||
$headers = [
|
||||
'(request-target)' => 'post ' . parse_url($url, PHP_URL_PATH),
|
||||
'Date' => $date->format('D, d M Y H:i:s \G\M\T'),
|
||||
'Host' => parse_url($url, PHP_URL_HOST),
|
||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json, application/json',
|
||||
'User-Agent' => 'GNU social ActivityPub Plugin - ' . GNUSOCIAL_ENGINE_URL,
|
||||
'Content-Type' => 'application/activity+json'
|
||||
'(request-target)' => 'post ' . parse_url($url, \PHP_URL_PATH),
|
||||
'Date' => $date->format('D, d M Y H:i:s \G\M\T'),
|
||||
'Host' => parse_url($url, \PHP_URL_HOST),
|
||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json, application/json',
|
||||
'User-Agent' => 'GNU social ActivityPub Plugin - ' . GNUSOCIAL_ENGINE_URL,
|
||||
'Content-Type' => 'application/activity+json',
|
||||
];
|
||||
|
||||
if ($digest) {
|
||||
@ -101,24 +101,14 @@ class HTTPSignature
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $headers
|
||||
* @return string
|
||||
*/
|
||||
private static function _headersToSigningString(array $headers): string
|
||||
{
|
||||
return implode("\n", array_map(function ($k, $v) {
|
||||
return strtolower($k) . ': ' . $v;
|
||||
}, array_keys($headers), $headers));
|
||||
return implode("\n", array_map(fn ($k, $v) => mb_strtolower($k) . ': ' . $v, array_keys($headers), $headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $signature
|
||||
* @return array
|
||||
*/
|
||||
public static function parseSignatureHeader(string $signature): array
|
||||
{
|
||||
$parts = explode(',', $signature);
|
||||
$parts = explode(',', $signature);
|
||||
$signatureData = [];
|
||||
|
||||
foreach ($parts as $part) {
|
||||
@ -129,51 +119,43 @@ class HTTPSignature
|
||||
|
||||
if (!isset($signatureData['keyId'])) {
|
||||
return [
|
||||
'error' => 'No keyId was found in the signature header. Found: ' . implode(', ', array_keys($signatureData))
|
||||
'error' => 'No keyId was found in the signature header. Found: ' . implode(', ', array_keys($signatureData)),
|
||||
];
|
||||
}
|
||||
|
||||
if (!filter_var($signatureData['keyId'], FILTER_VALIDATE_URL)) {
|
||||
if (!filter_var($signatureData['keyId'], \FILTER_VALIDATE_URL)) {
|
||||
return [
|
||||
'error' => 'keyId is not a URL: ' . $signatureData['keyId']
|
||||
'error' => 'keyId is not a URL: ' . $signatureData['keyId'],
|
||||
];
|
||||
}
|
||||
|
||||
if (!isset($signatureData['headers']) || !isset($signatureData['signature'])) {
|
||||
return [
|
||||
'error' => 'Signature is missing headers or signature parts'
|
||||
'error' => 'Signature is missing headers or signature parts',
|
||||
];
|
||||
}
|
||||
|
||||
return $signatureData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $publicKey
|
||||
* @param array $signatureData
|
||||
* @param array $inputHeaders
|
||||
* @param string $path
|
||||
* @param string $body
|
||||
* @return array
|
||||
*/
|
||||
public static function verify(string $publicKey, array $signatureData, array $inputHeaders, string $path, string $body): array
|
||||
{
|
||||
// We need this because the used Request headers fields specified by Signature are in lower case.
|
||||
$headersContent = array_change_key_case($inputHeaders, CASE_LOWER);
|
||||
$digest = 'SHA-256=' . base64_encode(hash('sha256', $body, true));
|
||||
$headersToSign = [];
|
||||
$headersContent = array_change_key_case($inputHeaders, \CASE_LOWER);
|
||||
$digest = 'SHA-256=' . base64_encode(hash('sha256', $body, true));
|
||||
$headersToSign = [];
|
||||
foreach (explode(' ', $signatureData['headers']) as $h) {
|
||||
if ($h == '(request-target)') {
|
||||
$headersToSign[$h] = 'post ' . $path;
|
||||
} elseif ($h == 'digest') {
|
||||
$headersToSign[$h] = $digest;
|
||||
} elseif (array_key_exists($h, $headersContent)) {
|
||||
} elseif (\array_key_exists($h, $headersContent)) {
|
||||
$headersToSign[$h] = $headersContent[$h];
|
||||
}
|
||||
}
|
||||
$signingString = self::_headersToSigningString($headersToSign);
|
||||
|
||||
$verified = openssl_verify($signingString, base64_decode($signatureData['signature']), $publicKey, OPENSSL_ALGO_SHA256);
|
||||
$verified = openssl_verify($signingString, base64_decode($signatureData['signature']), $publicKey, \OPENSSL_ALGO_SHA256);
|
||||
|
||||
return [$verified, $signingString];
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -53,29 +54,27 @@ abstract class Model
|
||||
/**
|
||||
* Create a Type from an ActivityStreams 2.0 JSON string
|
||||
*
|
||||
* @param string|array $data
|
||||
* @return Type\AbstractObject
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function jsonToType(string|array $data): Type\AbstractObject
|
||||
{
|
||||
if (is_string($data)) {
|
||||
if (\is_string($data)) {
|
||||
$attributes = json_decode($data, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE
|
||||
|| !is_array($attributes)
|
||||
if (json_last_error() !== \JSON_ERROR_NONE
|
||||
|| !\is_array($attributes)
|
||||
) {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
"An error occurred during the JSON decoding.\n '%s'",
|
||||
$data
|
||||
)
|
||||
$data,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$attributes = $data;
|
||||
}
|
||||
|
||||
if (!array_key_exists('type', $attributes)) {
|
||||
if (!\array_key_exists('type', $attributes)) {
|
||||
throw new InvalidArgumentException('Missing "type" attribute in $data: ' . var_export($data, true));
|
||||
}
|
||||
unset($data);
|
||||
@ -83,13 +82,13 @@ abstract class Model
|
||||
try {
|
||||
$type = TypeResolver::getClass($attributes['type']);
|
||||
} catch (Exception $e) {
|
||||
$message = json_encode($attributes, JSON_PRETTY_PRINT);
|
||||
$message = json_encode($attributes, \JSON_PRETTY_PRINT);
|
||||
throw new Exception(
|
||||
$e->getMessage() . "\n$message"
|
||||
$e->getMessage() . "\n{$message}",
|
||||
);
|
||||
}
|
||||
|
||||
if (is_string($type)) {
|
||||
if (\is_string($type)) {
|
||||
$type = new $type();
|
||||
}
|
||||
|
||||
@ -112,22 +111,17 @@ abstract class Model
|
||||
|
||||
/**
|
||||
* Create an Entity from an ActivityStreams 2.0 JSON string
|
||||
*
|
||||
* @param string|Type\AbstractObject $json
|
||||
* @param array $options
|
||||
* @return Entity
|
||||
*/
|
||||
abstract public static function fromJson(string|Type\AbstractObject $json, array $options = []): Entity;
|
||||
|
||||
/**
|
||||
* Get a JSON
|
||||
*
|
||||
* @param mixed $object
|
||||
* @param ?int $options PHP JSON options
|
||||
* @return string
|
||||
* @param ?int $options PHP JSON options
|
||||
*
|
||||
* @throws ClientException
|
||||
*/
|
||||
public static function toJson(mixed $object, int $options = null): string
|
||||
public static function toJson(mixed $object, ?int $options = null): string
|
||||
{
|
||||
switch ($object::class) {
|
||||
case 'App\Entity\Activity':
|
||||
@ -140,4 +134,4 @@ abstract class Model
|
||||
return $type->toJson($options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -63,29 +64,26 @@ class Activity extends Model
|
||||
* Create an Entity from an ActivityStreams 2.0 JSON string
|
||||
* This will persist new GSActivities, GSObjects, and APActivity
|
||||
*
|
||||
* @param string|AbstractObject $json
|
||||
* @param array $options
|
||||
* @return ActivitypubActivity
|
||||
* @throws NoSuchActorException
|
||||
* @throws ClientExceptionInterface
|
||||
* @throws NoSuchActorException
|
||||
* @throws RedirectionExceptionInterface
|
||||
* @throws ServerExceptionInterface
|
||||
* @throws TransportExceptionInterface
|
||||
*/
|
||||
public static function fromJson(string|AbstractObject $json, array $options = []): ActivitypubActivity
|
||||
{
|
||||
$type_activity = is_string($json) ? self::jsonToType($json) : $json;
|
||||
$type_activity = \is_string($json) ? self::jsonToType($json) : $json;
|
||||
|
||||
// Ditch known activities
|
||||
$ap_act = ActivitypubActivity::getByPK(['activity_uri' => $type_activity->get('id')]);
|
||||
if (!is_null($ap_act)) {
|
||||
if (!\is_null($ap_act)) {
|
||||
return $ap_act;
|
||||
}
|
||||
|
||||
// Find Actor and Object
|
||||
$actor = ActivityPub::getActorByUri($type_activity->get('actor'));
|
||||
$actor = ActivityPub::getActorByUri($type_activity->get('actor'));
|
||||
$type_object = $type_activity->get('object');
|
||||
if (is_string($type_object)) { // Retrieve it
|
||||
if (\is_string($type_object)) { // Retrieve it
|
||||
$type_object = ActivityPub::getObjectByUri($type_object, try_online: true);
|
||||
} else { // Encapsulated, if we have it locally, prefer it
|
||||
$type_object = ActivityPub::getObjectByUri($type_object->get('id'), try_online: false) ?? $type_object;
|
||||
@ -118,20 +116,20 @@ class Activity extends Model
|
||||
}
|
||||
// Store Activity
|
||||
$act = GSActivity::create([
|
||||
'actor_id' => $actor->getId(),
|
||||
'verb' => 'create',
|
||||
'actor_id' => $actor->getId(),
|
||||
'verb' => 'create',
|
||||
'object_type' => 'note',
|
||||
'object_id' => $note->getId(),
|
||||
'created' => new DateTime($type_activity->get('published') ?? 'now'),
|
||||
'source' => 'ActivityPub',
|
||||
'object_id' => $note->getId(),
|
||||
'created' => new DateTime($type_activity->get('published') ?? 'now'),
|
||||
'source' => 'ActivityPub',
|
||||
]);
|
||||
DB::persist($act);
|
||||
// Store ActivityPub Activity
|
||||
$ap_act = ActivitypubActivity::create([
|
||||
'activity_id' => $act->getId(),
|
||||
'activity_id' => $act->getId(),
|
||||
'activity_uri' => $type_activity->get('id'),
|
||||
'created' => new DateTime($type_activity->get('published') ?? 'now'),
|
||||
'modified' => new DateTime(),
|
||||
'created' => new DateTime($type_activity->get('published') ?? 'now'),
|
||||
'modified' => new DateTime(),
|
||||
]);
|
||||
DB::persist($ap_act);
|
||||
}
|
||||
@ -141,9 +139,6 @@ class Activity extends Model
|
||||
/**
|
||||
* Get a JSON
|
||||
*
|
||||
* @param mixed $object
|
||||
* @param int|null $options
|
||||
* @return string
|
||||
* @throws ClientException
|
||||
*/
|
||||
public static function toJson(mixed $object, ?int $options = null): string
|
||||
@ -151,24 +146,24 @@ class Activity extends Model
|
||||
if ($object::class !== 'App\Entity\Activity') {
|
||||
throw new InvalidArgumentException('First argument type is Activity');
|
||||
}
|
||||
|
||||
$gs_verb_to_activity_stream_two_verb = null;
|
||||
if (Event::handle('GSVerbToActivityStreamsTwoActivityType', [($verb = $object->getVerb()), &$gs_verb_to_activity_stream_two_verb]) === Event::next) {
|
||||
$gs_verb_to_activity_stream_two_verb = match ($verb) {
|
||||
'create' => 'Create',
|
||||
'undo' => 'Undo',
|
||||
default => throw new ClientException('Invalid verb'),
|
||||
};
|
||||
}
|
||||
|
||||
$gs_verb_to_activity_stream_two_verb = null;
|
||||
if (Event::handle('GSVerbToActivityStreamsTwoActivityType', [($verb = $object->getVerb()), &$gs_verb_to_activity_stream_two_verb]) === Event::next) {
|
||||
$gs_verb_to_activity_stream_two_verb = match ($verb) {
|
||||
'create' => 'Create',
|
||||
'undo' => 'Undo',
|
||||
default => throw new ClientException('Invalid verb'),
|
||||
};
|
||||
}
|
||||
|
||||
$attr = [
|
||||
'type' => $gs_verb_to_activity_stream_two_verb,
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => Router::url('activity_view', ['id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
'type' => $gs_verb_to_activity_stream_two_verb,
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'id' => Router::url('activity_view', ['id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
'published' => $object->getCreated()->format(DateTimeInterface::RFC3339),
|
||||
'actor' => $object->getActor()->getUri(Router::ABSOLUTE_URL),
|
||||
'to' => ['https://www.w3.org/ns/activitystreams#Public'], // TODO: implement proper scope address
|
||||
'cc' => ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
'actor' => $object->getActor()->getUri(Router::ABSOLUTE_URL),
|
||||
'to' => ['https://www.w3.org/ns/activitystreams#Public'], // TODO: implement proper scope address
|
||||
'cc' => ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
];
|
||||
$attr['object'] = ($attr['type'] === 'Create') ? self::jsonToType(Model::toJson($object->getObject())) : ActivityPub::getUriByObject($object->getObject());
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -60,29 +61,25 @@ use Plugin\ActivityPub\Util\Model;
|
||||
*/
|
||||
class Actor extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* Create an Entity from an ActivityStreams 2.0 JSON string
|
||||
* This will persist a new GSActor, ActivityPubRSA, and ActivityPubActor
|
||||
*
|
||||
* @param string|AbstractObject $json
|
||||
* @param array $options
|
||||
* @return ActivitypubActor
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function fromJson(string|AbstractObject $json, array $options = []): ActivitypubActor
|
||||
{
|
||||
$person = is_string($json) ? self::jsonToType($json) : $json;
|
||||
$person = \is_string($json) ? self::jsonToType($json) : $json;
|
||||
|
||||
// Actor
|
||||
$actor_map = [
|
||||
'nickname' => $person->get('preferredUsername'),
|
||||
'fullname' => !empty($person->get('name')) ? $person->get('name') : null,
|
||||
'created' => new DateTime($person->get('published') ?? 'now'),
|
||||
'bio' => $person->get('summary'),
|
||||
'created' => new DateTime($person->get('published') ?? 'now'),
|
||||
'bio' => $person->get('summary'),
|
||||
'is_local' => false,
|
||||
'type' => GSActor::PERSON,
|
||||
'roles' => UserRoles::USER,
|
||||
'type' => GSActor::PERSON,
|
||||
'roles' => UserRoles::USER,
|
||||
'modified' => new DateTime(),
|
||||
];
|
||||
|
||||
@ -99,11 +96,11 @@ class Actor extends Model
|
||||
|
||||
// ActivityPub Actor
|
||||
$ap_actor = ActivitypubActor::create([
|
||||
'inbox_uri' => $person->get('inbox'),
|
||||
'inbox_uri' => $person->get('inbox'),
|
||||
'inbox_shared_uri' => ($person->has('endpoints') && isset($person->get('endpoints')['sharedInbox'])) ? $person->get('endpoints')['sharedInbox'] : null,
|
||||
'uri' => $person->get('id'),
|
||||
'actor_id' => $actor->getId(),
|
||||
'url' => $person->get('url') ?? null,
|
||||
'uri' => $person->get('id'),
|
||||
'actor_id' => $actor->getId(),
|
||||
'url' => $person->get('url') ?? null,
|
||||
], $options['objects']['ActivitypubActor'] ?? null);
|
||||
|
||||
if (!isset($options['objects']['ActivitypubActor'])) {
|
||||
@ -112,7 +109,7 @@ class Actor extends Model
|
||||
|
||||
// Public Key
|
||||
$apRSA = ActivitypubRsa::create([
|
||||
'actor_id' => $actor->getID(),
|
||||
'actor_id' => $actor->getID(),
|
||||
'public_key' => ($person->has('publicKey') && isset($person->get('publicKey')['publicKeyPem'])) ? $person->get('publicKey')['publicKeyPem'] : null,
|
||||
], $options['objects']['ActivitypubRsa'] ?? null);
|
||||
|
||||
@ -125,8 +122,8 @@ class Actor extends Model
|
||||
try {
|
||||
// Retrieve media
|
||||
$get_response = HTTPClient::get($person->get('icon')->get('url'));
|
||||
$media = $get_response->getContent();
|
||||
$mimetype = $get_response->getHeaders()['content-type'][0] ?? null;
|
||||
$media = $get_response->getContent();
|
||||
$mimetype = $get_response->getHeaders()['content-type'][0] ?? null;
|
||||
unset($get_response);
|
||||
|
||||
// Only handle if it is an image
|
||||
@ -168,9 +165,8 @@ class Actor extends Model
|
||||
/**
|
||||
* Get a JSON
|
||||
*
|
||||
* @param mixed $object
|
||||
* @param int|null $options PHP JSON options
|
||||
* @return string
|
||||
* @param null|int $options PHP JSON options
|
||||
*
|
||||
* @throws ServerException
|
||||
*/
|
||||
public static function toJson(mixed $object, ?int $options = null): string
|
||||
@ -178,41 +174,41 @@ class Actor extends Model
|
||||
if ($object::class !== 'App\Entity\Actor') {
|
||||
throw new InvalidArgumentException('First argument type is Actor');
|
||||
}
|
||||
$rsa = ActivitypubRsa::getByActor($object);
|
||||
$rsa = ActivitypubRsa::getByActor($object);
|
||||
$public_key = $rsa->getPublicKey();
|
||||
$uri = null;
|
||||
$attr = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'type' => 'Person',
|
||||
'id' => $object->getUri(Router::ABSOLUTE_URL),
|
||||
'inbox' => Router::url('activitypub_actor_inbox', ['gsactor_id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
'outbox' => Router::url('activitypub_actor_outbox', ['gsactor_id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
$uri = null;
|
||||
$attr = [
|
||||
'@context' => 'https://www.w3.org/ns/activitystreams',
|
||||
'type' => 'Person',
|
||||
'id' => $object->getUri(Router::ABSOLUTE_URL),
|
||||
'inbox' => Router::url('activitypub_actor_inbox', ['gsactor_id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
'outbox' => Router::url('activitypub_actor_outbox', ['gsactor_id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
'following' => Router::url('actor_subscriptions_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
'followers' => Router::url('actor_subscribers_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
'liked' => Router::url('favourites_view_by_actor_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
'liked' => Router::url('favourites_view_by_actor_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
|
||||
//'streams' =>
|
||||
'preferredUsername' => $object->getNickname(),
|
||||
'publicKey' => [
|
||||
'id' => $uri . "#public-key",
|
||||
'owner' => $uri,
|
||||
'publicKeyPem' => $public_key
|
||||
'publicKey' => [
|
||||
'id' => $uri . '#public-key',
|
||||
'owner' => $uri,
|
||||
'publicKeyPem' => $public_key,
|
||||
],
|
||||
'name' => $object->getFullname(),
|
||||
'location' => $object->getLocation(),
|
||||
'name' => $object->getFullname(),
|
||||
'location' => $object->getLocation(),
|
||||
'published' => $object->getCreated()->format(DateTimeInterface::RFC3339),
|
||||
'summary' => $object->getBio(),
|
||||
'summary' => $object->getBio(),
|
||||
//'tag' => $object->getSelfTags(),
|
||||
'updated' => $object->getModified()->format(DateTimeInterface::RFC3339),
|
||||
'url' => $object->getUrl(Router::ABSOLUTE_URL),
|
||||
'url' => $object->getUrl(Router::ABSOLUTE_URL),
|
||||
];
|
||||
|
||||
// Avatar
|
||||
try {
|
||||
$avatar = Avatar::getAvatar($object->getId());
|
||||
$avatar = Avatar::getAvatar($object->getId());
|
||||
$attr['icon'] = $attr['image'] = [
|
||||
'type' => 'Image',
|
||||
'type' => 'Image',
|
||||
'mediaType' => $avatar->getAttachment()->getMimetype(),
|
||||
'url' => $avatar->getUrl(type: Router::ABSOLUTE_URL),
|
||||
'url' => $avatar->getUrl(type: Router::ABSOLUTE_URL),
|
||||
];
|
||||
} catch (Exception) {
|
||||
// No icon for this actor
|
||||
@ -222,4 +218,4 @@ class Actor extends Model
|
||||
Event::handle('ActivityPubAddActivityStreamsTwoData', [$type->get('type'), &$type]);
|
||||
return $type->toJson($options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,11 +39,10 @@ use App\Core\DB\DB;
|
||||
use App\Core\Event;
|
||||
use App\Core\GSFile;
|
||||
use App\Core\HTTPClient;
|
||||
use App\Core\VisibilityScope;
|
||||
use Component\Language\Entity\Language;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Log;
|
||||
use App\Core\Router\Router;
|
||||
use App\Core\VisibilityScope;
|
||||
use App\Entity\Note as GSNote;
|
||||
use App\Entity\NoteTag;
|
||||
use App\Util\Common;
|
||||
@ -56,11 +55,13 @@ use App\Util\TemporaryFile;
|
||||
use Component\Attachment\Entity\ActorToAttachment;
|
||||
use Component\Attachment\Entity\AttachmentToNote;
|
||||
use Component\Conversation\Conversation;
|
||||
use Component\Language\Entity\Language;
|
||||
use Component\Tag\Tag;
|
||||
use DateTime;
|
||||
use DateTimeInterface;
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use const PHP_URL_HOST;
|
||||
use Plugin\ActivityPub\ActivityPub;
|
||||
use Plugin\ActivityPub\Entity\ActivitypubObject;
|
||||
use Plugin\ActivityPub\Util\Model;
|
||||
@ -68,10 +69,6 @@ use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||
use function array_key_exists;
|
||||
use function is_null;
|
||||
use function is_string;
|
||||
use const PHP_URL_HOST;
|
||||
|
||||
/**
|
||||
* This class handles translation between JSON and GSNotes
|
||||
@ -98,7 +95,7 @@ class Note extends Model
|
||||
{
|
||||
$handleInReplyTo = function (AbstractObject|string $type_note): ?int {
|
||||
try {
|
||||
$parent_note = is_null($type_note->get('inReplyTo')) ? null : ActivityPub::getObjectByUri($type_note->get('inReplyTo'), try_online: true);
|
||||
$parent_note = \is_null($type_note->get('inReplyTo')) ? null : ActivityPub::getObjectByUri($type_note->get('inReplyTo'), try_online: true);
|
||||
if ($parent_note instanceof GSNote) {
|
||||
return $parent_note->getId();
|
||||
} elseif ($parent_note instanceof Type\AbstractObject && $parent_note->get('type') === 'Note') {
|
||||
@ -116,13 +113,13 @@ class Note extends Model
|
||||
};
|
||||
|
||||
$source = $options['source'] ?? 'ActivityPub';
|
||||
$type_note = is_string($json) ? self::jsonToType($json) : $json;
|
||||
$type_note = \is_string($json) ? self::jsonToType($json) : $json;
|
||||
$actor = null;
|
||||
$actor_id = null;
|
||||
if ($json instanceof AbstractObject
|
||||
&& array_key_exists('test_authority', $options)
|
||||
&& \array_key_exists('test_authority', $options)
|
||||
&& $options['test_authority']
|
||||
&& array_key_exists('actor_uri', $options)
|
||||
&& \array_key_exists('actor_uri', $options)
|
||||
) {
|
||||
$actor_uri = $options['actor_uri'];
|
||||
if ($actor_uri !== $type_note->get('attributedTo')) {
|
||||
@ -135,7 +132,7 @@ class Note extends Model
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($actor_id)) {
|
||||
if (\is_null($actor_id)) {
|
||||
$actor = ActivityPub::getActorByUri($type_note->get('attributedTo'));
|
||||
$actor_id = $actor->getId();
|
||||
}
|
||||
@ -164,14 +161,14 @@ class Note extends Model
|
||||
]);
|
||||
}
|
||||
|
||||
if (!is_null($map['language_id'])) {
|
||||
if (!\is_null($map['language_id'])) {
|
||||
$map['language_id'] = Language::getByLocale($map['language_id'])->getId();
|
||||
} else {
|
||||
$map['language_id'] = null;
|
||||
}
|
||||
|
||||
// Scope
|
||||
if (in_array('https://www.w3.org/ns/activitystreams#Public', $type_note->get('to'))) {
|
||||
if (\in_array('https://www.w3.org/ns/activitystreams#Public', $type_note->get('to'))) {
|
||||
// Public: Visible for all, shown in public feeds
|
||||
$map['scope'] = VisibilityScope::PUBLIC;
|
||||
} elseif (\in_array('https://www.w3.org/ns/activitystreams#Public', $type_note->get('cc'))) {
|
||||
@ -258,7 +255,7 @@ class Note extends Model
|
||||
case 'Hashtag':
|
||||
$match = ltrim($ap_tag->get('name'), '#');
|
||||
$tag = Tag::ensureValid($match);
|
||||
$canonical_tag = $ap_tag->get('canonical') ?? Tag::canonicalTag($tag, is_null($lang_id = $obj->getLanguageId()) ? null : Language::getById($lang_id)->getLocale());
|
||||
$canonical_tag = $ap_tag->get('canonical') ?? Tag::canonicalTag($tag, \is_null($lang_id = $obj->getLanguageId()) ? null : Language::getById($lang_id)->getLocale());
|
||||
DB::persist(NoteTag::create([
|
||||
'tag' => $tag,
|
||||
'canonical' => $canonical_tag,
|
||||
@ -323,7 +320,7 @@ class Note extends Model
|
||||
'content' => $object->getRendered(),
|
||||
'attachment' => [],
|
||||
'tag' => [],
|
||||
'inReplyTo' => is_null($object->getReplyTo()) ? null : ActivityPub::getUriByObject(GSNote::getById($object->getReplyTo())),
|
||||
'inReplyTo' => \is_null($object->getReplyTo()) ? null : ActivityPub::getUriByObject(GSNote::getById($object->getReplyTo())),
|
||||
'inConversation' => $object->getConversationUri(),
|
||||
'directMessage' => $object->getScope() === VisibilityScope::MESSAGE,
|
||||
];
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -43,8 +44,7 @@ abstract class ModelResponse
|
||||
* Provides a response in application/ld+json for ActivityStreams 2.0 Types
|
||||
*
|
||||
* @param mixed $object (Entity)
|
||||
* @param int $status The response status code
|
||||
* @return TypeResponse
|
||||
* @param int $status The response status code
|
||||
*/
|
||||
public static function handle(mixed $object, int $status = 200): TypeResponse
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -47,9 +48,8 @@ abstract class ActivityResponse
|
||||
/**
|
||||
* Provides a response in application/ld+json to GSActivity
|
||||
*
|
||||
* @param GSActivity $activity
|
||||
* @param int $status The response status code
|
||||
* @return TypeResponse
|
||||
*
|
||||
* @throws ClientException
|
||||
*/
|
||||
public static function handle(GSActivity $activity, int $status = 200): TypeResponse
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -47,9 +48,8 @@ abstract class ActorResponse
|
||||
/**
|
||||
* Provides a response in application/ld+json to GSActors
|
||||
*
|
||||
* @param GSActor $gsactor
|
||||
* @param int $status The response status code
|
||||
* @return TypeResponse
|
||||
*
|
||||
* @throws ClientException
|
||||
*/
|
||||
public static function handle(GSActor $gsactor, int $status = 200): TypeResponse
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -46,9 +47,7 @@ abstract class NoteResponse
|
||||
/**
|
||||
* Provides a response in application/ld+json to GSNotes
|
||||
*
|
||||
* @param GSNote $note
|
||||
* @param int $status The response status code
|
||||
* @return TypeResponse
|
||||
*/
|
||||
public static function handle(GSNote $note, int $status = 200): TypeResponse
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -45,13 +46,13 @@ class TypeResponse extends JsonResponse
|
||||
/**
|
||||
* Provides a response in application/ld+json for ActivityStreams 2.0 Types
|
||||
*
|
||||
* @param string|AbstractObject|null $json
|
||||
* @param int $status The response status code
|
||||
* @param null|AbstractObject|string $json
|
||||
* @param int $status The response status code
|
||||
*/
|
||||
public function __construct(string|AbstractObject|null $json = null, int $status = 202)
|
||||
{
|
||||
parent::__construct(
|
||||
data: is_object($json) ? $json->toJson() : $json,
|
||||
data: \is_object($json) ? $json->toJson() : $json,
|
||||
status: $status,
|
||||
headers: ['content-type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'],
|
||||
json: true,
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -49,8 +50,8 @@ class contentLangModelValidator extends ModelValidator
|
||||
* Validate manuallyApprovesFollowers value
|
||||
*
|
||||
* @param string $value
|
||||
* @param mixed $container A Note
|
||||
* @return bool
|
||||
* @param mixed $container A Note
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function validate($value, $container): bool
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
*
|
||||
* @package GNUsocial
|
||||
* @category ActivityPub
|
||||
*
|
||||
* @author Diogo Peralta Cordeiro <@diogo.site>
|
||||
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
@ -49,8 +50,8 @@ class manuallyApprovesFollowersModelValidator extends ModelValidator
|
||||
* Validate manuallyApprovesFollowers value
|
||||
*
|
||||
* @param string $value
|
||||
* @param mixed $container A Person
|
||||
* @return bool
|
||||
* @param mixed $container A Person
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function validate($value, $container): bool
|
||||
|
@ -23,16 +23,16 @@ declare(strict_types = 1);
|
||||
|
||||
namespace Plugin\AttachmentCollections\Controller;
|
||||
|
||||
use App\Core\Form;
|
||||
use App\Core\DB\DB;
|
||||
use App\Util\Common;
|
||||
use App\Core\Router\Router;
|
||||
use App\Core\Form;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Router\Router;
|
||||
use App\Util\Common;
|
||||
use Component\Feed\Util\FeedController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Plugin\AttachmentCollections\Entity\Collection;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class Controller extends FeedController
|
||||
{
|
||||
@ -47,16 +47,18 @@ class Controller extends FeedController
|
||||
}
|
||||
/**
|
||||
* Generate Collections page
|
||||
*
|
||||
* @param int $id actor id
|
||||
* @param ?string $nickname actor nickname
|
||||
* @return array twig template options
|
||||
*
|
||||
* @return array twig template options
|
||||
*/
|
||||
public function collectionsView(Request $request, int $id, ?string $nickname): array
|
||||
{
|
||||
$collections = DB::dql(
|
||||
'select collection from Plugin\AttachmentCollections\Entity\Collection collection '
|
||||
. 'where collection.actor_id = :id',
|
||||
['id' => $id]
|
||||
['id' => $id],
|
||||
);
|
||||
// create collection form
|
||||
$create = null;
|
||||
@ -64,9 +66,9 @@ class Controller extends FeedController
|
||||
$create = Form::create([
|
||||
['name', TextType::class, [
|
||||
'label' => _m('Create collection'),
|
||||
'attr' => [
|
||||
'attr' => [
|
||||
'placeholder' => _m('Name'),
|
||||
'required' => 'required'
|
||||
'required' => 'required',
|
||||
],
|
||||
'data' => '',
|
||||
]],
|
||||
@ -80,7 +82,7 @@ class Controller extends FeedController
|
||||
$create->handleRequest($request);
|
||||
if ($create->isSubmitted() && $create->isValid()) {
|
||||
DB::persist(Collection::create([
|
||||
'name' => $create->getData()['name'],
|
||||
'name' => $create->getData()['name'],
|
||||
'actor_id' => $id,
|
||||
]));
|
||||
DB::flush();
|
||||
@ -94,15 +96,14 @@ class Controller extends FeedController
|
||||
// Instead, I'm using an anonymous class to encapsulate
|
||||
// the functions and passing how the class to the template.
|
||||
// It's suggested at https://stackoverflow.com/a/50364502.
|
||||
$fn = new class ($id, $nickname, $request)
|
||||
{
|
||||
$fn = new class($id, $nickname, $request) {
|
||||
private $id;
|
||||
private $nick;
|
||||
private $request;
|
||||
public function __construct($id, $nickname, $request)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->nick = $nickname;
|
||||
$this->id = $id;
|
||||
$this->nick = $nickname;
|
||||
$this->request = $request;
|
||||
}
|
||||
// there's already a injected function called path,
|
||||
@ -114,12 +115,12 @@ class Controller extends FeedController
|
||||
if (\is_null($this->nick)) {
|
||||
return Router::url(
|
||||
'collection_notes_view_by_actor_id',
|
||||
['id' => $this->id, 'cid' => $cid]
|
||||
['id' => $this->id, 'cid' => $cid],
|
||||
);
|
||||
}
|
||||
return Router::url(
|
||||
'collection_notes_view_by_nickname',
|
||||
['nickname' => $this->nick, 'cid' => $cid]
|
||||
['nickname' => $this->nick, 'cid' => $cid],
|
||||
);
|
||||
}
|
||||
// There are many collections in this page and we need two
|
||||
@ -133,11 +134,11 @@ class Controller extends FeedController
|
||||
['name', TextType::class, [
|
||||
'attr' => [
|
||||
'placeholder' => 'New name',
|
||||
'required' => 'required'
|
||||
'required' => 'required',
|
||||
],
|
||||
'data' => '',
|
||||
]],
|
||||
['update_'.$collection->getId(), SubmitType::class, [
|
||||
['update_' . $collection->getId(), SubmitType::class, [
|
||||
'label' => _m('Save'),
|
||||
'attr' => [
|
||||
'title' => _m('Save'),
|
||||
@ -156,7 +157,7 @@ class Controller extends FeedController
|
||||
public function rmForm($collection)
|
||||
{
|
||||
$rm = Form::create([
|
||||
['remove_'.$collection->getId(), SubmitType::class, [
|
||||
['remove_' . $collection->getId(), SubmitType::class, [
|
||||
'label' => _m('Delete collection'),
|
||||
'attr' => [
|
||||
'title' => _m('Delete collection'),
|
||||
@ -190,12 +191,12 @@ class Controller extends FeedController
|
||||
public function collectionNotesByActorId(Request $request, int $id, int $cid): array
|
||||
{
|
||||
$collection = DB::findOneBy('attachment_collection', ['id' => $cid]);
|
||||
$attchs = DB::dql(
|
||||
$attchs = DB::dql(
|
||||
'select attch from attachment_album_entry entry '
|
||||
. 'left join Component\Attachment\Entity\Attachment attch '
|
||||
. 'with entry.attachment_id = attch.id '
|
||||
. 'where entry.collection_id = :cid',
|
||||
['cid' => $cid]
|
||||
['cid' => $cid],
|
||||
);
|
||||
return [
|
||||
'_template' => 'AttachmentCollections/collection.html.twig',
|
||||
|
@ -1,7 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Plugin\AttachmentCollections\Entity;
|
||||
|
||||
use App\Core\Entity;
|
||||
|
||||
class Collection extends Entity
|
||||
{
|
||||
// These tags are meant to be literally included and will be populated with the appropriate fields, setters and getters by `bin/generate_entity_fields`
|
||||
@ -44,22 +48,19 @@ class Collection extends Entity
|
||||
return $this->actor_id;
|
||||
}
|
||||
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
// }}} Autocode
|
||||
|
||||
|
||||
public static function schemaDef()
|
||||
{
|
||||
return [
|
||||
'name' => 'attachment_collection',
|
||||
'fields' => [
|
||||
'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
|
||||
'name' => ['type' => 'varchar', 'length' => 255, 'description' => 'collection\'s name'],
|
||||
'name' => ['type' => 'varchar', 'length' => 255, 'description' => 'collection\'s name'],
|
||||
'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to many', 'not null' => true, 'description' => 'foreign key to actor table'],
|
||||
],
|
||||
'primary key' => ['id'],
|
||||
'primary key' => ['id'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
namespace Plugin\AttachmentCollections\Entity;
|
||||
|
||||
use App\Core\Entity;
|
||||
|
||||
class CollectionEntry extends Entity
|
||||
{
|
||||
// These tags are meant to be literally included and will be populated with the appropriate fields, setters and getters by `bin/generate_entity_fields`
|
||||
@ -44,11 +48,9 @@ class CollectionEntry extends Entity
|
||||
return $this->collection_id;
|
||||
}
|
||||
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
// }}} Autocode
|
||||
|
||||
|
||||
public static function schemaDef()
|
||||
{
|
||||
return [
|
||||
@ -62,4 +64,3 @@ class CollectionEntry extends Entity
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
// {{{ License
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
@ -34,12 +34,12 @@ namespace Plugin\AudioEncoder;
|
||||
|
||||
use App\Core\Event;
|
||||
use App\Core\GSFile;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Core\Modules\Plugin;
|
||||
use App\Util\Exception\ServerException;
|
||||
use App\Util\Formatting;
|
||||
use FFMpeg\FFProbe as ffprobe;
|
||||
use SplFileInfo;
|
||||
use function App\Core\I18n\_m;
|
||||
|
||||
class AudioEncoder extends Plugin
|
||||
{
|
||||
@ -66,7 +66,7 @@ class AudioEncoder extends Plugin
|
||||
* Adds duration metadata to audios
|
||||
*
|
||||
* @param null|string $mimetype in/out
|
||||
* @param null|int $width out audio duration
|
||||
* @param null|int $width out audio duration
|
||||
*
|
||||
* @return bool true if metadata filled
|
||||
*/
|
||||
@ -75,14 +75,14 @@ class AudioEncoder extends Plugin
|
||||
// Create FFProbe instance
|
||||
// Need to explicitly tell the drivers' location, or it won't find them
|
||||
$ffprobe = ffprobe::create([
|
||||
'ffmpeg.binaries' => exec('which ffmpeg'),
|
||||
'ffmpeg.binaries' => exec('which ffmpeg'),
|
||||
'ffprobe.binaries' => exec('which ffprobe'),
|
||||
]);
|
||||
|
||||
$metadata = $ffprobe->streams($file->getRealPath()) // extracts streams informations
|
||||
->audios() // filters audios streams
|
||||
->first(); // returns the first audio stream
|
||||
$width = (int)ceil((float)$metadata->get('duration'));
|
||||
->audios() // filters audios streams
|
||||
->first(); // returns the first audio stream
|
||||
$width = (int) ceil((float) $metadata->get('duration'));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -100,7 +100,7 @@ class AudioEncoder extends Plugin
|
||||
'audioEncoder/audioEncoderView.html.twig',
|
||||
[
|
||||
'attachment' => $vars['attachment'],
|
||||
'note' => $vars['note'],
|
||||
'note' => $vars['note'],
|
||||
],
|
||||
);
|
||||
return Event::stop;
|
||||
@ -112,9 +112,9 @@ class AudioEncoder extends Plugin
|
||||
public function onPluginVersion(array &$versions): bool
|
||||
{
|
||||
$versions[] = [
|
||||
'name' => 'AudioEncoder',
|
||||
'version' => self::version(),
|
||||
'author' => 'Diogo Peralta Cordeiro',
|
||||
'name' => 'AudioEncoder',
|
||||
'version' => self::version(),
|
||||
'author' => 'Diogo Peralta Cordeiro',
|
||||
'rawdescription' => _m('Use PHP-FFMpeg for some more audio support.'),
|
||||
];
|
||||
return Event::next;
|
||||
|
@ -25,8 +25,8 @@ namespace Plugin\Cover\Entity;
|
||||
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use Component\Attachment\Entity\Attachment;
|
||||
use App\Util\Common;
|
||||
use Component\Attachment\Entity\Attachment;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
|
@ -187,8 +187,8 @@ class AttachmentEmbed extends Entity
|
||||
$attr['has_attachment'] = false;
|
||||
} elseif (!\is_null($thumbnail)) {
|
||||
$attr['has_attachment'] = true;
|
||||
$attr['width'] = $thumbnail->getWidth();
|
||||
$attr['height'] = $thumbnail->getHeight();
|
||||
$attr['width'] = $thumbnail->getWidth();
|
||||
$attr['height'] = $thumbnail->getHeight();
|
||||
}
|
||||
return $attr;
|
||||
}
|
||||
|
@ -55,7 +55,8 @@ class ImageEncoder extends Plugin
|
||||
return '3.0.0';
|
||||
}
|
||||
|
||||
public static function shouldHandle (string $mimetype): bool {
|
||||
public static function shouldHandle(string $mimetype): bool
|
||||
{
|
||||
return GSFile::mimetypeMajor($mimetype) === 'image';
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
|
||||
@ -26,6 +26,7 @@ namespace Plugin\Oomox\Controller;
|
||||
use App\Core\Cache;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Form;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\ClientException;
|
||||
use App\Util\Exception\NoLoggedInUser;
|
||||
@ -41,8 +42,6 @@ use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\SubmitButton;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use function App\Core\I18n\_m;
|
||||
use function is_null;
|
||||
|
||||
/**
|
||||
* Oomox controller
|
||||
@ -64,67 +63,67 @@ class Oomox
|
||||
*/
|
||||
public function getOomoxForm(?EntityOomox $current_oomox_settings, bool $is_light): FormInterface
|
||||
{
|
||||
$theme = $is_light ? 'light' : 'dark';
|
||||
$foreground = 'colour_foreground_' . $theme;
|
||||
$theme = $is_light ? 'light' : 'dark';
|
||||
$foreground = 'colour_foreground_' . $theme;
|
||||
$background_hard = 'colour_background_hard_' . $theme;
|
||||
$background_card = 'colour_background_card_' . $theme;
|
||||
$border = 'colour_border_' . $theme;
|
||||
$accent = 'colour_accent_' . $theme;
|
||||
$reset = 'colour_reset_' . $theme;
|
||||
$save = 'save_oomox_colours_' . $theme;
|
||||
$border = 'colour_border_' . $theme;
|
||||
$accent = 'colour_accent_' . $theme;
|
||||
$reset = 'colour_reset_' . $theme;
|
||||
$save = 'save_oomox_colours_' . $theme;
|
||||
|
||||
if (isset($current_oomox_settings)) {
|
||||
if ($is_light) {
|
||||
$current_foreground = $current_oomox_settings->getColourForegroundLight() ?: '#09090d';
|
||||
$current_foreground = $current_oomox_settings->getColourForegroundLight() ?: '#09090d';
|
||||
$current_background_hard = $current_oomox_settings->getColourBackgroundHardLight() ?: '#ebebeb';
|
||||
$current_background_card = $current_oomox_settings->getColourBackgroundCardLight() ?: '#f0f0f0';
|
||||
$current_border = $current_oomox_settings->getColourBorderLight() ?: '#d5d5d5';
|
||||
$current_accent = $current_oomox_settings->getColourAccentLight() ?: '#a22430';
|
||||
$current_border = $current_oomox_settings->getColourBorderLight() ?: '#d5d5d5';
|
||||
$current_accent = $current_oomox_settings->getColourAccentLight() ?: '#a22430';
|
||||
} else {
|
||||
$current_foreground = $current_oomox_settings->getColourForegroundDark() ?: '#f0f6f6';
|
||||
$current_foreground = $current_oomox_settings->getColourForegroundDark() ?: '#f0f6f6';
|
||||
$current_background_hard = $current_oomox_settings->getColourBackgroundHardDark() ?: '#141216';
|
||||
$current_background_card = $current_oomox_settings->getColourBackgroundCardDark() ?: '#131217';
|
||||
$current_border = $current_oomox_settings->getColourBorderDark() ?: '#201f25';
|
||||
$current_accent = $current_oomox_settings->getColourAccentDark() ?: '#5ddbcf';
|
||||
$current_border = $current_oomox_settings->getColourBorderDark() ?: '#201f25';
|
||||
$current_accent = $current_oomox_settings->getColourAccentDark() ?: '#5ddbcf';
|
||||
}
|
||||
} else {
|
||||
$current_foreground = $is_light ? '#09090d' : '#f0f6f6';
|
||||
$current_foreground = $is_light ? '#09090d' : '#f0f6f6';
|
||||
$current_background_hard = $is_light ? '#ebebeb' : '#141216';
|
||||
$current_background_card = $is_light ? '#f0f0f0' : '#131217';
|
||||
$current_border = $is_light ? '#d5d5d5' : '#201f25';
|
||||
$current_accent = $is_light ? '#a22430' : '#5ddbcf';
|
||||
$current_border = $is_light ? '#d5d5d5' : '#201f25';
|
||||
$current_accent = $is_light ? '#a22430' : '#5ddbcf';
|
||||
}
|
||||
|
||||
return Form::create([
|
||||
[$foreground, ColorType::class, [
|
||||
'html5' => true,
|
||||
'data' => $current_foreground,
|
||||
'data' => $current_foreground,
|
||||
'label' => _m('Foreground colour'),
|
||||
'help' => _m('Choose the foreground colour'),],
|
||||
'help' => _m('Choose the foreground colour'), ],
|
||||
],
|
||||
[$background_hard, ColorType::class, [
|
||||
'html5' => true,
|
||||
'data' => $current_background_hard,
|
||||
'data' => $current_background_hard,
|
||||
'label' => _m('Background colour'),
|
||||
'help' => _m('Choose the background colour'),],
|
||||
'help' => _m('Choose the background colour'), ],
|
||||
],
|
||||
[$background_card, ColorType::class, [
|
||||
'html5' => true,
|
||||
'data' => $current_background_card,
|
||||
'data' => $current_background_card,
|
||||
'label' => _m('Card background colour'),
|
||||
'help' => _m('Choose the card background colour'),],
|
||||
'help' => _m('Choose the card background colour'), ],
|
||||
],
|
||||
[$border, ColorType::class, [
|
||||
'html5' => true,
|
||||
'data' => $current_border,
|
||||
'data' => $current_border,
|
||||
'label' => _m('Border colour'),
|
||||
'help' => _m('Choose the borders accents'),],
|
||||
'help' => _m('Choose the borders accents'), ],
|
||||
],
|
||||
[$accent, ColorType::class, [
|
||||
'html5' => true,
|
||||
'data' => $current_accent,
|
||||
'data' => $current_accent,
|
||||
'label' => _m('Accent colour'),
|
||||
'help' => _m('Choose the accent colour'),],
|
||||
'help' => _m('Choose the accent colour'), ],
|
||||
],
|
||||
['hidden', HiddenType::class, []],
|
||||
[$reset, SubmitType::class, ['label' => _m('Reset colours to default')]],
|
||||
@ -141,11 +140,11 @@ class Oomox
|
||||
*/
|
||||
public static function oomoxSettingsLight(Request $request): array
|
||||
{
|
||||
$user = Common::ensureLoggedIn();
|
||||
$user = Common::ensureLoggedIn();
|
||||
$actor_id = $user->getId();
|
||||
|
||||
$current_oomox_settings = PluginOomox::getEntity($user);
|
||||
$form_light = (new self)->getOomoxForm($current_oomox_settings, true);
|
||||
$form_light = (new self)->getOomoxForm($current_oomox_settings, true);
|
||||
|
||||
$form_light->handleRequest($request);
|
||||
if ($form_light->isSubmitted() && $form_light->isValid()) {
|
||||
@ -156,15 +155,15 @@ class Oomox
|
||||
$current_oomox_settings?->resetTheme(true);
|
||||
}
|
||||
} else {
|
||||
$data = $form_light->getData();
|
||||
$data = $form_light->getData();
|
||||
$current_oomox_settings = EntityOomox::create(
|
||||
[
|
||||
'actor_id' => $actor_id,
|
||||
'colour_foreground_light' => $data['colour_foreground_light'],
|
||||
'actor_id' => $actor_id,
|
||||
'colour_foreground_light' => $data['colour_foreground_light'],
|
||||
'colour_background_hard_light' => $data['colour_background_hard_light'],
|
||||
'colour_background_card_light' => $data['colour_background_card_light'],
|
||||
'colour_border_light' => $data['colour_border_light'],
|
||||
'colour_accent_light' => $data['colour_accent_light'],
|
||||
'colour_border_light' => $data['colour_border_light'],
|
||||
'colour_accent_light' => $data['colour_accent_light'],
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -187,11 +186,11 @@ class Oomox
|
||||
*/
|
||||
public static function oomoxSettingsDark(Request $request): array
|
||||
{
|
||||
$user = Common::ensureLoggedIn();
|
||||
$user = Common::ensureLoggedIn();
|
||||
$actor_id = $user->getId();
|
||||
|
||||
$current_oomox_settings = PluginOomox::getEntity($user);
|
||||
$form_dark = (new self)->getOomoxForm($current_oomox_settings, false);
|
||||
$form_dark = (new self)->getOomoxForm($current_oomox_settings, false);
|
||||
|
||||
$form_dark->handleRequest($request);
|
||||
if ($form_dark->isSubmitted() && $form_dark->isValid()) {
|
||||
@ -200,15 +199,15 @@ class Oomox
|
||||
if ($reset_button->isClicked()) {
|
||||
$current_oomox_settings?->resetTheme(false);
|
||||
} else {
|
||||
$data = $form_dark->getData();
|
||||
$data = $form_dark->getData();
|
||||
$current_oomox_settings = EntityOomox::create(
|
||||
[
|
||||
'actor_id' => $actor_id,
|
||||
'colour_foreground_dark' => $data['colour_foreground_dark'],
|
||||
'actor_id' => $actor_id,
|
||||
'colour_foreground_dark' => $data['colour_foreground_dark'],
|
||||
'colour_background_hard_dark' => $data['colour_background_hard_dark'],
|
||||
'colour_background_card_dark' => $data['colour_background_card_dark'],
|
||||
'colour_border_dark' => $data['colour_border_dark'],
|
||||
'colour_accent_dark' => $data['colour_accent_dark'],
|
||||
'colour_border_dark' => $data['colour_border_dark'],
|
||||
'colour_accent_dark' => $data['colour_accent_dark'],
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -225,18 +224,16 @@ class Oomox
|
||||
/**
|
||||
* Renders the resulting CSS file from user options, serves that file as a response
|
||||
*
|
||||
* @return Response
|
||||
* @throws ClientException
|
||||
* @throws NoLoggedInUser
|
||||
* @throws ServerException
|
||||
*
|
||||
* @throws ClientException
|
||||
*/
|
||||
public function oomoxCSS(): Response
|
||||
{
|
||||
$user = Common::ensureLoggedIn();
|
||||
|
||||
$oomox_table = PluginOomox::getEntity($user);
|
||||
if (is_null($oomox_table)) {
|
||||
if (\is_null($oomox_table)) {
|
||||
throw new ClientException(_m('No custom colours defined', 404));
|
||||
}
|
||||
|
||||
|
@ -197,20 +197,20 @@ class Oomox extends Entity
|
||||
return $this->modified;
|
||||
}
|
||||
|
||||
public function resetTheme(bool $is_light) {
|
||||
public function resetTheme(bool $is_light)
|
||||
{
|
||||
if ($is_light) {
|
||||
$this->colour_background_hard_light = '#09090d';
|
||||
$this->colour_background_card_light = '#ebebeb';
|
||||
$this->colour_foreground_light = '#f0f0f0';
|
||||
$this->colour_border_light = '#d5d5d5';
|
||||
$this->colour_accent_light = '#a22430';
|
||||
$this->colour_foreground_light = '#f0f0f0';
|
||||
$this->colour_border_light = '#d5d5d5';
|
||||
$this->colour_accent_light = '#a22430';
|
||||
} else {
|
||||
|
||||
$this->colour_background_hard_dark = '#141216';
|
||||
$this->colour_background_card_dark = '#131217';
|
||||
$this->colour_foreground_dark = '#f0f6f6';
|
||||
$this->colour_border_dark = '#201f25';
|
||||
$this->colour_accent_dark = '#5ddbcf';
|
||||
$this->colour_foreground_dark = '#f0f6f6';
|
||||
$this->colour_border_dark = '#201f25';
|
||||
$this->colour_accent_dark = '#5ddbcf';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,13 +29,13 @@ use App\Core\Router\RouteLoader;
|
||||
use App\Core\Router\Router;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Actor;
|
||||
use Component\Language\Entity\Language;
|
||||
use App\Entity\Note;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\DuplicateFoundException;
|
||||
use App\Util\Exception\NotFoundException;
|
||||
use App\Util\Exception\ServerException;
|
||||
use App\Util\Formatting;
|
||||
use Component\Language\Entity\Language;
|
||||
use Component\Posting\Posting;
|
||||
use DateTime;
|
||||
use Plugin\RepeatNote\Entity\NoteRepeat;
|
||||
@ -53,11 +53,11 @@ class RepeatNote extends NoteHandlerPlugin
|
||||
|
||||
if (!\is_null($repeat_entity)) {
|
||||
return DB::findBy('activity', [
|
||||
'actor_id' => $actor_id,
|
||||
'verb' => 'repeat',
|
||||
'object_type' => 'note',
|
||||
'object_id' => $note->getId()
|
||||
], order_by: ['created' => 'DESC'])[0];
|
||||
'actor_id' => $actor_id,
|
||||
'verb' => 'repeat',
|
||||
'object_type' => 'note',
|
||||
'object_id' => $note->getId(),
|
||||
], order_by: ['created' => 'DESC'])[0];
|
||||
}
|
||||
|
||||
// Create a new note with the same content as the original
|
||||
|
@ -31,12 +31,12 @@ use function App\Core\I18n\_m;
|
||||
use App\Entity\Actor;
|
||||
use App\Entity\ActorTag;
|
||||
use App\Entity\ActorTagBlock;
|
||||
use Component\Language\Entity\Language;
|
||||
use App\Entity\Note;
|
||||
use App\Entity\NoteTag;
|
||||
use App\Entity\NoteTagBlock;
|
||||
use App\Util\Common;
|
||||
use App\Util\Exception\RedirectException;
|
||||
use Component\Language\Entity\Language;
|
||||
use Component\Tag\Tag;
|
||||
use Functional as F;
|
||||
use Plugin\TagBasedFiltering\TagBasedFiltering as TagFilerPlugin;
|
||||
|
@ -31,7 +31,7 @@ class TreeNotes extends Plugin
|
||||
*/
|
||||
public function onFormatNoteList(array $notes_in, ?array &$notes_out)
|
||||
{
|
||||
$roots = array_filter($notes_in, static fn (Note $note) => is_null($note->getReplyTo()));
|
||||
$roots = array_filter($notes_in, static fn (Note $note) => \is_null($note->getReplyTo()));
|
||||
$notes_out = $this->build_tree($roots, $notes_in);
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ abstract class Entity
|
||||
public function getNotificationTargetIds(array $ids_already_known = [], ?int $sender_id = null, bool $include_additional = true): array
|
||||
{
|
||||
// Additional actors that should know about this
|
||||
if (array_key_exists('additional', $ids_already_known)) {
|
||||
if (\array_key_exists('additional', $ids_already_known)) {
|
||||
return $ids_already_known['additional'];
|
||||
}
|
||||
return [];
|
||||
|
@ -44,7 +44,7 @@ class BugFoundException extends ServerException
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$frame = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, limit: 2)[1];
|
||||
$file = mb_substr($frame['file'], \mb_strlen(INSTALLDIR) + 1);
|
||||
$file = mb_substr($frame['file'], mb_strlen(INSTALLDIR) + 1);
|
||||
Log::critical("{$log_message} in {$file}:{$frame['line']}");
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ declare(strict_types = 1);
|
||||
|
||||
namespace App\Util\Exception;
|
||||
|
||||
class FileNotAllowedException extends \InvalidArgumentException
|
||||
use InvalidArgumentException;
|
||||
|
||||
class FileNotAllowedException extends InvalidArgumentException
|
||||
{
|
||||
public function __construct(string $mimetype)
|
||||
{
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
// {{{ License
|
||||
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
|
@ -6,8 +6,8 @@ namespace App\Util\Form;
|
||||
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Entity\Actor;
|
||||
use Component\Language\Entity\Language;
|
||||
use App\Util\Common;
|
||||
use Component\Language\Entity\Language;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||
|
@ -263,7 +263,7 @@ abstract class Formatting
|
||||
// php-intl is highly recommended...
|
||||
if (!\function_exists('transliterator_transliterate')) {
|
||||
$str = preg_replace('/[^\pL\pN]/u', '', $str);
|
||||
$str = mb_convert_case($str, MB_CASE_LOWER, 'UTF-8');
|
||||
$str = mb_convert_case($str, \MB_CASE_LOWER, 'UTF-8');
|
||||
return mb_substr($str, 0, $length);
|
||||
}
|
||||
$str = transliterator_transliterate('Any-Latin;' // any charset to latin compatible
|
||||
|
@ -39,7 +39,7 @@ class Notification
|
||||
public const NOTICE_BY_SUBSCRIBED = 1;
|
||||
public const MENTION = 2;
|
||||
public const REPLY = 3;
|
||||
public const SUBSCRIPTION = 4;
|
||||
public const SUBSCRIPTION = 4;
|
||||
public const FAVORITE = 5;
|
||||
public const NUDGE = 6;
|
||||
public const DM = 7;
|
||||
|
@ -24,10 +24,10 @@ namespace App\Tests\Entity;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Event;
|
||||
use App\Core\GSFile;
|
||||
use Component\Attachment\Entity\AttachmentToNote;
|
||||
use App\Entity\Note;
|
||||
use App\Util\GNUsocialTestCase;
|
||||
use App\Util\TemporaryFile;
|
||||
use Component\Attachment\Entity\AttachmentToNote;
|
||||
use Jchook\AssertThrows\AssertThrows;
|
||||
use SplFileInfo;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
|
@ -23,9 +23,9 @@ namespace App\Tests\Entity;
|
||||
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Event;
|
||||
use Component\Attachment\Entity\AttachmentThumbnail;
|
||||
use App\Util\Exception\NotStoredLocallyException;
|
||||
use App\Util\GNUsocialTestCase;
|
||||
use Component\Attachment\Entity\AttachmentThumbnail;
|
||||
use Functional as F;
|
||||
use Jchook\AssertThrows\AssertThrows;
|
||||
use SplFileInfo;
|
||||
|
Loading…
Reference in New Issue
Block a user