Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ MAILER_DSN=null://default
KBIN_JS_ENABLED=false
KBIN_DEFAULT_LANG=en
KBIN_DOMAIN=kbin.test
KBIN_STORAGE_URL=https://kbin.test/media
ELASTICSEARCH_ENABLED=false
KBIN_API_ITEMS_PER_PAGE=2
KBIN_FEDERATION_ENABLED=true
Expand Down
32 changes: 32 additions & 0 deletions migrations/Version20260113103210.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20260113103210 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add ap_indexable to ActivityPub actors';
}

public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE magazine ADD ap_indexable BOOLEAN DEFAULT NULL');
$this->addSql('ALTER TABLE "user" ADD ap_indexable BOOLEAN DEFAULT NULL');
// The column should be nullable so that we know whether other software simply does not set this value,
// but for local users and magazines we should only have true and false as options
$this->addSql('UPDATE "user" SET ap_indexable = true WHERE ap_id IS NULL');
$this->addSql('UPDATE magazine SET ap_indexable = true WHERE ap_id IS NULL');
}

public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE "user" DROP ap_indexable');
$this->addSql('ALTER TABLE magazine DROP ap_indexable');
}
}
1 change: 1 addition & 0 deletions src/DTO/MagazineDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class MagazineDto
public int $postCommentCount = 0;
public bool $isAdult = false;
public bool $isPostingRestrictedToMods = false;
public ?bool $indexable = null;
public ?bool $isUserSubscribed = null;
public ?bool $isBlockedByUser = null;
public ?int $localSubscribers = null;
Expand Down
2 changes: 2 additions & 0 deletions src/DTO/MagazineRequestDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class MagazineRequestDto
public ?bool $isAdult = null;
public ?bool $isPostingRestrictedToMods = null;
public ?bool $discoverable = null;
public ?bool $indexable = null;

public function mergeIntoDto(MagazineDto $dto): MagazineDto
{
Expand All @@ -26,6 +27,7 @@ public function mergeIntoDto(MagazineDto $dto): MagazineDto
$dto->isAdult = null !== $this->isAdult ? $this->isAdult : $dto->isAdult;
$dto->isPostingRestrictedToMods = $this->isPostingRestrictedToMods ?? false;
$dto->discoverable = $this->discoverable ?? $dto->discoverable ?? true;
$dto->indexable = $this->indexable ?? $dto->indexable ?? true;

return $dto;
}
Expand Down
4 changes: 4 additions & 0 deletions src/DTO/MagazineResponseDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class MagazineResponseDto implements \JsonSerializable
public ?int $localSubscribers = null;
public ?ENotificationStatus $notificationStatus = null;
public ?bool $discoverable = null;
public ?bool $indexable = null;

public static function create(
?ModeratorResponseDto $owner = null,
Expand Down Expand Up @@ -69,6 +70,7 @@ public static function create(
bool $isPostingRestrictedToMods = false,
?int $localSubscribers = null,
?bool $discoverable = null,
?bool $indexable = null,
): self {
$dto = new MagazineResponseDto();
$dto->owner = $owner;
Expand Down Expand Up @@ -97,6 +99,7 @@ public static function create(
$dto->isPostingRestrictedToMods = $isPostingRestrictedToMods;
$dto->localSubscribers = $localSubscribers;
$dto->discoverable = $discoverable;
$dto->indexable = $indexable;

return $dto;
}
Expand Down Expand Up @@ -131,6 +134,7 @@ public function jsonSerialize(): mixed
'localSubscribers' => $this->localSubscribers,
'notificationStatus' => $this->notificationStatus,
'discoverable' => $this->discoverable,
'indexable' => $this->indexable,
];
}
}
3 changes: 3 additions & 0 deletions src/DTO/MagazineSmallResponseDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class MagazineSmallResponseDto implements \JsonSerializable
public ?string $apId = null;
public ?string $apProfileId = null;
public ?bool $discoverable = null;
public ?bool $indexable = null;

public function __construct(MagazineDto $dto)
{
Expand All @@ -30,6 +31,7 @@ public function __construct(MagazineDto $dto)
$this->apId = $dto->apId;
$this->apProfileId = $dto->apProfileId;
$this->discoverable = $dto->discoverable;
$this->indexable = $dto->indexable;
}

