forked from GNUsocial/gnu-social
[ActivityPub][Inbox] Restore Create Note Functionality
Minor bug fixes
This commit is contained in:
parent
7145dba8af
commit
56526c9ba6
@ -38,7 +38,7 @@ class Link extends Component
|
||||
/**
|
||||
* Extract URLs from $content and create the appropriate Link and NoteToLink entities
|
||||
*/
|
||||
public function onProcessNoteContent(Note $note, string $content)
|
||||
public function onProcessNoteContent(Note $note, string $content): bool
|
||||
{
|
||||
if (Common::config('attachments', 'process_links')) {
|
||||
$matched_urls = [];
|
||||
@ -56,9 +56,10 @@ class Link extends Component
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function onRenderContent(string &$text)
|
||||
public function onRenderPlainTextContent(string &$text): bool
|
||||
{
|
||||
$text = $this->replaceURLs($text);
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function getURLRegex(): string
|
||||
|
@ -21,6 +21,7 @@ namespace Component\Notification\Entity;
|
||||
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Actor;
|
||||
use DateTimeInterface;
|
||||
|
||||
|
@ -155,7 +155,7 @@ class Posting extends Component
|
||||
{
|
||||
$rendered = null;
|
||||
$mentions = [];
|
||||
Event::handle('RenderNoteContent', [$content, $content_type, &$rendered, &$mentions, $actor, $language]);
|
||||
Event::handle('RenderNoteContent', [$content, $content_type, &$rendered, $actor, $language, &$mentions]);
|
||||
$note = Note::create([
|
||||
'actor_id' => $actor->getId(),
|
||||
'content' => $content,
|
||||
@ -216,7 +216,7 @@ class Posting extends Component
|
||||
return $note;
|
||||
}
|
||||
|
||||
public function onRenderNoteContent(string $content, string $content_type, ?string &$rendered, array &$mentions, Actor $author, string $language)
|
||||
public function onRenderNoteContent(string $content, string $content_type, ?string &$rendered, Actor $author, ?string $language = null, array &$mentions = [])
|
||||
{
|
||||
switch ($content_type) {
|
||||
case 'text/plain':
|
||||
|
@ -60,7 +60,7 @@ class Tag extends Component
|
||||
/**
|
||||
* Process note by extracting any tags present
|
||||
*/
|
||||
public function onProcessNoteContent(Note $note, string $content)
|
||||
public function onProcessNoteContent(Note $note, string $content): bool
|
||||
{
|
||||
$matched_tags = [];
|
||||
$processed_tags = false;
|
||||
@ -75,12 +75,16 @@ class Tag extends Component
|
||||
if ($processed_tags) {
|
||||
DB::flush();
|
||||
}
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function onRenderContent(string &$text, string $language)
|
||||
public function onRenderPlainTextNoteContent(string &$text, ?string $language = null): bool
|
||||
{
|
||||
if (!is_null($language)) {
|
||||
$text = preg_replace_callback(self::TAG_REGEX, fn ($m) => $m[1] . self::tagLink($m[2], $language), $text);
|
||||
}
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
private static function tagLink(string $tag, string $language): string
|
||||
{
|
||||
@ -113,7 +117,7 @@ class Tag extends Component
|
||||
*
|
||||
* $term /^(note|tag|people|actor)/ means we want to match only either a note or an actor
|
||||
*/
|
||||
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, &$note_expr, &$actor_expr)
|
||||
public function onSearchCreateExpression(ExpressionBuilder $eb, string $term, &$note_expr, &$actor_expr): bool
|
||||
{
|
||||
$search_term = str_contains($term, ':#') ? explode(':', $term)[1] : $term;
|
||||
$temp_note_expr = $eb->eq('note_tag.tag', $search_term);
|
||||
@ -132,9 +136,10 @@ class Tag extends Component
|
||||
return Event::stop;
|
||||
}
|
||||
|
||||
public function onSeachQueryAddJoins(QueryBuilder &$note_qb, QueryBuilder &$actor_qb)
|
||||
public function onSeachQueryAddJoins(QueryBuilder &$note_qb, QueryBuilder &$actor_qb): bool
|
||||
{
|
||||
$note_qb->join('App\Entity\NoteTag', 'note_tag', Expr\Join::WITH, 'note_tag.note_id = note.id');
|
||||
$actor_qb->join('App\Entity\ActorTag', 'actor_tag', Expr\Join::WITH, 'actor_tag.tagger = actor.id');
|
||||
return Event::next;
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,8 @@ class ActivityPub extends Plugin
|
||||
];
|
||||
|
||||
// So that this isn't hardcoded everywhere
|
||||
public const PUBLIC_TO = ['https://www.w3.org/ns/activitystreams#Public',
|
||||
public const PUBLIC_TO = [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
'Public',
|
||||
'as:Public',
|
||||
];
|
||||
@ -86,6 +87,28 @@ class ActivityPub extends Plugin
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function onStartGetActorUri(Actor $actor, int $type, ?string &$uri):bool
|
||||
{
|
||||
if (
|
||||
// Is remote?
|
||||
!$actor->getIsLocal()
|
||||
// Is in ActivityPub?
|
||||
&& !is_null($ap_actor = ActivitypubActor::getWithPK(['actor_id' => $actor->getId()]))
|
||||
// We can only provide a full URL (anything else wouldn't make sense)
|
||||
&& $type === Router::ABSOLUTE_URL
|
||||
) {
|
||||
$uri = $ap_actor->getUri();
|
||||
return Event::stop;
|
||||
}
|
||||
|
||||
return Event::next;
|
||||
}
|
||||
|
||||
public function onStartGetActorUrl(Actor $actor, int $type, ?string &$url):bool
|
||||
{
|
||||
return $this->onStartGetActorUri($actor, $type, $url);
|
||||
}
|
||||
|
||||
public static function getActorByUri(string $resource, ?bool $attempt_fetch = true): Actor
|
||||
{
|
||||
// Try local
|
||||
|
@ -60,8 +60,9 @@ class Inbox extends Controller
|
||||
// TODO: Check if Actor has authority over payload
|
||||
|
||||
// Store Activity
|
||||
dd(AS2ToEntity::store(activity: $type->toArray(), source: 'ActivityPub'));
|
||||
$ap_act = AS2ToEntity::store(activity: $type->toArray(), source: 'ActivityPub');
|
||||
DB::flush();
|
||||
dd($ap_act, $act = $ap_act->getActivity(), $act->getActor(), $act->getObject());
|
||||
|
||||
return new TypeResponse($type, status: 202);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace Plugin\ActivityPub\Entity;
|
||||
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use App\Entity\Activity;
|
||||
use DateTimeInterface;
|
||||
|
||||
/**
|
||||
@ -42,17 +43,24 @@ class ActivitypubActivity extends Entity
|
||||
{
|
||||
// {{{ Autocode
|
||||
// @codeCoverageIgnoreStart
|
||||
private int $activity_id;
|
||||
private string $activity_uri;
|
||||
private int $actor_id;
|
||||
private string $verb;
|
||||
private string $object_type;
|
||||
private int $object_id;
|
||||
private string $object_uri;
|
||||
private bool $is_local;
|
||||
private ?string $source;
|
||||
private DateTimeInterface $created;
|
||||
private DateTimeInterface $modified;
|
||||
|
||||
public function setActivityId(int $activity_id): self
|
||||
{
|
||||
$this->activity_id = $activity_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActivityId(): int
|
||||
{
|
||||
return $this->activity_id;
|
||||
}
|
||||
|
||||
public function getActivityUri(): string
|
||||
{
|
||||
return $this->activity_uri;
|
||||
@ -64,50 +72,6 @@ class ActivitypubActivity extends Entity
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setActorId(int $actor_id): self
|
||||
{
|
||||
$this->actor_id = $actor_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getActorId(): int
|
||||
{
|
||||
return $this->actor_id;
|
||||
}
|
||||
|
||||
public function setVerb(string $verb): self
|
||||
{
|
||||
$this->verb = $verb;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVerb(): string
|
||||
{
|
||||
return $this->verb;
|
||||
}
|
||||
|
||||
public function setObjectType(string $object_type): self
|
||||
{
|
||||
$this->object_type = $object_type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObjectType(): string
|
||||
{
|
||||
return $this->object_type;
|
||||
}
|
||||
|
||||
public function setObjectId(int $object_id): self
|
||||
{
|
||||
$this->object_id = $object_id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getObjectId(): int
|
||||
{
|
||||
return $this->object_id;
|
||||
}
|
||||
|
||||
public function getObjectUri(): string
|
||||
{
|
||||
return $this->object_uri;
|
||||
@ -130,17 +94,6 @@ class ActivitypubActivity extends Entity
|
||||
return $this->is_local;
|
||||
}
|
||||
|
||||
public function setSource(?string $source): self
|
||||
{
|
||||
$this->source = $source;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSource(): ?string
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
public function setCreated(DateTimeInterface $created): self
|
||||
{
|
||||
$this->created = $created;
|
||||
@ -166,19 +119,20 @@ class ActivitypubActivity extends Entity
|
||||
// @codeCoverageIgnoreEnd
|
||||
// }}} Autocode
|
||||
|
||||
public function getActivity(): Activity
|
||||
{
|
||||
return DB::findOneBy('activity', ['id' => $this->getActivityId()]);
|
||||
}
|
||||
|
||||
public static function schemaDef(): array
|
||||
{
|
||||
return [
|
||||
'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_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Activity\'s URI'],
|
||||
'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'who made the note'],
|
||||
'verb' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'internal activity verb, influenced by activity pub verbs'],
|
||||
'object_type' => ['type' => 'varchar', 'length' => 32, 'description' => 'the name of the table this object refers to'],
|
||||
'object_id' => ['type' => 'int', 'description' => 'id in the referenced table'],
|
||||
'object_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Object\'s URI'],
|
||||
'is_local' => ['type' => 'bool', 'not null' => true, 'description' => 'whether this was a locally generated or an imported activity'],
|
||||
'source' => ['type' => 'varchar', 'length' => 32, 'description' => 'the source of this activity'],
|
||||
'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'],
|
||||
],
|
||||
|
@ -238,7 +238,7 @@ class Explorer
|
||||
'fullname' => $res['name'] ?? null,
|
||||
'created' => new DateTime($res['published'] ?? 'now'),
|
||||
'bio' => isset($res['summary']) ? mb_substr(Security::sanitize($res['summary']), 0, 1000) : null,
|
||||
'homepage' => $res['url'] ?? $res['id'],
|
||||
'homepage' => $res['url'],
|
||||
'is_local' => false,
|
||||
'modified' => new DateTime(),
|
||||
];
|
||||
|
@ -6,6 +6,7 @@ namespace Plugin\ActivityPub\Util\Model\AS2ToEntity;
|
||||
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Event;
|
||||
use App\Entity\Activity;
|
||||
use App\Entity\Actor;
|
||||
use App\Entity\Note;
|
||||
use App\Util\Exception\ClientException;
|
||||
@ -35,58 +36,47 @@ abstract class AS2ToEntity
|
||||
/**
|
||||
* @throws ClientException
|
||||
*/
|
||||
public static function store(array $activity, ?string $source = null): array
|
||||
public static function store(array $activity, ?string $source = null): ActivitypubActivity
|
||||
{
|
||||
$act = ActivitypubActivity::getWithPK(['activity_uri' => $activity['id']]);
|
||||
if (\is_null($act)) {
|
||||
$ap_act = ActivitypubActivity::getWithPK(['activity_uri' => $activity['id']]);
|
||||
if (\is_null($ap_act)) {
|
||||
$actor = ActivityPub::getActorByUri($activity['actor']);
|
||||
$map = [
|
||||
'activity_uri' => $activity['id'],
|
||||
// Store Object
|
||||
$obj = null;
|
||||
switch ($activity['object']['type']) {
|
||||
case 'Note':
|
||||
$obj = AS2ToNote::translate($activity['object'], $source, $activity['actor'], $actor->getId());
|
||||
break;
|
||||
default:
|
||||
if (!Event::handle('ActivityPubObject', [$activity['object']['type'], $activity['object'], &$obj])) {
|
||||
throw new ClientException('Unsupported Object type.');
|
||||
}
|
||||
break;
|
||||
}
|
||||
DB::persist($obj);
|
||||
// Store Activity
|
||||
$act = Activity::create([
|
||||
'actor_id' => $actor->getId(),
|
||||
'verb' => self::activity_stream_two_verb_to_gs_verb($activity['type']),
|
||||
'object_type' => self::activity_stream_two_object_type_to_gs_table($activity['object']['type']),
|
||||
'object_id' => $obj->getId(),
|
||||
'is_local' => false,
|
||||
'created' => new DateTime($activity['published'] ?? 'now'),
|
||||
'source' => $source,
|
||||
]);
|
||||
DB::persist($act);
|
||||
// Store ActivityPub Activity
|
||||
$ap_act = ActivitypubActivity::create([
|
||||
'activity_id' => $act->getId(),
|
||||
'activity_uri' => $activity['id'],
|
||||
'object_uri' => $activity['object']['id'],
|
||||
'is_local' => false,
|
||||
'created' => new DateTime($activity['published'] ?? 'now'),
|
||||
'modified' => new DateTime(),
|
||||
'source' => $source,
|
||||
];
|
||||
|
||||
$act = new ActivitypubActivity();
|
||||
foreach ($map as $prop => $val) {
|
||||
$set = Formatting::snakeCaseToCamelCase("set_{$prop}");
|
||||
$act->{$set}($val);
|
||||
]);
|
||||
DB::persist($ap_act);
|
||||
}
|
||||
|
||||
$obj = null;
|
||||
switch ($activity['object']['type']) {
|
||||
case 'Note':
|
||||
$obj = AS2ToNote::translate($activity['object'], $source, $activity['actor'], $act);
|
||||
break;
|
||||
default:
|
||||
if (!Event::handle('ActivityPubObject', [$activity['object']['type'], $activity['object'], &$obj])) {
|
||||
throw new ClientException('Unsupported Object type.');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
DB::persist($obj);
|
||||
$act->setObjectId($obj->getId());
|
||||
DB::persist($act);
|
||||
} else {
|
||||
$actor = Actor::getById($act->getActorId());
|
||||
switch ($activity['object']['type']) {
|
||||
case 'Note':
|
||||
$obj = Note::getWithPK(['id' => $act->getObjectId()]);
|
||||
break;
|
||||
default:
|
||||
if (!Event::handle('ActivityPubObject', [$activity['object']['type'], $activity['object'], &$obj])) {
|
||||
throw new ClientException('Unsupported Object type.');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [$actor, $act, $obj];
|
||||
return $ap_act;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ namespace Plugin\ActivityPub\Util\Model\AS2ToEntity;
|
||||
|
||||
use App\Core\Event;
|
||||
use App\Entity\Actor;
|
||||
use App\Entity\Language;
|
||||
use App\Entity\Note;
|
||||
use App\Util\Formatting;
|
||||
use DateTime;
|
||||
@ -18,11 +19,9 @@ abstract class AS2ToNote
|
||||
/**
|
||||
*@throws Exception
|
||||
*/
|
||||
public static function translate(array $object, ?string $source, ?string $actor_uri, ?ActivitypubActivity $act = null): Note
|
||||
public static function translate(array $object, ?string $source, ?string $actor_uri = null, ?int $actor_id = null): Note
|
||||
{
|
||||
if (isset($actor_uri) && $actor_uri === $object['attributedTo']) {
|
||||
$actor_id = $act->getActorId();
|
||||
} else {
|
||||
if (is_null($actor_uri) || $actor_uri !== $object['attributedTo']) {
|
||||
$actor_id = ActivityPub::getActorByUri($object['attributedTo'])->getId();
|
||||
}
|
||||
$map = [
|
||||
@ -30,22 +29,32 @@ abstract class AS2ToNote
|
||||
'created' => new DateTime($object['published'] ?? 'now'),
|
||||
'content' => $object['content'] ?? null,
|
||||
'content_type' => 'text/html',
|
||||
'language_id' => $object['contentLang'] ?? null,
|
||||
'url' => \array_key_exists('url', $object) ? $object['url'] : $object['id'],
|
||||
'actor_id' => $actor_id,
|
||||
'modified' => new DateTime(),
|
||||
'source' => $source,
|
||||
];
|
||||
if ($map['content'] !== null) {
|
||||
$mentions = [];
|
||||
Event::handle('RenderNoteContent', [
|
||||
$map['content'],
|
||||
$map['content_type'],
|
||||
&$map['rendered'],
|
||||
Actor::getById($actor_id),
|
||||
null, // TODO reply to
|
||||
$map['language_id'],
|
||||
&$mentions,
|
||||
]);
|
||||
}
|
||||
|
||||
$obj = new Note();
|
||||
|
||||
if (!is_null($map['language_id'])) {
|
||||
$map['language_id'] = Language::getFromLocale($map['language_id'])->getId();
|
||||
} else {
|
||||
$map['language_id'] = null;
|
||||
}
|
||||
|
||||
foreach ($map as $prop => $val) {
|
||||
$set = Formatting::snakeCaseToCamelCase("set_{$prop}");
|
||||
$obj->{$set}($val);
|
||||
|
@ -25,6 +25,8 @@ namespace App\Controller;
|
||||
|
||||
use App\Core\Controller;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Router\Router;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use function App\Core\I18n\_m;
|
||||
use App\Util\Exception\ClientException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@ -37,6 +39,9 @@ class Actor extends Controller
|
||||
private function ActorById(int $id, callable $handle)
|
||||
{
|
||||
$actor = DB::findOneBy('actor', ['id' => $id]);
|
||||
if ($actor->getIsLocal()) {
|
||||
return new RedirectResponse(Router::url('actor_view_nickname', ['nickname' => $actor->getNickname()]));
|
||||
}
|
||||
if (empty($actor)) {
|
||||
throw new ClientException(_m('No such actor.'), 404);
|
||||
} else {
|
||||
|
@ -26,6 +26,7 @@ namespace App\Entity;
|
||||
use App\Core\Cache;
|
||||
use App\Core\DB\DB;
|
||||
use App\Core\Entity;
|
||||
use App\Core\Event;
|
||||
use App\Core\Router\Router;
|
||||
use App\Core\UserRoles;
|
||||
use App\Util\Common;
|
||||
@ -432,14 +433,28 @@ class Actor extends Entity
|
||||
);
|
||||
}
|
||||
|
||||
public function getUri(int $type = Router::ABSOLUTE_PATH): string
|
||||
public function getUri(int $type = Router::ABSOLUTE_URL): string
|
||||
{
|
||||
return Router::url('actor_view_id', ['id' => $this->getId()], $type);
|
||||
$uri = null;
|
||||
if (Event::handle('StartGetActorUri', [$this, $type, &$uri]) === Event::next) {
|
||||
if ($this->getIsLocal()) {
|
||||
$uri = Router::url('actor_view_id', ['id' => $this->getId()], $type);
|
||||
}
|
||||
Event::handle('EndGetActorUri', [$this, $type, &$uri]);
|
||||
}
|
||||
return $uri;
|
||||
}
|
||||
|
||||
public function getUrl(int $type = Router::ABSOLUTE_PATH): string
|
||||
public function getUrl(int $type = Router::ABSOLUTE_URL): string
|
||||
{
|
||||
return Router::url('actor_view_nickname', ['nickname' => $this->getNickname()], $type);
|
||||
$url = null;
|
||||
if (Event::handle('StartGetActorUrl', [$this, $type, &$url]) === Event::next) {
|
||||
if ($this->getIsLocal()) {
|
||||
$url = Router::url('actor_view_nickname', ['nickname' => $this->getNickname()], $type);
|
||||
}
|
||||
Event::handle('EndGetActorUrl', [$this, $type, &$url]);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function getAliases(): array
|
||||
|
@ -62,7 +62,6 @@ class LocalUser extends Entity implements UserInterface, PasswordAuthenticatedUs
|
||||
private ?PhoneNumber $phone_number;
|
||||
private ?int $sms_carrier;
|
||||
private ?string $sms_email;
|
||||
private ?string $uri;
|
||||
private ?bool $auto_subscribe_back;
|
||||
private ?int $subscription_policy;
|
||||
private ?bool $is_stream_private;
|
||||
@ -179,17 +178,6 @@ class LocalUser extends Entity implements UserInterface, PasswordAuthenticatedUs
|
||||
return $this->sms_email;
|
||||
}
|
||||
|
||||
public function setUri(?string $uri): self
|
||||
{
|
||||
$this->uri = $uri;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUri(): ?string
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
public function setAutoSubscribeBack(?bool $auto_subscribe_back): self
|
||||
{
|
||||
$this->auto_subscribe_back = $auto_subscribe_back;
|
||||
@ -399,7 +387,6 @@ class LocalUser extends Entity implements UserInterface, PasswordAuthenticatedUs
|
||||
'phone_number' => ['type' => 'phone_number', 'description' => 'phone number'],
|
||||
'sms_carrier' => ['type' => 'int', 'foreign key' => true, 'target' => 'SmsCarrier.id', 'multiplicity' => 'one to one', 'description' => 'foreign key to sms_carrier'],
|
||||
'sms_email' => ['type' => 'varchar', 'length' => 191, 'description' => 'built from sms and carrier (see sms_carrier)'],
|
||||
'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universally unique identifier, usually a tag URI'],
|
||||
'auto_subscribe_back' => ['type' => 'bool', 'default' => false, 'description' => 'automatically subscribe to users who subscribed us'],
|
||||
'subscription_policy' => ['type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => '0 = anybody can subscribe; 1 = require approval'],
|
||||
'is_stream_private' => ['type' => 'bool', 'default' => false, 'description' => 'whether to limit all notices to subscribers only'],
|
||||
@ -412,7 +399,6 @@ class LocalUser extends Entity implements UserInterface, PasswordAuthenticatedUs
|
||||
'user_outgoing_email_key' => ['outgoing_email'],
|
||||
'user_incoming_email_key' => ['incoming_email'],
|
||||
'user_phone_number_key' => ['phone_number'],
|
||||
'user_uri_key' => ['uri'],
|
||||
],
|
||||
'indexes' => [
|
||||
'user_nickname_idx' => ['nickname'],
|
||||
|
@ -55,7 +55,7 @@ class Note extends Entity
|
||||
private ?string $source;
|
||||
private int $scope = VisibilityScope::PUBLIC;
|
||||
private string $url;
|
||||
private int $language_id;
|
||||
private ?int $language_id = null;
|
||||
private DateTimeInterface $created;
|
||||
private DateTimeInterface $modified;
|
||||
|
||||
@ -166,7 +166,7 @@ class Note extends Entity
|
||||
return $this->language_id;
|
||||
}
|
||||
|
||||
public function setLanguageId(int $language_id): self
|
||||
public function setLanguageId(?int $language_id): self
|
||||
{
|
||||
$this->language_id = $language_id;
|
||||
return $this;
|
||||
|
@ -237,7 +237,7 @@ abstract class Formatting
|
||||
// Split \n\n into paragraphs, process each paragrah and merge
|
||||
return implode("\n", F\map(explode("\n\n", $text), function (string $paragraph) use ($language) {
|
||||
$paragraph = nl2br($paragraph, use_xhtml: false);
|
||||
Event::handle('RenderContent', [&$paragraph, $language]);
|
||||
Event::handle('onRenderPlainTextNoteContent', [&$paragraph, $language]);
|
||||
|
||||
return HTML::html(['p' => [$paragraph]], options: ['raw' => true, 'indent' => false]);
|
||||
}));
|
||||
|
Loading…
Reference in New Issue
Block a user