app/Entity/Schema/ORM/User.php line 17

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Sq\Entity\Schema\ORM;
  3. use Carbon\Carbon;
  4. use Doctrine\Common\Collections\ArrayCollection;
  5. use Doctrine\Common\Collections\Collection;
  6. use Doctrine\ORM\Mapping as ORM;
  7. use Sq\GraphQL\Exception\SqGraphQLException;
  8. use Sq\Service\Repository\ORM\UserRepository;
  9. use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
  10. use Symfony\Component\Security\Core\User\UserInterface;
  11. #[ORM\Entity(repositoryClassUserRepository::class)]
  12. #[ORM\Table(name'users')]
  13. #[ORM\UniqueConstraint(name'u_email'columns: ['u_email'])]
  14. class User implements UserInterfacePasswordAuthenticatedUserInterface
  15. {
  16.     public const ROLE_USER 'user';
  17.     public const ROLE_ADMIN 'admin';
  18.     public const ROLE_MODERATOR 'moderator';
  19.     public const VALID_ROLES = [
  20.         self::ROLE_USER,
  21.         self::ROLE_MODERATOR,
  22.         self::ROLE_ADMIN,
  23.     ];
  24.     /**
  25.      *
  26.      * @var int|null
  27.      */
  28.     #[ORM\Id]
  29.     #[ORM\GeneratedValue(strategy'AUTO')]
  30.     #[ORM\Column(name'u_id'type'integer'nullablefalseoptions: ['unsigned' => true])]
  31.     private $id;
  32.     /**
  33.      * @deprecated We now get relationships from Organization->getMember(), and Organization->getOwner()
  34.      */
  35.     #[ORM\OneToOne(targetEntityLegacyAlphaMap::class, mappedBy'user'cascade: ['persist'])]
  36.     private $legacyAlphaMap;
  37.     #[ORM\OneToOne(targetEntityLeadDynoAffiliate::class, mappedBy'user'cascade: ['persist'])]
  38.     private $leadDynoAffiliate;
  39.     /**
  40.      * @var string
  41.      */
  42.     #[ORM\Column(name'u_email'type'binary_aes_encrypted'nullablefalse)]
  43.     private $email;
  44.     /**
  45.      * @var string
  46.      */
  47.     #[ORM\Column(name'u_password_hash'type'string'nullablefalse)]
  48.     private $password;
  49.     /**
  50.      * @var string|null
  51.      */
  52.     #[ORM\Column(name'u_role'type'string'nullabletrue)]
  53.     private $role self::ROLE_USER;
  54.     /**
  55.      * @var string
  56.      */
  57.     #[ORM\Column(name'u_first_name'type'string'nullablefalse)]
  58.     private $firstName;
  59.     /**
  60.      * @var string
  61.      */
  62.     #[ORM\Column(name'u_timezone'type'string'length42nullablefalseoptions: ['default' => 'UTC'])]
  63.     private $timezone 'UTC';
  64.     /**
  65.      * @var UserSettings
  66.      */
  67.     #[ORM\OneToOne(targetEntityUserSettings::class, mappedBy'user'cascade: ['persist''remove'])]
  68.     private $settings;
  69.     /**
  70.      * @var Collection<UserOrganizationAssignment>
  71.      */
  72.     #[ORM\OneToMany(targetEntityUserOrganizationAssignment::class, mappedBy'user'fetch'EAGER'cascade: ['persist'], orphanRemovaltrue)]
  73.     private $organizationAssignments;
  74.     /**
  75.      *
  76.      * @var Collection<Workspace>
  77.      */
  78.     #[ORM\ManyToMany(targetEntityWorkspace::class, inversedBy'users'fetch'EAGER'cascade: ['persist'])]
  79.     #[ORM\JoinTable(name'user_workspace_assignments'joinColumns: [new ORM\JoinColumn(name'uwa_u_id'referencedColumnName'u_id')], inverseJoinColumns: [new ORM\JoinColumn(name'uwa_ws_id'referencedColumnName'ws_id')])]
  80.     private $workspaces;
  81.     /**
  82.      * @var int|null
  83.      */
  84.     #[ORM\Column(name'u_home_design'type'integer'nullabletrueoptions: ['unsigned' => true])]
  85.     private $homeDesign;
  86.     /**
  87.      * @var \DateTimeInterface
  88.      */
  89.     #[ORM\Column(name'u_date_joined'type'datetime'nullablefalse)]
  90.     private $dateJoined;
  91.     /**
  92.      * @var \DateTimeInterface
  93.      */
  94.     #[ORM\Column(name'u_last_login'type'datetime'nullablefalse)]
  95.     private $lastLogin;
  96.     /**
  97.      * @var \DateTimeInterface|null
  98.      */
  99.     #[ORM\Column(name'u_first_visit'type'datetime'nullabletrue)]
  100.     private $firstVisit;
  101.     /**
  102.      * @var int|null
  103.      */
  104.     #[ORM\Column(name'u_mautic_id'type'integer'nullabletrueoptions: ['unsigned' => true])]
  105.     private $mauticId;
  106.     /**
  107.      * @var bool
  108.      */
  109.     #[ORM\Column(name'u_email_bounced'type'boolean'nullablefalseoptions: ['default' => false])]
  110.     private $emailBounced false;
  111.     #[ORM\ManyToOne(targetEntitySiteNews::class)]
  112.     #[ORM\JoinColumn(name'u_last_seen_site_news'referencedColumnName'site_news_id'nullabletrue)]
  113.     private $lastSeenSiteNews;
  114.     /**
  115.      * @var Collection|UserOnboardingStep[]
  116.      */
  117.     #[ORM\OneToMany(targetEntityUserOnboardingStep::class, mappedBy'user'orphanRemovaltruecascade: ['persist'])]
  118.     private $onboardingStepsSeen;
  119.     /**
  120.      * @var bool
  121.      */
  122.     #[ORM\Column(name'u_onboarding_completed'type'boolean'nullablefalseoptions: ['default' => false])]
  123.     private $onboardingCompleted false;
  124.     /**
  125.      * @var \DateTimeInterface|null
  126.      */
  127.     #[ORM\Column(name'u_onboarding_completed_datetime'type'datetime'nullabletrue)]
  128.     private $onboardingCompletedDatetime;
  129.     /**
  130.      * @var int
  131.      */
  132.     #[ORM\Column(name'u_onboarding_version'type'integer'nullablefalseoptions: ['default' => 1])]
  133.     private $onboardingVersion;
  134.     #[ORM\OneToMany(targetEntityAndroidApp::class, mappedBy'user'cascade: ['persist'])]
  135.     private $androidApps;
  136.     #[ORM\OneToMany(targetEntityIosApp::class, mappedBy'user'cascade: ['persist'])]
  137.     private $iosApps;
  138.     /**
  139.      * @var Collection<FacebookToken>
  140.      */
  141.     #[ORM\ManyToMany(targetEntityFacebookToken::class, mappedBy'users')]
  142.     private $facebookTokens;
  143.     /**
  144.      * @var Collection<GoogleToken>
  145.      */
  146.     #[ORM\ManyToMany(targetEntityGoogleToken::class, mappedBy'users')]
  147.     private $googleTokens;
  148.     /**
  149.      * @var Collection<LinkedInToken>
  150.      */
  151.     #[ORM\ManyToMany(targetEntityLinkedInToken::class, mappedBy'users')]
  152.     private $linkedInTokens;
  153.     public function __construct(string $emailstring $firstNameint $onboardingVersion 1)
  154.     {
  155.         $this->email $email;
  156.         // See $this->setPassword().
  157.         $this->password '';
  158.         $this->firstName $firstName;
  159.         $this->dateJoined = new \DateTimeImmutable;
  160.         $this->lastLogin = clone $this->dateJoined;
  161.         $this->timezone 'UTC';
  162.         $this->onboardingVersion $onboardingVersion;
  163.         $this->settings = new UserSettings($this);
  164.         $this->organizationAssignments = new ArrayCollection;
  165.         $this->workspaces = new ArrayCollection;
  166.         $this->onboardingStepsSeen = new ArrayCollection;
  167.         $this->androidApps = new ArrayCollection();
  168.         $this->iosApps = new ArrayCollection();
  169.         $this->facebookTokens = new ArrayCollection();
  170.         $this->googleTokens = new ArrayCollection();
  171.         $this->linkedInTokens = new ArrayCollection();
  172.     }
  173.     public function getId(): ?int
  174.     {
  175.         return $this->id;
  176.     }
  177.     public function getEmail(): string
  178.     {
  179.         return $this->email;
  180.     }
  181.     public function setEmail(string $email): self
  182.     {
  183.         $this->email $email;
  184.         return $this;
  185.     }
  186.     /** @deprecated (delete when upgraded to Symfony 6+) */
  187.     public function getUsername(): string
  188.     {
  189.         return $this->getUserIdentifier();
  190.     }
  191.     public function getUserIdentifier(): string
  192.     {
  193.         // Return the user ID because that's how we identify users in auth (Paseto) tokens.
  194.         return (string) $this->getId();
  195.     }
  196.     public function getPassword(): string
  197.     {
  198.         return $this->password;
  199.     }
  200.     public function hasPasswordSet(): bool
  201.     {
  202.         return !empty($this->password);
  203.     }
  204.     /**
  205.      * Set Raw Password Hash
  206.      * Do *NOT* enter a plaintext password here! Setting the password requires the external password encoder service
  207.      * which encodes according to the per-UserInterface security configuration. Please use the following for both entity
  208.      * construction and this method.
  209.      *
  210.      * [Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface $passwordHashers]
  211.      * $user->setPassword($passwordHasher->hashPassword($user, 'the_new_password'));
  212.      */
  213.     public function setPassword(string $password): self
  214.     {
  215.         $this->password $password;
  216.         return $this;
  217.     }
  218.     /** @deprecated (delete when upgrading to Symfony 6+) */
  219.     public function getSalt(): ?string
  220.     {
  221.         return null;
  222.     }
  223.     public function eraseCredentials(): void
  224.     {
  225.     }
  226.     public function getRole(): string
  227.     {
  228.         return $this->role ?: self::ROLE_USER;
  229.     }
  230.     /**
  231.      * @internal
  232.      *
  233.      * @deprecated
  234.      *
  235.      * Not actually deprecated, but this is specifically for Symfony usage.
  236.      * This is for app roles (admin) and has *nothing* to do with the user-organization assignment roles.
  237.      */
  238.     public function getRoles(): iterable
  239.     {
  240.         return ['ROLE_' strtoupper($this->getRole())];
  241.     }
  242.     public function setRole(string $role): self
  243.     {
  244.         if (!in_array($role, static::VALID_ROLES))
  245.         {
  246.             throw new \Exception(sprintf('Cannot add role "%s"; not a valid role.'$role));
  247.         }
  248.         $this->role $role;
  249.         return $this;
  250.     }
  251.     public function getFirstName(): string
  252.     {
  253.         return $this->firstName;
  254.     }
  255.     public function setFirstName(string $firstName): self
  256.     {
  257.         $this->firstName $firstName;
  258.         return $this;
  259.     }
  260.     public function getTimezone(): \DateTimeZone
  261.     {
  262.         return new \DateTimeZone($this->timezone);
  263.     }
  264.     public function setTimezone(\DateTimeZone $timezone): self
  265.     {
  266.         $this->timezone $timezone->getName();
  267.         return $this;
  268.     }
  269.     public function getSettings(): UserSettings
  270.     {
  271.         return $this->settings;
  272.     }
  273.     public function getOrganizations(): Collection
  274.     {
  275.         return $this->organizationAssignments->map(function (UserOrganizationAssignment $assignment): Organization
  276.         {
  277.             return $assignment->getOrganization();
  278.         });
  279.     }
  280.     public function getOwnedOrganization(): Organization
  281.     {
  282.         $orgs $this->getOrganizations();
  283.         foreach ($orgs as $org)
  284.         {
  285.             if ($this->getRoleWithinOrganization($org) === UserOrganizationAssignment::ROLE_OWNER)
  286.             {
  287.                 return $org;
  288.             }
  289.         }
  290.         throw new \LogicException('No owned organization found for User ID ' $this->getId());
  291.     }
  292.     public function getOrganizationsWithRoles(): \SplObjectStorage
  293.     {
  294.         $map = new \SplObjectStorage;
  295.         /** @var UserOrganizationAssignment $assignment */
  296.         foreach ($this->organizationAssignments as $assignment)
  297.         {
  298.             $map->attach($assignment->getOrganization(), $assignment->getRole());
  299.         }
  300.         return $map;
  301.     }
  302.     public function getRoleWithinOrganization(Organization $organization): ?string
  303.     {
  304.         return $this->getOrganizationsWithRoles()[$organization] ?? null;
  305.     }
  306.     public function getOrganizationAssignments(): Collection
  307.     {
  308.         return $this->organizationAssignments;
  309.     }
  310.     public function getOrganizationAssignment(Organization $organization): ?UserOrganizationAssignment
  311.     {
  312.         $assignments $this->organizationAssignments->filter(function (UserOrganizationAssignment $assignment) use ($organization)
  313.         {
  314.             return $assignment->getOrganization() === $organization;
  315.         });
  316.         return $assignments->count() > $assignments->first() : null;
  317.     }
  318.     public function isInOrganization(Organization $organization): bool
  319.     {
  320.         return $this->getOrganizations()->contains($organization);
  321.     }
  322.     public function addToOrganization(Organization $organizationstring $role): self
  323.     {
  324.         // Make sure we don't accidentally add the user to the same org twice.
  325.         $alreadyAssigned $this->organizationAssignments->filter(function (UserOrganizationAssignment $assignment) use ($organization)
  326.         {
  327.             return $organization === $assignment->getOrganization();
  328.         });
  329.         if ($alreadyAssigned->count() > 0)
  330.         {
  331.             return $this;
  332.         }
  333.         $assignment = new UserOrganizationAssignment($this$organization$role);
  334.         $this->organizationAssignments->add($assignment);
  335.         $organization->getUserAssignments()->add($assignment);
  336.         return $this;
  337.     }
  338.     public function removeFromOrganization(Organization $organization): self
  339.     {
  340.         /** @var UserOrganizationAssignment $assignment */
  341.         foreach ($this->organizationAssignments as $assignment)
  342.         {
  343.             if ($assignment->getOrganization()->getId() === $organization->getId())
  344.             {
  345.                 $this->organizationAssignments->removeElement($assignment);
  346.                 $organization->getUserAssignments()->removeElement($assignment);
  347.             }
  348.         }
  349.         /** @var Workspace $workspace */
  350.         foreach ($this->workspaces as $workspace)
  351.         {
  352.             if ($workspace->getOrganization()->getId() === $organization->getId())
  353.             {
  354.                 $this->workspaces->removeElement($workspace);
  355.                 $workspace->getUsers()->removeElement($workspace);
  356.             }
  357.         }
  358.         return $this;
  359.     }
  360.     public function getWorkspaces(bool $includeDeleted false): Collection
  361.     {
  362.         if ($includeDeleted)
  363.         {
  364.             return $this->workspaces;
  365.         }
  366.         return $this->workspaces->filter(
  367.             fn (Workspace $workspace): bool => !$workspace->isDeleted()
  368.         );
  369.     }
  370.     public function getWorkspacesInOrganization(Organization $organizationbool $includeDeleted false): Collection
  371.     {
  372.         return $this->workspaces->filter(function (Workspace $workspace) use ($organization$includeDeleted): bool
  373.         {
  374.             return $workspace->getOrganization()->getId() === $organization->getId()
  375.                 && ($includeDeleted || !$workspace->isDeleted());
  376.         });
  377.     }
  378.     public function isInWorkspace(Workspace $workspace): bool
  379.     {
  380.         return $this->getWorkspaces()->contains($workspace);
  381.     }
  382.     public function addToWorkspace(Workspace $workspace): self
  383.     {
  384.         if (!$this->getOrganizations()->contains($workspace->getOrganization()))
  385.         {
  386.             throw new \Exception("User does not belong to that workspace's organization.");
  387.         }
  388.         if (!$this->workspaces->contains($workspace))
  389.         {
  390.             $this->workspaces->add($workspace);
  391.             $workspace->getUsers()->add($this);
  392.         }
  393.         return $this;
  394.     }
  395.     public function removeFromWorkspace(Workspace $workspace): self
  396.     {
  397.         if ($this->workspaces->contains($workspace))
  398.         {
  399.             if (count($this->getWorkspacesInOrganization($workspace->getOrganization())) <= 1)
  400.             {
  401.                 throw SqGraphQLException::notEnoughWorkspaces();
  402.             }
  403.             $this->workspaces->removeElement($workspace);
  404.             $workspace->getUsers()->removeElement($this);
  405.         }
  406.         return $this;
  407.     }
  408.     public function getMobileApps(bool $onlyConnected true): Collection
  409.     {
  410.         $allApps = new ArrayCollection(
  411.             array_merge($this->iosApps->toArray(), $this->androidApps->toArray())
  412.         );
  413.         return $allApps->filter(function (MobileAppInterface $app) use ($onlyConnected)
  414.         {
  415.             return !$onlyConnected || $app->isConnected();
  416.         });
  417.     }
  418.     public function getAndroidApps(bool $onlyConnected true): Collection
  419.     {
  420.         $allApps $this->getMobileApps($onlyConnected);
  421.         return $allApps->filter(function (MobileAppInterface $app)
  422.         {
  423.             return $app instanceof AndroidApp;
  424.         });
  425.     }
  426.     public function getIosApps(bool $onlyConnected true): Collection
  427.     {
  428.         $allApps $this->getMobileApps($onlyConnected);
  429.         return $allApps->filter(function (MobileAppInterface $app)
  430.         {
  431.             return $app instanceof IosApp;
  432.         });
  433.     }
  434.     public function addMobileApp(MobileAppInterface $app): self
  435.     {
  436.         if ($app instanceof IosApp)
  437.         {
  438.             if (!$this->iosApps->contains($app))
  439.             {
  440.                 $this->iosApps->add($app);
  441.             }
  442.         }
  443.         elseif ($app instanceof AndroidApp)
  444.         {
  445.             if (!$this->androidApps->contains($app))
  446.             {
  447.                 $this->androidApps->add($app);
  448.             }
  449.         }
  450.         return $this;
  451.     }
  452.     public function removeMobileApp(MobileAppInterface $app): self
  453.     {
  454.         if ($app instanceof IosApp)
  455.         {
  456.             if ($this->iosApps->contains($app))
  457.             {
  458.                 $this->iosApps->removeElement($app);
  459.             }
  460.         }
  461.         elseif ($app instanceof AndroidApp)
  462.         {
  463.             if ($this->androidApps->contains($app))
  464.             {
  465.                 $this->androidApps->removeElement($app);
  466.             }
  467.         }
  468.         return $this;
  469.     }
  470.     public function getHomeDesign(): ?int
  471.     {
  472.         return $this->homeDesign;
  473.     }
  474.     public function setHomeDesign(?int $homeDesign): self
  475.     {
  476.         $this->homeDesign $homeDesign;
  477.         return $this;
  478.     }
  479.     public function getDateJoined(): \DateTimeInterface
  480.     {
  481.         return clone $this->dateJoined;
  482.     }
  483.     public function setDateJoined(\DateTimeInterface $dateJoined): self
  484.     {
  485.         $this->dateJoined = clone $dateJoined;
  486.         return $this;
  487.     }
  488.     public function getLastLogin(): \DateTimeInterface
  489.     {
  490.         return clone $this->lastLogin;
  491.     }
  492.     public function setLastLogin(\DateTimeInterface $lastLogin): self
  493.     {
  494.         $this->lastLogin = clone $lastLogin;
  495.         return $this;
  496.     }
  497.     public function getFirstVisit(): ?\DateTimeInterface
  498.     {
  499.         return $this->firstVisit !== null
  500.             ? clone $this->firstVisit
  501.             null;
  502.     }
  503.     public function setFirstVisit(?\DateTimeInterface $firstVisit): self
  504.     {
  505.         $this->firstVisit $firstVisit !== null ? clone $firstVisit null;
  506.         return $this;
  507.     }
  508.     public function getMauticId(): ?int
  509.     {
  510.         return $this->mauticId;
  511.     }
  512.     public function setMauticId(?int $mauticId): self
  513.     {
  514.         $this->mauticId $mauticId;
  515.         return $this;
  516.     }
  517.     public function hasEmailBounced(): bool
  518.     {
  519.         return $this->emailBounced;
  520.     }
  521.     public function setEmailBounced(bool $emailBounced): self
  522.     {
  523.         $this->emailBounced $emailBounced;
  524.         return $this;
  525.     }
  526.     public function getLastSeenSiteNews(): ?SiteNews
  527.     {
  528.         return $this->lastSeenSiteNews;
  529.     }
  530.     public function setLastSeenSiteNews(?SiteNews $lastSeenSiteNews): self
  531.     {
  532.         $this->lastSeenSiteNews $lastSeenSiteNews;
  533.         return $this;
  534.     }
  535.     /**
  536.      * Get LeadDyno Affiliate object if this member has an affiliate account on LeadDyno.
  537.      */
  538.     public function getLeadDynoAffiliate(): ?LeadDynoAffiliate
  539.     {
  540.         return $this->leadDynoAffiliate;
  541.     }
  542.     /**
  543.      * Set a member as being a LeadDynoAffiliate.
  544.      */
  545.     public function setLeadDynoAffiliate(?LeadDynoAffiliate $affiliate): self
  546.     {
  547.         $this->leadDynoAffiliate $affiliate;
  548.         // set (or unset) the owning side of the relation if necessary
  549.         $newUser null === $affiliate null $this;
  550.         if ($affiliate->getUser() !== $newUser)
  551.         {
  552.             $affiliate->setUser($newUser);
  553.         }
  554.         return $this;
  555.     }
  556.     public function getOnboardingStepsSeen(): Collection
  557.     {
  558.         return $this->onboardingStepsSeen;
  559.     }
  560.     public function markOnboardingStepAsSeen(UserOnboardingStep|string $step): self
  561.     {
  562.         $step is_string($step) ? (new UserOnboardingStep($this$step)) : $step;
  563.         // Don't add it to the collection if we already have it.
  564.         foreach ($this->onboardingStepsSeen as $stepSeen)
  565.         {
  566.             if ($stepSeen->getName() === $step->getName())
  567.             {
  568.                 return $this;
  569.             }
  570.         }
  571.         $this->onboardingStepsSeen->add($step);
  572.         return $this;
  573.     }
  574.     public function isOnboardingCompleted(): bool
  575.     {
  576.         return $this->onboardingCompleted;
  577.     }
  578.     public function getOnboardingCompletedDatetime(): ?\DateTimeInterface
  579.     {
  580.         return $this->onboardingCompletedDatetime;
  581.     }
  582.     public function completeOnboarding(): self
  583.     {
  584.         $this->onboardingCompleted true;
  585.         $this->onboardingCompletedDatetime Carbon::now();
  586.         return $this;
  587.     }
  588.     public function markAllOnboardingTourTipsSeen(): self
  589.     {
  590.         $tourTips = [
  591.             'v1_tourtip_categories_articles',
  592.             'v1_tourtip_categories_demo',
  593.             'v1_tourtip_posting_plan_demo_profiles',
  594.             'v1_tourtip_posting_plan_demo_timeslot',
  595.             'v1_tourtip_queue_manage_post',
  596.             'v1_tourtip_queue_new_post',
  597.             'v1_tourtip_post_editor_select_category',
  598.             'v1_tourtip_post_editor_customize_per_profile',
  599.             'v1_tourtip_post_editor_link',
  600.             'v1_tourtip_post_editor_post_timing',
  601.             'v1_tourtip_post_editor_post_preview',
  602.             'v1_tourtip_profiles_add_your_own',
  603.             'v1_tourtip_profiles_demo',
  604.         ];
  605.         foreach ($tourTips as $tourTip)
  606.         {
  607.             $this->markOnboardingStepAsSeen($tourTip);
  608.         }
  609.         return $this;
  610.     }
  611.     /* Also need to remove all user_onboarding_steps */
  612.     public function resetOnboarding(): self
  613.     {
  614.         $this->onboardingCompleted false;
  615.         $this->onboardingCompletedDatetime null;
  616.         return $this;
  617.     }
  618.     public function getOnboardingVersion(): int
  619.     {
  620.         return $this->onboardingVersion;
  621.     }
  622.     public function wasLegacySignup(): bool
  623.     {
  624.         return $this->getOwnedOrganization()->getLegacyMember()->wasLegacySignup();
  625.     }
  626.     public function canRollbackToLegacy(): bool
  627.     {
  628.         return $this->getOwnedOrganization()->getLegacyMember()->canRollbackToLegacy();
  629.     }
  630.     /**
  631.      * @return FALSE if user was never a migrated legacy member.
  632.      * @return FALSE if user was a migrated legacy member but is currently in the overhaul.
  633.      * @return TRUE if user was a migrated legacy member and was rolled back to legacy.
  634.      */
  635.     public function isRolledBackToLegacy(): bool
  636.     {
  637.         $legacyMember $this->getOwnedOrganization()->getLegacyMember();
  638.         return $legacyMember->wasLegacySignup() && !$legacyMember->isInOverhaul();
  639.     }
  640.     /**
  641.      * Get all FacebookTokens associated with this user.
  642.      *
  643.      * @param bool $onlyActive If true, filters out deleted tokens
  644.      * @return Collection<FacebookToken>
  645.      */
  646.     public function getFacebookTokens(bool $onlyActive true): Collection
  647.     {
  648.         if (!$onlyActive)
  649.         {
  650.             return $this->facebookTokens;
  651.         }
  652.         return $this->facebookTokens->filter(function (FacebookToken $token): bool
  653.         {
  654.             return !$token->isDeleted();
  655.         });
  656.     }
  657.     /**
  658.      * Get all GoogleTokens associated with this user.
  659.      *
  660.      * @param bool $onlyActive If true, filters out deleted tokens
  661.      * @return Collection<GoogleToken>
  662.      */
  663.     public function getGoogleTokens(bool $onlyActive true): Collection
  664.     {
  665.         if (!$onlyActive)
  666.         {
  667.             return $this->googleTokens;
  668.         }
  669.         return $this->googleTokens->filter(function (GoogleToken $token): bool
  670.         {
  671.             return !$token->isDeleted();
  672.         });
  673.     }
  674.     /**
  675.      * Get all LinkedInTokens associated with this user.
  676.      *
  677.      * @param bool $onlyActive If true, filters out deleted tokens
  678.      * @return Collection<LinkedInToken>
  679.      */
  680.     public function getLinkedInTokens(bool $onlyActive true): Collection
  681.     {
  682.         if (!$onlyActive)
  683.         {
  684.             return $this->linkedInTokens;
  685.         }
  686.         return $this->linkedInTokens->filter(function (LinkedInToken $token): bool
  687.         {
  688.             return !$token->isDeleted();
  689.         });
  690.     }
  691.     /**
  692.      * Get all social tokens (Google, Facebook, LinkedIn) for this user.
  693.      *
  694.      * @param bool $onlyActive If true, filters out deleted/error tokens
  695.      * @return array{google: Collection<GoogleToken>, facebook: Collection<FacebookToken>, linkedin: Collection<LinkedInToken>}
  696.      */
  697.     public function getAllSocialTokens(bool $onlyActive true): array
  698.     {
  699.         return [
  700.             'google' => $this->getGoogleTokens($onlyActive),
  701.             'facebook' => $this->getFacebookTokens($onlyActive),
  702.             'linkedin' => $this->getLinkedInTokens($onlyActive),
  703.         ];
  704.     }
  705. }