public function jsonSerialize(): mixed
Expand All @@ -44,6 +46,7 @@ public function jsonSerialize(): mixed
'apId' => $this->apId,
'apProfileId' => $this->apProfileId,
'discoverable' => $this->discoverable,
'indexable' => $this->indexable,
];
}
}
2 changes: 2 additions & 0 deletions src/DTO/MagazineUpdateRequestDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class MagazineUpdateRequestDto
public ?bool $isAdult = null;
public ?bool $isPostingRestrictedToMods = null;
public ?bool $discoverable = null;
public ?bool $indexable = null;

public function mergeIntoDto(MagazineDto $dto, ImageRepository $imageRepository): MagazineDto
{
Expand All @@ -27,6 +28,7 @@ public function mergeIntoDto(MagazineDto $dto, ImageRepository $imageRepository)
$dto->isAdult = null === $this->isAdult ? $this->isAdult : $dto->isAdult;
$dto->isPostingRestrictedToMods = $this->isPostingRestrictedToMods ?? false;
$dto->discoverable = $this->discoverable ?? $dto->discoverable ?? true;
$dto->indexable = $this->indexable ?? $dto->indexable ?? true;

return $dto;
}
Expand Down
3 changes: 3 additions & 0 deletions src/DTO/UserDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class UserDto implements UserDtoInterface
public ?string $applicationText = null;
public ?int $reputationPoints = null;
public ?bool $discoverable = null;
public ?bool $indexable = null;

