mirror of
https://github.com/karatektus/pl_mail.git
synced 2026-10-25 01:54:10 +00:00
chore(dead-code): drop public methods nothing calls
Grep across src, templates, config, tests and assets (including the
getter/property forms Twig and forms resolve) found no caller for:
Account add/removeMailbox, add/removeMessageThread, isGmailWatchActive;
MailboxRepository find{Trash,Archive,Draft}MailboxForAccount,
getIdsOfActiveInboxMailboxesForUser; MessageThreadRepository
countForLabels; MessageRepository existsWithFromAddressInThread,
iterateForRecategorization; LabelRepository findVisibleTreeForUser;
ClientErrorRepository findOneByFingerprint; ImapFolderProvisioner
ensurePath; MailPresetProvider findByEmail; UserEntityModel plainPassword
(set/get and the property: every form field is mapped: false);
OnboardingStep requiresAdmin; MailProvider usesImap; ShareWindow
usesDates; ShareDetail jsCalendarProperty; HealthReport worstSeverity;
PushDeliveryOutcome reachedTheDevice; PushTransport addressedByUrl;
CategorySource helpKey; AiPrompts customisedCount; ResetReport
skippedTables; TwoFactorEnrolment qrContent; MicrosoftOAuthErrorTranslator
isAdminConsentRequired; IntegrationsGlobal forDownload.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4b885ec4b7
commit
87bc044557
22 files changed
-353
No files matched your search
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\DTO\Health;
|
||||
|
||||
use App\Domain\Enum\Health\HealthSeverity;
|
||||
|
||||
/**
|
||||
* Everything wrong with one user's accounts, at one moment.
|
||||
@@ -39,20 +38,6 @@ final readonly class HealthReport
|
||||
return [] === $this->issues;
|
||||
}
|
||||
|
||||
/** The worst severity present, or null when there is nothing wrong. */
|
||||
public function worstSeverity(): ?HealthSeverity
|
||||
{
|
||||
$worst = null;
|
||||
|
||||
foreach ($this->issues as $issue) {
|
||||
if (null === $worst || $issue->severity->rank() < $worst->rank()) {
|
||||
$worst = $issue->severity;
|
||||
}
|
||||
}
|
||||
|
||||
return $worst;
|
||||
}
|
||||
|
||||
/**
|
||||
* What the topbar counts.
|
||||
*
|
||||
|
||||
@@ -317,17 +317,6 @@ enum MailProvider: string
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this provider is synced over IMAP at all.
|
||||
*
|
||||
* Both current providers are API-synced; the method exists so the IMAP
|
||||
* syncer can ask the question instead of hard-coding provider checks.
|
||||
*/
|
||||
public function usesImap(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function imapHost(): ?string
|
||||
{
|
||||
return match ($this) {
|
||||
|
||||
@@ -34,25 +34,6 @@ enum ShareDetail: string
|
||||
case Description = 'description';
|
||||
case Participants = 'participants';
|
||||
|
||||
/**
|
||||
* The JSCalendar property this case unlocks (RFC 8984 §4.2).
|
||||
*
|
||||
* Exhaustive with no default, so a fifth detail cannot be added without
|
||||
* somebody deciding where in the canonical object it comes from — the one
|
||||
* question that has to be answered before it can be revealed at all.
|
||||
* Locations and participants are maps in JSCalendar rather than scalars;
|
||||
* the reader flattens them, and the name here is the key it reaches for.
|
||||
*/
|
||||
public function jsCalendarProperty(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Title => 'title',
|
||||
self::Location => 'locations',
|
||||
self::Description => 'description',
|
||||
self::Participants => 'participants',
|
||||
};
|
||||
}
|
||||
|
||||
/** Translation key for the checkbox label. */
|
||||
public function transKey(): string
|
||||
{
|
||||
|
||||
@@ -29,22 +29,6 @@ enum ShareWindow: string
|
||||
/** Two dates the owner named, unchanging. */
|
||||
case Fixed = 'fixed';
|
||||
|
||||
/**
|
||||
* Whether the link's start and end dates are the ones that matter.
|
||||
*
|
||||
* Exhaustive with no default: a third shape — "this month", "the next
|
||||
* quarter" — would need the form, the validation and the reader to agree
|
||||
* about which columns it reads, and a fallthrough here would make it agree
|
||||
* silently with whichever branch came last.
|
||||
*/
|
||||
public function usesDates(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
self::Rolling => false,
|
||||
self::Fixed => true,
|
||||
};
|
||||
}
|
||||
|
||||
/** Translation key for the radio label. */
|
||||
public function transKey(): string
|
||||
{
|
||||
|
||||
@@ -59,12 +59,6 @@ enum CategorySource: string
|
||||
return 'settings.sorting.source.' . $this->value;
|
||||
}
|
||||
|
||||
/** Translation key for the line under it. */
|
||||
public function helpKey(): string
|
||||
{
|
||||
return 'settings.sorting.source.' . $this->value . '_help';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whatever was stored or posted, read charitably.
|
||||
*
|
||||
|
||||
@@ -60,16 +60,4 @@ enum OnboardingStep: string
|
||||
self::Integrations => 'fa-solid fa-cloud',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the step configures the whole install rather than one person's
|
||||
* mailbox. Only ROLE_ADMIN is offered these.
|
||||
*/
|
||||
public function requiresAdmin(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
self::AdminMailCredentials, self::AdminIntegrationCredentials, self::AdminAi => true,
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -40,19 +40,6 @@ enum PushDeliveryOutcome: string
|
||||
/** Nothing was sent: the transport is unconfigured, or the row cannot address it. */
|
||||
case Skipped = 'skipped';
|
||||
|
||||
/**
|
||||
* Whether this outcome means the device was reached.
|
||||
*
|
||||
* Stays a predicate rather than a `self::Accepted === $x` at each call
|
||||
* site, because two of the three failures are not the caller's business
|
||||
* to enumerate — a reader wants "did it arrive", and adding a fifth case
|
||||
* would otherwise have to be found in every comparison.
|
||||
*/
|
||||
public function reachedTheDevice(): bool
|
||||
{
|
||||
return self::Accepted === $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The semantic colour this outcome is drawn in, named rather than spelled
|
||||
* as classes: the admin table and the settings pane show the same four
|
||||
|
||||
@@ -33,20 +33,4 @@ enum PushTransport: string
|
||||
|
||||
/** FCM HTTP v1 — Google owns the device token. */
|
||||
case Fcm = 'fcm';
|
||||
|
||||
/**
|
||||
* Whether a subscription of this kind is addressed by a URL this server
|
||||
* POSTs to, as opposed to a token handed to somebody else's gateway.
|
||||
*
|
||||
* Stays a method rather than a `url !== null` test on the entity: it is a
|
||||
* property of the transport, and asking the row would answer "no" for a
|
||||
* Web Push subscription that is merely half-built.
|
||||
*/
|
||||
public function addressedByUrl(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
self::WebPush => true,
|
||||
self::Fcm => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,6 @@ class UserEntityModel
|
||||
User::ROLE_ADMIN,
|
||||
];
|
||||
|
||||
private ?string $plainPassword = null;
|
||||
|
||||
public function addRole(string $role): User
|
||||
{
|
||||
if ($this instanceof User) {
|
||||
@@ -45,11 +43,6 @@ class UserEntityModel
|
||||
throw new LogicException();
|
||||
}
|
||||
|
||||
public function getPlainPassword(): ?string
|
||||
{
|
||||
return $this->plainPassword;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
if (true === $this instanceof User) {
|
||||
@@ -59,13 +52,6 @@ class UserEntityModel
|
||||
throw new LogicException('Not a User');
|
||||
}
|
||||
|
||||
public function setPlainPassword(?string $plainPassword): self
|
||||
{
|
||||
$this->plainPassword = $plainPassword;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isDeleted(): ?bool
|
||||
{
|
||||
if (true === $this instanceof User) {
|
||||
|
||||
@@ -142,20 +142,6 @@ final class AiPrompts
|
||||
};
|
||||
}
|
||||
|
||||
/** Whether anything at all has been overridden. For the chip on the card. */
|
||||
public function customisedCount(): int
|
||||
{
|
||||
$count = 0;
|
||||
|
||||
foreach (PromptSlot::cases() as $slot) {
|
||||
if (null !== $this->of($slot)) {
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* One prompt, as it is worth storing: trimmed, capped, and null when empty.
|
||||
*
|
||||
|
||||
@@ -414,50 +414,6 @@ class Account extends AccountModel
|
||||
$this->updatedAt = new DateTimeImmutable();
|
||||
}
|
||||
|
||||
public function addMailbox(Mailbox $mailbox): static
|
||||
{
|
||||
if (!$this->mailboxes->contains($mailbox)) {
|
||||
$this->mailboxes->add($mailbox);
|
||||
$mailbox->account = $this;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeMailbox(Mailbox $mailbox): static
|
||||
{
|
||||
if ($this->mailboxes->removeElement($mailbox)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($mailbox->account === $this) {
|
||||
$mailbox->account = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addMessageThread(MessageThread $messageThread): static
|
||||
{
|
||||
if (!$this->messageThreads->contains($messageThread)) {
|
||||
$this->messageThreads->add($messageThread);
|
||||
$messageThread->account = $this;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeMessageThread(MessageThread $messageThread): static
|
||||
{
|
||||
if ($this->messageThreads->removeElement($messageThread)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($messageThread->account === $this) {
|
||||
$messageThread->account = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Which calendar events extracted from this account's mail land on.
|
||||
*
|
||||
@@ -710,22 +666,6 @@ class Account extends AccountModel
|
||||
return true === $this->isGmail() || true === $this->isMicrosoft();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stays a method rather than becoming a property: this reads a timestamp
|
||||
* and answers a question about it against the clock, which is an
|
||||
* interpretation, not the mapped boolean column that $isActive is.
|
||||
*/
|
||||
public function isGmailWatchActive(): bool
|
||||
{
|
||||
$expiry = $this->gmailWatchExpiry;
|
||||
|
||||
if (null === $expiry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $expiry > new DateTimeImmutable();
|
||||
}
|
||||
|
||||
public function addAlias(EmailAlias $alias): static
|
||||
{
|
||||
if (false === $this->aliases->contains($alias)) {
|
||||
|
||||
@@ -164,16 +164,6 @@ class LabelRepository extends ServiceEntityRepository
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Visible custom labels of a user in sidebar tree order.
|
||||
*
|
||||
* @return Label[]
|
||||
*/
|
||||
public function findVisibleTreeForUser(UserInterface $user): array
|
||||
{
|
||||
return $this->treeOrder($this->findVisibleForUser($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a sidebar path ("Work/Invoices") to the user's custom Label at
|
||||
* that path. Candidates are narrowed by leaf name in SQL, the full parent
|
||||
|
||||
@@ -7,7 +7,6 @@ use App\Entity\Mail\Account;
|
||||
use App\Entity\Mail\Mailbox;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Mailbox>
|
||||
@@ -32,26 +31,11 @@ class MailboxRepository extends ServiceEntityRepository
|
||||
return $indexed;
|
||||
}
|
||||
|
||||
public function findTrashMailboxForAccount(Account $account): ?Mailbox
|
||||
{
|
||||
return $this->findOneBy(['account' => $account, 'specialUse' => MailboxSpecialUse::TRASH]);
|
||||
}
|
||||
|
||||
public function findArchiveMailboxForAccount(Account $account): ?Mailbox
|
||||
{
|
||||
return $this->findOneBy(['account' => $account, 'specialUse' => MailboxSpecialUse::ARCHIVE]);
|
||||
}
|
||||
|
||||
public function findSentMailboxForAccount(Account $account): ?Mailbox
|
||||
{
|
||||
return $this->findOneBy(['account' => $account, 'specialUse' => MailboxSpecialUse::SENT]);
|
||||
}
|
||||
|
||||
public function findDraftMailboxForAccount(Account $account): ?Mailbox
|
||||
{
|
||||
return $this->findOneBy(['account' => $account, 'specialUse' => MailboxSpecialUse::DRAFTS]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The oldest full-listing this account's folders have between them, or null
|
||||
* if any of them has never had one.
|
||||
@@ -130,25 +114,6 @@ class MailboxRepository extends ServiceEntityRepository
|
||||
return $queryBuilder->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* QueryBuilder for the join to Account and for the id-only projection —
|
||||
* the caller compares sets of ids and has no use for a Mailbox.
|
||||
*/
|
||||
public function getIdsOfActiveInboxMailboxesForUser(UserInterface $user): array
|
||||
{
|
||||
return $this->createQueryBuilder('mailbox')
|
||||
->select('mailbox.id')
|
||||
->leftJoin('mailbox.account', 'account')
|
||||
->where('account.isActive = :isActive')
|
||||
->andWhere('account.usr = :usr')
|
||||
->andWhere('mailbox.specialUse = :inbox')
|
||||
->setParameter('isActive', true)
|
||||
->setParameter('usr', $user)
|
||||
->setParameter('inbox', MailboxSpecialUse::INBOX)
|
||||
->getQuery()
|
||||
->getSingleColumnResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a folder's deletion sweep due right now, without lying about what
|
||||
* has been listed.
|
||||
|
||||
@@ -1696,11 +1696,6 @@ class MessageRepository extends ServiceEntityRepository
|
||||
->getOneOrNullResult();
|
||||
}
|
||||
|
||||
public function existsWithFromAddressInThread(string $fromAddress, MessageThread $thread): bool
|
||||
{
|
||||
return $this->existsWithAnyFromAddressInThread([$fromAddress], $thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the thread contain a message sent by any of these addresses?
|
||||
*
|
||||
@@ -2104,25 +2099,6 @@ class MessageRepository extends ServiceEntityRepository
|
||||
return array_values(array_map(static fn ($id): string => (string) $id, $rows));
|
||||
}
|
||||
|
||||
/**
|
||||
* Streamed, so recategorising a large account does not load it into memory
|
||||
* — the reason this cannot be findBy().
|
||||
*
|
||||
* @return iterable<Message>
|
||||
*/
|
||||
public function iterateForRecategorization(Account $account, bool $includeCategorized): iterable
|
||||
{
|
||||
$qb = $this->createQueryBuilder('m')
|
||||
->where('m.account = :account')
|
||||
->setParameter('account', $account);
|
||||
|
||||
if (false === $includeCategorized) {
|
||||
$qb->andWhere('m.category IS NULL');
|
||||
}
|
||||
|
||||
return $qb->getQuery()->toIterable();
|
||||
}
|
||||
|
||||
/**
|
||||
* QueryBuilder for the join to the owning user and for the single-column
|
||||
* projection, as with findSyncedGmailIdsForUser().
|
||||
|
||||
@@ -1123,22 +1123,6 @@ class MessageThreadRepository extends ServiceEntityRepository
|
||||
->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Same to-many membership test as findForLabels().
|
||||
*
|
||||
* @param Label[] $labels
|
||||
*/
|
||||
public function countForLabels(array $labels): int
|
||||
{
|
||||
return (int) $this->createQueryBuilder('thread')
|
||||
->select('COUNT(DISTINCT thread.id)')
|
||||
->innerJoin('thread.labels', 'label')
|
||||
->where('label IN (:labels)')
|
||||
->setParameter('labels', $labels)
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the labels collection of every given thread in ONE query so
|
||||
* the list view's label chips don't lazy-load per row. Fetch-joining onto
|
||||
|
||||
@@ -40,11 +40,6 @@ final class ClientErrorRepository extends ServiceEntityRepository
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public function findOneByFingerprint(string $fingerprint): ?ClientError
|
||||
{
|
||||
return $this->findOneBy(['fingerprint' => $fingerprint]);
|
||||
}
|
||||
|
||||
public function countAll(): int
|
||||
{
|
||||
return (int) $this->createQueryBuilder('e')
|
||||
|
||||
@@ -130,34 +130,6 @@ readonly class ImapFolderProvisioner
|
||||
return $this->create($account, $client, $path, $known?->specialUse, record: null === $known);
|
||||
}
|
||||
|
||||
/**
|
||||
* The full path for an arbitrary folder name — a custom label being pushed
|
||||
* — creating it if need be.
|
||||
*
|
||||
* The name is a plMail label's full name, so its own "/" separators become
|
||||
* the server's, which is what makes a nested label arrive as a nested
|
||||
* folder rather than one with a slash in its name.
|
||||
*/
|
||||
public function ensurePath(Account $account, Client $client, string $labelFullName): ?string
|
||||
{
|
||||
$separator = $this->separatorFor($account);
|
||||
$segments = array_values(array_filter(explode('/', $labelFullName), static fn (string $s): bool => '' !== $s));
|
||||
|
||||
if (0 === count($segments)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = $this->pathFor($account, implode($separator, $segments));
|
||||
|
||||
$existing = $this->mailboxes->findOneBy(['account' => $account, 'fullPath' => $path]);
|
||||
|
||||
if (null !== $existing) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
return $this->create($account, $client, $path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue the CREATE, subscribe, and record the folder locally.
|
||||
*
|
||||
|
||||
@@ -59,17 +59,6 @@ final class MailPresetProvider
|
||||
return $this->all()[$key] ?? null;
|
||||
}
|
||||
|
||||
public function findByEmail(string $email): ?MailPreset
|
||||
{
|
||||
$at = strrpos($email, '@');
|
||||
|
||||
if (false === $at) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->findByDomain(substr($email, $at + 1));
|
||||
}
|
||||
|
||||
public function findByDomain(string $domain): ?MailPreset
|
||||
{
|
||||
foreach ($this->all() as $preset) {
|
||||
|
||||
@@ -43,12 +43,4 @@ final readonly class ResetReport
|
||||
{
|
||||
return array_keys(array_filter($this->tables));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function skippedTables(): array
|
||||
{
|
||||
return array_keys(array_filter($this->tables, static fn (bool $truncated): bool => false === $truncated));
|
||||
}
|
||||
}
|
||||
@@ -65,11 +65,6 @@ final readonly class MicrosoftOAuthErrorTranslator
|
||||
];
|
||||
}
|
||||
|
||||
public function isAdminConsentRequired(?string $rawError): bool
|
||||
{
|
||||
return true === $this->translate($rawError)['adminActionable'];
|
||||
}
|
||||
|
||||
private function extractCode(?string $rawError): ?string
|
||||
{
|
||||
if (null === $rawError || '' === $rawError) {
|
||||
|
||||
@@ -61,11 +61,6 @@ final readonly class TwoFactorEnrolment
|
||||
return $this->totpAuthenticator->getQRContent($user);
|
||||
}
|
||||
|
||||
public function qrContent(User $user): string
|
||||
{
|
||||
return $this->totpAuthenticator->getQRContent($user);
|
||||
}
|
||||
|
||||
public function verifyCode(User $user, string $code): bool
|
||||
{
|
||||
if (null === $user->totpSecret) {
|
||||
|
||||
@@ -73,16 +73,6 @@ class IntegrationsGlobal implements ResetInterface
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connections a file can be pulled out of.
|
||||
*
|
||||
* @return list<Integration>
|
||||
*/
|
||||
public function forDownload(): array
|
||||
{
|
||||
return $this->supporting(Capability::Download);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Integration>
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user