[CORE][ENTITY] Properly port ProfileTag, ProfileTagSubscription and ProfileList as ActorTag, ActorTagSubscription and ActorCircle

This commit is contained in:
Diogo Peralta Cordeiro 2021-11-27 04:11:35 +00:00
parent 11d2cfb9ed
commit 51994406da
Signed by: diogo
GPG Key ID: 18D2D35001FBFAB0
7 changed files with 206 additions and 84 deletions

View File

@ -42,11 +42,10 @@ use App\Core\Controller;
use App\Core\DB\DB; use App\Core\DB\DB;
use App\Core\Event; use App\Core\Event;
use App\Core\Form; use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Log; use App\Core\Log;
use App\Entity\ActorCircle;
use App\Entity\ActorLanguage; use App\Entity\ActorLanguage;
use App\Entity\Language; use App\Entity\Language;
use App\Entity\UserNotificationPrefs;
use App\Util\Common; use App\Util\Common;
use App\Util\Exception\AuthenticationException; use App\Util\Exception\AuthenticationException;
use App\Util\Exception\RedirectException; use App\Util\Exception\RedirectException;
@ -55,6 +54,7 @@ use App\Util\Form\ActorArrayTransformer;
use App\Util\Form\ArrayTransformer; use App\Util\Form\ArrayTransformer;
use App\Util\Form\FormFields; use App\Util\Form\FormFields;
use App\Util\Formatting; use App\Util\Formatting;
use Component\Notification\Entity\UserNotificationPrefs;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
use Exception; use Exception;
use Functional as F; use Functional as F;
@ -66,6 +66,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\SubmitButton; use Symfony\Component\Form\SubmitButton;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use function App\Core\I18n\_m;
// }}} Imports // }}} Imports
@ -110,9 +111,6 @@ class UserPanel extends Controller
]; ];
$extra_step = function ($data, $extra_args) use ($user, $actor) { $extra_step = function ($data, $extra_args) use ($user, $actor) {
$user->setNickname($data['nickname']); $user->setNickname($data['nickname']);
if (!$data['full_name'] && !$actor->getFullname()) {
$actor->setFullname($data['nickname']);
}
}; };
return Form::handle($form_definition, $request, $actor, $extra, $extra_step, [['self_tags' => $extra['self_tags']]]); return Form::handle($form_definition, $request, $actor, $extra, $extra_step, [['self_tags' => $extra['self_tags']]]);
} }

View File