#[Assert\Callback]
public function validate(
Expand Down Expand Up @@ -97,6 +98,7 @@ public static function create(
?string $applicationText = null,
?int $reputationPoints = null,
?bool $discoverable = null,
?bool $indexable = null,
): self {
$dto = new UserDto();
$dto->id = $id;
Expand All @@ -116,6 +118,7 @@ public static function create(
$dto->applicationText = $applicationText;
$dto->reputationPoints = $reputationPoints;
$dto->discoverable = $discoverable;
$dto->indexable = $indexable;

return $dto;
}
Expand Down
3 changes: 3 additions & 0 deletions src/DTO/UserResponseDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class UserResponseDto implements \JsonSerializable
public ?string $serverSoftware = null;
public ?string $serverSoftwareVersion = null;
public ?ENotificationStatus $notificationStatus = null;
public ?bool $indexable = null;

/**
* @var int|null this will only be populated on single user retrieves, not on batch ones,
Expand Down Expand Up @@ -57,6 +58,7 @@ public function __construct(UserDto $dto)
$this->isGlobalModerator = $dto->isGlobalModerator;
$this->reputationPoints = $dto->reputationPoints;
$this->discoverable = $dto->discoverable;
$this->indexable = $dto->indexable;
}

public function jsonSerialize(): mixed
Expand All @@ -82,6 +84,7 @@ public function jsonSerialize(): mixed
'notificationStatus' => $this->notificationStatus,
'reputationPoints' => $this->reputationPoints,
'discoverable' => $this->discoverable,
'indexable' => $this->indexable,
];
}
}
3 changes: 3 additions & 0 deletions src/DTO/UserSettingsDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public function __construct(
#[OA\Property(type: 'string', enum: EFrontContentOptions::OPTIONS)]
public ?string $frontDefaultContent = null,
public ?bool $discoverable = null,
public ?bool $indexable = null,
) {
}

Expand Down Expand Up @@ -72,6 +73,7 @@ public function jsonSerialize(): mixed
'notifyOnUserSignup' => $this->notifyOnUserSignup,
'directMessageSetting' => $this->directMessageSetting,
'discoverable' => $this->discoverable,
'indexable' => $this->indexable,
];
}

Expand All @@ -98,6 +100,7 @@ public function mergeIntoDto(UserSettingsDto $dto): UserSettingsDto
$dto->directMessageSetting = $this->directMessageSetting ?? $dto->directMessageSetting;
$dto->frontDefaultContent = $this->frontDefaultContent ?? $dto->frontDefaultContent;
$dto->discoverable = $this->discoverable ?? $dto->discoverable;
$dto->indexable = $this->indexable ?? $dto->indexable;

return $dto;
}
Expand Down
3 changes: 3 additions & 0 deletions src/DTO/UserSmallResponseDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class UserSmallResponseDto implements \JsonSerializable
public ?string $apProfileId = null;
public ?\DateTimeImmutable $createdAt = null;
public ?bool $discoverable = null;
public ?bool $indexable = null;

public function __construct(UserDto $dto)
{
Expand All @@ -38,6 +39,7 @@ public function __construct(UserDto $dto)
$this->isAdmin = $dto->isAdmin;
$this->isGlobalModerator = $dto->isGlobalModerator;
$this->discoverable = $dto->discoverable;
$this->indexable = $dto->indexable;
}

public function jsonSerialize(): mixed
Expand All @@ -56,6 +58,7 @@ public function jsonSerialize(): mixed
'apProfileId' => $this->apProfileId,
'createdAt' => $this->createdAt?->format(\DateTimeImmutable::ATOM),
'discoverable' => $this->discoverable,
'indexable' => $this->indexable,
];
}
}
3 changes: 3 additions & 0 deletions src/Entity/Traits/ActivityPubActorTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ trait ActivityPubActorTrait
#[Column(type: 'boolean', nullable: true)]
public ?bool $apDiscoverable = null;

#[Column(type: 'boolean', nullable: true)]
public ?bool $apIndexable = null;

#[Column(type: 'boolean', nullable: true)]
public ?bool $apManuallyApprovesFollowers = null;

Expand Down
1 change: 1 addition & 0 deletions src/Factory/ActivityPub/GroupFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public function create(Magazine $magazine, bool $includeContext = true): array
),
'postingRestrictedToMods' => $magazine->postingRestrictedToMods,
'discoverable' => $magazine->apDiscoverable,
'indexable' => $magazine->apIndexable,
'endpoints' => [
'sharedInbox' => $this->urlGenerator->generate(
'ap_shared_inbox',
Expand Down
1 change: 1 addition & 0 deletions src/Factory/ActivityPub/PersonFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function create(User $user, bool $context = true): array
'url' => $this->getActivityPubId($user),
'manuallyApprovesFollowers' => false,
'discoverable' => $user->apDiscoverable,
'indexable' => $user->apIndexable,
'published' => $user->createdAt->format(DATE_ATOM),
'following' => $this->urlGenerator->generate(
'ap_user_following',
Expand Down
2 changes: 2 additions & 0 deletions src/Factory/MagazineFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public function createDto(Magazine $magazine): MagazineDto
$dto->isAdult = $magazine->isAdult;
$dto->isPostingRestrictedToMods = $magazine->postingRestrictedToMods;
$dto->discoverable = $magazine->apDiscoverable;
$dto->indexable = $magazine->apIndexable;
$dto->tags = $magazine->tags;
$dto->badges = $magazine->badges;
$dto->moderators = $magazine->moderators;
Expand Down Expand Up @@ -178,6 +179,7 @@ public function createResponseDto(MagazineDto|Magazine $magazine): MagazineRespo
$dto->isPostingRestrictedToMods,
$dto->localSubscribers,
$dto->discoverable,
$dto->indexable,
);
}

Expand Down
1 change: 1 addition & 0 deletions src/Factory/UserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function createDto(User $user, ?int $reputationPoints = null): UserDto
$currentUser && ($currentUser->isAdmin() || $currentUser->isModerator()) ? $user->applicationText : null,
reputationPoints: $reputationPoints,
discoverable: $user->apDiscoverable,
indexable: $user->apIndexable,
);

// Only return the user's vote if permission to control voting has been given
Expand Down
4 changes: 4 additions & 0 deletions src/Form/MagazineType.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'required' => false,
'help' => 'magazine_discoverable_help',
])
->add('indexable', CheckboxType::class, [
'required' => false,
'help' => 'magazine_indexable_by_search_engines_help',
])
->add('submit', SubmitType::class);

$builder->addEventSubscriber(new DisableFieldsOnMagazineEdit());
Expand Down
4 changes: 4 additions & 0 deletions src/Form/UserSettingsType.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'required' => false,
'help' => 'user_discoverable_help',
])
->add('indexable', CheckboxType::class, [
'required' => false,
'help' => 'user_indexable_by_search_engines_help',
])
->add('featuredMagazines', TextareaType::class, ['required' => false])
->add('preferredLanguages', LanguageType::class, [
'required' => false,
Expand Down
2 changes: 2 additions & 0 deletions src/Service/ActivityPubManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ private function updateUser(string $actorUrl): ?User
$user->apAttributedToUrl = $actor['attributedTo'] ?? null;
$user->apPreferredUsername = $actor['preferredUsername'] ?? null;
$user->apDiscoverable = $actor['discoverable'] ?? null;
$user->apIndexable = $actor['indexable'] ?? null;
$user->apManuallyApprovesFollowers = $actor['manuallyApprovesFollowers'] ?? false;
$user->apPublicUrl = $actor['url'] ?? $actorUrl;
$user->apDeletedAt = null;
Expand Down Expand Up @@ -656,6 +657,7 @@ private function updateMagazine(string $actorUrl): ?Magazine
$magazine->apFetchedAt = new \DateTime();
$magazine->isAdult = $actor['sensitive'] ?? false;
$magazine->postingRestrictedToMods = filter_var($actor['postingRestrictedToMods'] ?? false, FILTER_VALIDATE_BOOLEAN) ?? false;
$magazine->apIndexable = $actor['indexable'] ?? null;

if (null !== $magazine->apFollowersUrl) {
try {
Expand Down
5 changes: 5 additions & 0 deletions src/Service/MagazineManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public function create(MagazineDto $dto, ?User $user, bool $rateLimit = true): M
);
// default new local magazines to be discoverable
$magazine->apDiscoverable = $dto->discoverable ?? true;
// default new local magazines to be indexable
$magazine->apIndexable = $dto->indexable ?? true;
}

$this->entityManager->persist($magazine);
Expand Down Expand Up @@ -149,6 +151,9 @@ public function edit(Magazine $magazine, MagazineDto $dto, User $editedBy): Maga
if (null !== $dto->discoverable) {
$magazine->apDiscoverable = $dto->discoverable;
}
if (null !== $dto->indexable) {
$magazine->apIndexable = $dto->indexable;
}

$this->entityManager->flush();

Expand Down
2 changes: 2 additions & 0 deletions src/Service/UserManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ public function create(UserDto $dto, bool $verifyUserEmail = true, $rateLimit =
$user = KeysGenerator::generate($user);
// default new local users to be discoverable
$user->apDiscoverable = $dto->discoverable ?? true;
// default new local users to be indexable
$user->apIndexable = true;
}

$this->entityManager->persist($user);
Expand Down
5 changes: 5 additions & 0 deletions src/Service/UserSettingsManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public function createDto(User $user): UserSettingsDto
$user->directMessageSetting,
$user->frontDefaultContent,
$user->apDiscoverable,
$user->apIndexable,
);
}

Expand Down Expand Up @@ -73,6 +74,10 @@ public function update(User $user, UserSettingsDto $dto): void
$user->apDiscoverable = $dto->discoverable;
}

if (null !== $dto->indexable) {
$user->apIndexable = $dto->indexable;
}

$this->entityManager->flush();
}
}
4 changes: 3 additions & 1 deletion templates/admin/federation.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
{{ form_label(form.federationUsesAllowList, 'federation_uses_allowlist') }}
{{ form_widget(form.federationUsesAllowList) }}
</div>
{{ form_help(form.federationUsesAllowList) }}
<div class="checkbox help-text">
{{ form_help(form.federationUsesAllowList) }}
</div>
<div class="actions">
{{ form_row(form.submit, { 'label': 'save'|trans, attr: {class: 'btn btn__primary'} }) }}
</div>
Expand Down
10 changes: 10 additions & 0 deletions templates/base.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@
<meta property="og:description" content="{{ block('description')|trim }}">
<meta property="og:image" content="{% block image %}{{ absolute_url(asset('mbin-og.png')) }}{% endblock %}">

{% if magazine is defined and magazine and magazine.apIndexable is same as false %}
<meta name="robots" content="noindex">
{% elseif user is defined and user and user.apIndexable is same as false %}
<meta name="robots" content="noindex">
{% elseif entry is defined and entry and entry.user and entry.user.apIndexable is same as false %}
<meta name="robots" content="noindex">
{% elseif post is defined and post and post.user and post.user.apIndexable is same as false %}
<meta name="robots" content="noindex">
{% endif %}

<link rel="icon" href="{{ asset('favicon.ico') }}" sizes="any">
<link rel="apple-touch-icon" href="{{ asset('assets/icons/apple-touch-icon.png') }}">
<link rel="icon" href="{{ asset('favicon.svg') }}" type="image/svg+xml">
Expand Down
Loading
Loading