@ -199,11 +199,11 @@ class GSFile
* Throw a client exception if the cache key $id doesn't contain * Throw a client exception if the cache key $id doesn't contain
* exactly one entry * exactly one entry
*/ */
public static function error($except, $id, array $res) public static function error($exception, $id, array $res)
{ {
switch (\count($res)) { switch (\count($res)) {
case 0: case 0:
throw new $except(); throw new $exception();
case 1: case 1:
return $res[0]; return $res[0];
default: default:

View File

@ -1,6 +1,6 @@
<?php <?php
declare(strict_types = 1); declare(strict_types=1);
// {{{ License // {{{ License
@ -35,6 +35,8 @@ use App\Util\Nickname;
use Component\Avatar\Avatar; use Component\Avatar\Avatar;
use DateTimeInterface; use DateTimeInterface;
use Functional as F; use Functional as F;
use function in_array;
use function is_null;
/** /**
* Entity for actors * Entity for actors
@ -57,7 +59,7 @@ class Actor extends Entity
private int $id; private int $id;
private string $nickname; private string $nickname;
private ?string $fullname = null; private ?string $fullname = null;
private int $roles = 4; private int $roles = 4;
private ?string $homepage; private ?string $homepage;
private ?string $bio; private ?string $bio;
private ?string $location; private ?string $location;
@ -91,7 +93,7 @@ class Actor extends Entity
return $this->nickname; return $this->nickname;
} }
public function setFullname(string $fullname): self public function setFullname(?string $fullname): self
{ {
$this->fullname = $fullname; $this->fullname = $fullname;
return $this; return $this;
@ -99,7 +101,7 @@ class Actor extends Entity
public function getFullname(): ?string public function getFullname(): ?string
{ {
if (\is_null($this->fullname)) { if (is_null($this->fullname)) {
return null; return null;
} }
return $this->fullname; return $this->fullname;
@ -245,43 +247,123 @@ class Actor extends Entity
public static function getById(int $id): ?self public static function getById(int $id): ?self
{ {
return Cache::get('actor-id-' . $id, fn () => DB::find('actor', ['id' => $id])); return Cache::get('actor-id-' . $id, fn() => DB::find('actor', ['id' => $id]));
} }
public static function getNicknameById(int $id): string public static function getNicknameById(int $id): string
{ {
return Cache::get('actor-nickname-id-' . $id, fn () => self::getById($id)->getNickname()); return Cache::get('actor-nickname-id-' . $id, fn() => self::getById($id)->getNickname());
} }
public static function getFullnameById(int $id): ?string public static function getFullnameById(int $id): ?string
{ {
return Cache::get('actor-fullname-id-' . $id, fn () => self::getById($id)->getFullname()); return Cache::get('actor-fullname-id-' . $id, fn() => self::getById($id)->getFullname());
} }
/**
* Tags attributed to self
*
* @return [ActorCircle]
*/
public function getSelfTags(bool $_test_force_recompute = false): array public function getSelfTags(bool $_test_force_recompute = false): array
{ {
return Cache::get( return $this->getOtherTags(scoped: $this->getId(), _test_force_recompute: $_test_force_recompute);
'selftags-' . $this->id,
fn () => DB::findBy('actor_tag', ['tagger' => $this->id, 'tagged' => $this->id]),
beta: $_test_force_recompute ? \INF : 1.0,
);
} }
public function setSelfTags(array $tags, array $existing): void /**
* Get tags that other people put on this actor, in reverse-chron order
*
* @param Actor|int|null $scoped Actor we are requesting as:
* - If null = All tags attributed to self by other actors (excludes self tags)
* - If self = Same as getSelfTags
* - otherwise = Tags that $scoped attributed to $this
* @param int|null $offset Offset from latest
* @param int|null $limit Max number to get
* @param bool $_test_force_recompute
* @return [ActorCircle] resulting lists
*/
public function getOtherTags(Actor|int|null $scoped = null, ?int $offset = null, ?int $limit = null, bool $_test_force_recompute = false): array
{ {
$tag_existing = F\map($existing, fn ($pt) => $pt->getTag()); if (is_null($scoped)) {
$tag_to_add = array_diff($tags, $tag_existing); return Cache::get(
$tag_to_remove = array_diff($tag_existing, $tags); "othertags-{$this->getId()}",
$pt_to_remove = F\filter($existing, fn ($pt) => \in_array($pt->getTag(), $tag_to_remove)); fn() => DB::dql(
foreach ($tag_to_add as $tag) { <<< EOQ
$pt = ActorTag::create(['tagger' => $this->id, 'tagged' => $this->id, 'tag' => $tag]); SELECT circle
DB::persist($pt); FROM App\Entity\ActorTag tag
JOIN App\Entity\ActorCircle circle
WITH
tag.tagger = circle.tagger
AND tag.tag = circle.tag
WHERE tag.tagged = :id
ORDER BY tag.modified DESC, tag.tagged DESC
EOQ,
['id' => $this->getId()],
['offset' => $offset,
'limit' => $limit])
);
} else {
$scoped_id = is_int($scoped) ? $scoped : $scoped->getId();
return Cache::get(
"othertags-{$this->getId()}-by-{$scoped_id}",
fn() => DB::dql(
<<< EOQ
SELECT circle
FROM App\Entity\ActorTag tag
JOIN App\Entity\ActorCircle circle
WITH
tag.tagger = circle.tagger
AND tag.tag = circle.tag
WHERE
tag.tagged = :id
AND ( circle.private != true
OR ( circle.tagger = :scoped
AND circle.private = true
)
)
ORDER BY tag.modified DESC, tag.tagged DESC
EOQ,
['id' => $this->getId(),
'scoped' => $scoped_id],
['offset' => $offset,
'limit' => $limit]
)
);
} }
foreach ($pt_to_remove as $pt) { }
DB::persist($pt);
DB::remove($pt); /**
* @param array $tags array of strings to become self tags
* @param array|null $existing array of existing self tags (actor_circle[])
* @return $this
* @throws NotFoundException
* @throws \App\Util\Exception\DuplicateFoundException
*/
public function setSelfTags(array $tags, ?array $existing = null): self
{
if (is_null($existing)) {
$existing = $this->getSelfTags();
} }
Cache::delete('selftags-' . $this->id); $existing_actor_circles = F\map($existing, fn($actor_circle) => $actor_circle->getTag());
$tags_to_add = array_diff($tags, $existing_actor_circles);
$tags_to_remove = array_diff($existing_actor_circles, $tags);
$actor_circles_to_remove = F\filter($existing, fn($actor_circle) => in_array($actor_circle->getTag(), $tags_to_remove));
foreach ($tags_to_add as $tag) {
$actor_circle = ActorCircle::create(['tagger' => $this->getId(), 'tag' => $tag, 'private' => false]);
$actor_tag = ActorTag::create(['tagger' => $this->id, 'tagged' => $this->id, 'tag' => $tag]);
DB::persist($actor_circle);
DB::persist($actor_tag);
}
foreach ($actor_circles_to_remove as $actor_circle) {
$actor_tag = DB::findOneBy('actor_tag', ['tagger' => $this->getId(), 'tagged' => $this->getId(), 'tag' => $actor_circle->getTag()]);
DB::persist($actor_tag);
DB::remove($actor_tag);
// TODO: use DB::removeBy when implemented
DB::remove(DB::getReference('actor_circle', ['id' => $actor_circle->getId()]));
}
Cache::delete("selftags-{$this->getId()}");
Cache::delete("othertags-{$this->getId()}-by-{$this->getId()}");
return $this;
} }
public function getSubscribersCount() public function getSubscribersCount()
@ -290,9 +372,9 @@ class Actor extends Entity
'subscribers-' . $this->id, 'subscribers-' . $this->id,
function () { function () {
return DB::dql( return DB::dql(
'select count(f) from App\Entity\Subscription f where f.subscribed = :subscribed', 'select count(f) from App\Entity\Subscription f where f.subscribed = :subscribed',
['subscribed' => $this->id], ['subscribed' => $this->id],
)[0][1] - 1; // Remove self subscription )[0][1] - 1; // Remove self subscription
}, },
); );
} }
@ -303,9 +385,9 @@ class Actor extends Entity
'subscribed-' . $this->id, 'subscribed-' . $this->id,
function () { function () {
return DB::dql( return DB::dql(
'select count(f) from App\Entity\Subscription f where f.subscriber = :subscriber', 'select count(f) from App\Entity\Subscription f where f.subscriber = :subscriber',
['subscriber' => $this->id], ['subscriber' => $this->id],
)[0][1] - 1; // Remove self subscription )[0][1] - 1; // Remove self subscription
}, },
); );
} }
@ -331,16 +413,16 @@ class Actor extends Entity
$nickname = Nickname::normalize($nickname, check_already_used: false); $nickname = Nickname::normalize($nickname, check_already_used: false);
return Cache::get( return Cache::get(
'relative-nickname-' . $nickname . '-' . $this->getId(), 'relative-nickname-' . $nickname . '-' . $this->getId(),
fn () => DB::dql( fn() => DB::dql(
<<<'EOF' <<<'EOF'
select a from actor a where select a from actor a where
a.id in (select fa.subscribed from subscription fa join actor aa with fa.subscribed = aa.id where fa.subscriber = :actor_id and aa.nickname = :nickname) or a.id in (select fa.subscribed from subscription fa join actor aa with fa.subscribed = aa.id where fa.subscriber = :actor_id and aa.nickname = :nickname) or
a.id in (select fb.subscriber from subscription fb join actor ab with fb.subscriber = ab.id where fb.subscribed = :actor_id and ab.nickname = :nickname) or a.id in (select fb.subscriber from subscription fb join actor ab with fb.subscriber = ab.id where fb.subscribed = :actor_id and ab.nickname = :nickname) or
a.nickname = :nickname a.nickname = :nickname
EOF, EOF,
['nickname' => $nickname, 'actor_id' => $this->getId()], ['nickname' => $nickname, 'actor_id' => $this->getId()],
['limit' => 1], ['limit' => 1],
)[0] ?? null, )[0] ?? null,
); );
} }
@ -377,43 +459,43 @@ class Actor extends Entity
*/ */
public function getPreferredLanguageChoices(?self $context = null): array public function getPreferredLanguageChoices(?self $context = null): array
{ {
$id = $context?->getId() ?? $this->getId(); $id = $context?->getId() ?? $this->getId();
$key = ActorLanguage::collectionCacheKey($this, $context); $key = ActorLanguage::collectionCacheKey($this, $context);
$langs = Cache::getList( $langs = Cache::getList(
$key, $key,
fn () => DB::dql( fn() => DB::dql(
'select l from actor_language al join language l with al.language_id = l.id where al.actor_id = :id order by al.ordering ASC', 'select l from actor_language al join language l with al.language_id = l.id where al.actor_id = :id order by al.ordering ASC',
['id' => $id], ['id' => $id],
), ),
) ?: [ ) ?: [
Language::getFromLocale(Common::config('site', 'language')), Language::getFromLocale(Common::config('site', 'language')),
]; ];
return array_merge(...F\map($langs, fn ($l) => $l->toChoiceFormat())); return array_merge(...F\map($langs, fn($l) => $l->toChoiceFormat()));
} }
public static function schemaDef(): array public static function schemaDef(): array
{ {
return [ return [
'name' => 'actor', 'name' => 'actor',
'description' => 'local and remote users, groups and bots are actors, for instance', 'description' => 'local and remote users, groups and bots are actors, for instance',
'fields' => [ 'fields' => [
'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'], 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
'nickname' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username'], 'nickname' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'nickname or username'],
'fullname' => ['type' => 'text', 'description' => 'display name'], 'fullname' => ['type' => 'text', 'description' => 'display name'],
'roles' => ['type' => 'int', 'not null' => true, 'default' => UserRoles::USER, 'description' => 'Bitmap of permissions this actor has'], 'roles' => ['type' => 'int', 'not null' => true, 'default' => UserRoles::USER, 'description' => 'Bitmap of permissions this actor has'],
'homepage' => ['type' => 'text', 'description' => 'identifying URL'], 'homepage' => ['type' => 'text', 'description' => 'identifying URL'],
'bio' => ['type' => 'text', 'description' => 'descriptive biography'], 'bio' => ['type' => 'text', 'description' => 'descriptive biography'],
'location' => ['type' => 'text', 'description' => 'physical location'], 'location' => ['type' => 'text', 'description' => 'physical location'],
'lat' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'], 'lat' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'latitude'],
'lon' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'], 'lon' => ['type' => 'numeric', 'precision' => 10, 'scale' => 7, 'description' => 'longitude'],
'location_id' => ['type' => 'int', 'description' => 'location id if possible'], 'location_id' => ['type' => 'int', 'description' => 'location id if possible'],
'location_service' => ['type' => 'int', 'description' => 'service used to obtain location id'], 'location_service' => ['type' => 'int', 'description' => 'service used to obtain location id'],
'is_local' => ['type' => 'bool', 'not null' => true, 'description' => 'Does this actor have a LocalUser associated'], 'is_local' => ['type' => 'bool', 'not null' => true, 'description' => 'Does this actor have a LocalUser associated'],
'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'],
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
], ],
'primary key' => ['id'], 'primary key' => ['id'],
'indexes' => [ 'indexes' => [
'actor_nickname_idx' => ['nickname'], 'actor_nickname_idx' => ['nickname'],
], ],
'fulltext indexes' => [ 'fulltext indexes' => [

View File

@ -21,6 +21,8 @@ declare(strict_types = 1);
namespace App\Entity; namespace App\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\Entity; use App\Core\Entity;
use DateTimeInterface; use DateTimeInterface;
@ -35,6 +37,7 @@ use DateTimeInterface;
* @author Mikael Nordfeldth <mmn@hethane.se> * @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @author Hugo Sales <hugo@hsal.es> * @author Hugo Sales <hugo@hsal.es>
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/ */
@ -42,6 +45,7 @@ class ActorCircle extends Entity
{ {
// {{{ Autocode // {{{ Autocode
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
private int $id;
private int $tagger; private int $tagger;
private string $tag; private string $tag;
private ?string $description; private ?string $description;
@ -49,6 +53,17 @@ class ActorCircle extends Entity
private DateTimeInterface $created; private DateTimeInterface $created;
private DateTimeInterface $modified; private DateTimeInterface $modified;
public function setId(int $id): ActorCircle
{
$this->id = $id;
return $this;
}
public function getId(): int
{
return $this->id;
}
public function setTagger(int $tagger): self public function setTagger(int $tagger): self
{ {
$this->tagger = $tagger; $this->tagger = $tagger;
@ -118,12 +133,32 @@ class ActorCircle extends Entity
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
// }}} Autocode // }}} Autocode
public function getSubscribedActors(?int $offset = null, ?int $limit = null): array
{
return Cache::get(
"circle-{$this->getId()}",
fn() => DB::dql(
<<< EOQ
SELECT actor
FROM App\Entity\Actor actor
JOIN App\Entity\ActorCircleSubscription subscription
WITH actor.id = subscription.actor_id
ORDER BY subscription.created DESC, actor.id DESC
EOQ,
options:
['offset' => $offset,
'limit' => $limit]
)
);
}
public static function schemaDef(): array public static function schemaDef(): array
{ {
return [ return [
'name' => 'actor_circle', 'name' => 'actor_circle',
'description' => 'a actor can have lists of actors, to separate their feed', 'description' => 'a actor can have lists of actors, to separate their feed',
'fields' => [ 'fields' => [
'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
'tagger' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'many to one', 'name' => 'actor_list_tagger_fkey', 'not null' => true, 'description' => 'user making the tag'], 'tagger' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'many to one', 'name' => 'actor_list_tagger_fkey', 'not null' => true, 'description' => 'user making the tag'],
'tag' => ['type' => 'varchar', 'length' => 64, 'foreign key' => true, 'target' => 'ActorTag.tag', 'multiplicity' => 'many to one', 'not null' => true, 'description' => 'actor tag'], // Join with ActorTag // // so, Doctrine doesn't like that the target is not unique, even though the pair is 'tag' => ['type' => 'varchar', 'length' => 64, 'foreign key' => true, 'target' => 'ActorTag.tag', 'multiplicity' => 'many to one', 'not null' => true, 'description' => 'actor tag'], // Join with ActorTag // // so, Doctrine doesn't like that the target is not unique, even though the pair is
'description' => ['type' => 'text', 'description' => 'description of the people tag'], 'description' => ['type' => 'text', 'description' => 'description of the people tag'],
@ -131,7 +166,7 @@ class ActorCircle extends Entity
'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'],
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
], ],
'primary key' => ['tagger', 'tag'], 'primary key' => ['id'],
'indexes' => [ 'indexes' => [
'actor_list_modified_idx' => ['modified'], 'actor_list_modified_idx' => ['modified'],
'actor_list_tag_idx' => ['tag'], 'actor_list_tag_idx' => ['tag'],
@ -139,4 +174,9 @@ class ActorCircle extends Entity
], ],
]; ];
} }
public function __toString()
{
return $this->getTag();
}
} }

View File

@ -23,7 +23,7 @@ use App\Core\Entity;
use DateTimeInterface; use DateTimeInterface;
/** /**
* Entity for actor Tag Subscription * Entity for actor circle subscriptions
* *
* @category DB * @category DB
* @package GNUsocial * @package GNUsocial
@ -33,17 +33,18 @@ use DateTimeInterface;
* @author Mikael Nordfeldth <mmn@hethane.se> * @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @author Hugo Sales <hugo@hsal.es> * @author Hugo Sales <hugo@hsal.es>
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/ */
class ActorTagSubscription extends Entity class ActorCircleSubscription extends Entity
{ {
// {{{ Autocode // {{{ Autocode
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
private int $actor_id; private int $actor_id;
private int $actor_tag; private int $circle_id;
private \DateTimeInterface $created; private DateTimeInterface $created;
private \DateTimeInterface $modified; private DateTimeInterface $modified;
public function setActorId(int $actor_id): self public function setActorId(int $actor_id): self
{ {
@ -56,15 +57,15 @@ class ActorTagSubscription extends Entity
return $this->actor_id; return $this->actor_id;
} }
public function setActorTag(int $actor_tag): self public function setCircleid(int $circle_id): self
{ {
$this->actor_tag = $actor_tag; $this->circle_id = $circle_id;
return $this; return $this;
} }
public function getActorTag(): int public function getCircleid(): int
{ {
return $this->actor_tag; return $this->circle_id;
} }
public function setCreated(DateTimeInterface $created): self public function setCreated(DateTimeInterface $created): self
@ -95,18 +96,17 @@ class ActorTagSubscription extends Entity
public static function schemaDef(): array public static function schemaDef(): array
{ {
return [ return [
'name' => 'actor_tag_subscription', 'name' => 'actor_tag_subscription',
'fields' => [ 'fields' => [
'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'actor_tag_subscription_actor_id_fkey', 'not null' => true, 'description' => 'foreign key to actor table'], 'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'actor_circle_subscription_actor_id_fkey', 'not null' => true, 'description' => 'foreign key to actor table'],
'actor_tag' => ['type' => 'int', // 'foreign key' => true, 'target' => 'ActorTag.tag', 'multiplicity' => 'one to one', // tag can't unique, but doctrine doesn't understand this 'circle_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'ActorCircle.id', 'multiplicity' => 'one to one', 'name' => 'actor_circle_subscription_actor_circle_fkey', 'not null' => true, 'description' => 'foreign key to actor_circle'],
'name' => 'actor_tag_subscription_actor_tag_fkey', 'not null' => true, 'description' => 'foreign key to actor_tag', ], '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'], 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
], ],
'primary key' => ['actor_tag_id', 'actor_id'], 'primary key' => ['circle_id', 'actor_id'],
'indexes' => [ 'indexes' => [
'actor_tag_subscription_actor_id_idx' => ['actor_id'], 'actor_circle_subscription_actor_id_idx' => ['actor_id'],
'actor_tag_subscription_created_idx' => ['created'], 'actor_circle_subscription_created_idx' => ['created'],
], ],
]; ];
} }

View File

@ -19,6 +19,8 @@
namespace App\Entity; namespace App\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\Entity; use App\Core\Entity;
use DateTimeInterface; use DateTimeInterface;
@ -97,7 +99,7 @@ class ActorTag extends Entity
return [ return [
'name' => 'actor_tag', 'name' => 'actor_tag',
'fields' => [ 'fields' => [
'tagger' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'actor_tag_tagger_fkey', 'not null' => true, 'description' => 'user making the tag'], 'tagger' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'actor_tag_tagger_fkey', 'not null' => true, 'description' => 'actor making the tag'],
'tagged' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'actor_tag_tagged_fkey', 'not null' => true, 'description' => 'actor tagged'], 'tagged' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'actor_tag_tagged_fkey', 'not null' => true, 'description' => 'actor tagged'],
'tag' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this notice'], 'tag' => ['type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this notice'],
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],

View File

@ -29,7 +29,7 @@
<nav class="profile-info-tags"> <nav class="profile-info-tags">
{% if actor_tags %} {% if actor_tags %}
{% for tag in actor_tags %} {% for tag in actor_tags %}
<a href='#'><em>#{{ tag }}</em></a> <a href='#'><em>#{{ tag.getTag() }}</em></a>
{% endfor %} {% endfor %}
{% else %} {% else %}
{{ '(No tags)' | trans }} {{ '(No tags)' | trans }}